Skip to content

Event iterator loops indefinitely when the same id is sent #1473

@Zonigaru

Description

@Zonigaru

Environment

@orpc/server: 1.13.6
bun: 1.3.10

Reproduction

The bug is present in Websocket, I haven't tested the other adapters.

Simple reproduction with this code (part of this documentation page: https://orpc.dev/docs/adapters/websocket):

import { RPCHandler } from "@orpc/server/bun-ws";
import { onError, os } from "@orpc/server";

// = Router

const eventIterator = os.handler(async function* ({ signal }) {
  while (signal && !signal.aborted) {
    yield { message: "Hello World!" };
    console.log(`Message sent`);
    await Bun.sleep(5000);
  }
});

const router = {
  subscribe: eventIterator,
};

// ==

const handler = new RPCHandler(router, {
  interceptors: [
    onError((error) => {
      console.error(error);
    }),
  ],
});

Bun.serve({
  port: 3000,
  fetch(req, server) {
    if (server.upgrade(req)) {
      return;
    }

    return new Response("Upgrade failed", { status: 500 });
  },
  websocket: {
    message(ws, message) {
      handler.message(ws, message, {
        context: {},
      });
    },
    close(ws) {
      handler.close(ws);
    },
  },
});

Then I use an API client such as Bruno or Postman, repeatedly sending this message:

{
  "i": 1,
  "p": {
    "u": "/subscribe",
    "b": {
      "json": {}
    }
  }
}

If you close the websocket client connection, you will see on the server side that the “Message sent” logs are still being iterated. I noticed that only the last message sent is aborted correctly. Would the handler replace the previous signal assigned to the ID without aborting it?

I understand that this issue only occurs if oRPC is misused without using a RPCLink. However, in production, attackers could exploit this vulnerability to unnecessarily overload the server.

Describe the bug

When an event iterator is present in the router and I use the correct message format with the same id, the previous signals are never aborted. This issue causes an endless loop, since the previous iterators are never aborted.

Additional context

No response

Logs

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions