denoland / deno

A modern runtime for JavaScript and TypeScript.
https://deno.com
MIT License
94.49k stars 5.24k forks source link

Force kill Deno.serve http server #25792

Open alexgleason opened 1 week ago

alexgleason commented 1 week ago

I am testing automatic websocket reconnection, and I've found the best way to do it is to start a real HTTP server inside the test and then close it.

Unfortunately there is only a .shutdown() method, which waits 30s for active websocket connections to close on their own before ending the server. I tried passing a signal into it too (and aborting it), but it has the same behavior as .shutdown().

To work around this issue, I created a set of connections:

private connections = new Set<WebSocket>();

which I then manage when the socket opens and closes:

      const { response, socket } = Deno.upgradeWebSocket(req);

      socket.onopen = () => {
        this.connections.add(socket);
      };

      socket.onclose = () => {
        this.connections.delete(socket);
      };

finally, to close the server, I loop over the connections and call .close() on them, then I call .shutdown():

  async close(): Promise<void> {
    this.connections.forEach((conn) => conn.close());
    this.connections = new Set<WebSocket>();

    await this.server.shutdown();
  }

This workaround is working good for me, but ideally there would be a server.kill() or server.shutdown(force = true) method that would do basically the same thing sigint does to the server.

bartlomieju commented 1 week ago

Can you provide a full reproduction please? Passing signal option to Deno.serve() API and later aborting it should abruptly close all connections and immediately close the server.

alexgleason commented 1 week ago

@bartlomieju Sure, here's a reproduction:

server.ts:

const controller = new AbortController();

setTimeout(() => controller.abort(), 10_000);

let i = 1;
setInterval(() => console.log(i++), 1_000);

Deno.serve({ signal: controller.signal }, (req) => {
  const { response } = Deno.upgradeWebSocket(req);
  return response;
});

Start this with deno run -A server.ts, then quickly run wscat -c http://localhost:8000 in another terminal.

This actually never dies, so I think maybe it was only limited to 30s in the test. :thinking:

image

It's Deno 1.46.3

alexgleason commented 1 week ago

After I ctrl+C wscat I can't reconnect again, so at least that's working.

https://github.com/user-attachments/assets/b818d95e-2774-4997-8701-cd15b107911c

bartlomieju commented 1 week ago

CC @littledivy could you take a look at this one?