Skip to content

Commit

Permalink
fix: abort signal on router listen
Browse files Browse the repository at this point in the history
  • Loading branch information
kitsonk committed Jul 11, 2023
1 parent c9ed546 commit 3e8a21d
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 40 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"deno.enable": true
"deno.enable": true,
"deno.lint": true
}
2 changes: 1 addition & 1 deletion deno.jsonc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"tasks": {
"example": "deno run --allow-net --allow-hrtime _examples/server.ts",
"test": "deno test"
"test": "deno test --allow-net"
}
}
35 changes: 6 additions & 29 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion deps_test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export {
assert,
assertEquals,
} from "https://deno.land/[email protected]/testing/asserts.ts";
} from "https://deno.land/[email protected]/testing/asserts.ts";
export { deferred } from "https://deno.land/[email protected]/async/deferred.ts";
19 changes: 12 additions & 7 deletions router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ type ListenOptions = ListenOptionsBase | ListenOptionsSecure;
interface InternalState {
closed: boolean;
closing: boolean;
handling: Set<Promise<void>>;
handling: Set<Promise<Response>>;
server: ServerConstructor;
}

Expand Down Expand Up @@ -793,7 +793,7 @@ export class Router extends EventTarget {
#preferJson: boolean;
#routes = new Set<Route>();
#secure = false;
#state!: InternalState;
#state?: InternalState;
#statusRoutes = new Set<StatusRoute<Status>>();
#uid = 0;

Expand Down Expand Up @@ -878,6 +878,7 @@ export class Router extends EventTarget {
const uid = this.#uid++;
performance.mark(`${HANDLE_START} ${uid}`);
const deferred = new Deferred<Response>();
this.#state?.handling.add(deferred.promise);
requestEvent.respondWith(deferred.promise).catch((error) =>
this.#error(requestEvent.request, error, false)
);
Expand Down Expand Up @@ -940,6 +941,7 @@ export class Router extends EventTarget {
response = this.#error(request, error, true);
}
deferred.resolve(response);
this.#state?.handling.delete(deferred.promise);
const measure = performance.measure(
`handle ${uid}`,
`${HANDLE_START} ${uid}`,
Expand All @@ -961,6 +963,7 @@ export class Router extends EventTarget {
response,
);
deferred.resolve(result ?? response);
this.#state?.handling.delete(deferred.promise);
const measure = performance.measure(
`handle ${uid}`,
`${HANDLE_START} ${uid}`,
Expand All @@ -978,6 +981,7 @@ export class Router extends EventTarget {
response = this.#notFound(request);
}
deferred.resolve(response);
this.#state?.handling.delete(deferred.promise);
const measure = performance.measure(
`handle ${uid}`,
`${HANDLE_START} ${uid}`,
Expand Down Expand Up @@ -1426,16 +1430,17 @@ export class Router extends EventTarget {
this.#state = {
closed: false,
closing: false,
handling: new Set<Promise<void>>(),
handling: new Set(),
server: Server,
};
this.#secure = secure;
if (signal) {
signal.addEventListener("abort", async () => {
if (!this.#state.handling.size) {
await server.close();
this.#state.closed = true;
}
assert(this.#state, "router state should exist");
this.#state.closing = true;
await Promise.all(this.#state.handling);
await server.close();
this.#state.closed = true;
});
}
const listener = await server.listen();
Expand Down
33 changes: 32 additions & 1 deletion router_test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Status } from "./deps.ts";
import { assert, assertEquals } from "./deps_test.ts";
import { assert, assertEquals, deferred } from "./deps_test.ts";
import { Router, RouterRequestEvent } from "./router.ts";

Deno.test({
Expand Down Expand Up @@ -75,3 +75,34 @@ Deno.test({
);
},
});

Deno.test({
name: "Router - abort signal - closes properly",
async fn() {
const router = new Router();
const abortController = new AbortController();
const { signal } = abortController;
const rp: Promise<string>[] = [];
router.get("/", () => {
const d = deferred<Response>();
setTimeout(
() =>
d.resolve(
new Response(JSON.stringify({ hello: "world" }), {
headers: { "content-type": "application/json" },
}),
),
200,
);
return d;
});
router.addEventListener("listen", ({ port, hostname }) => {
rp.push(fetch(`http://${hostname}:${port}/`).then((r) => r.text()));
rp.push(fetch(`http://${hostname}:${port}/`).then((r) => r.text()));
setTimeout(() => abortController.abort(), 100);
});
await router.listen({ signal });
// deno-lint-ignore no-explicit-any
return Promise.all(rp) as Promise<any>;
},
});

0 comments on commit 3e8a21d

Please sign in to comment.