I'm trying to close the port manually (in somewhere else) but not sure the way to do it.
A smallest example:
import { Application, Router } from 'jsr:@oak/oak'
const app = new Application()
const router = new Router()
app.use(router.routes())
app.use(router.allowedMethods())
router.get('/', (ctx) => {
ctx.response.body = JSON.stringify({ message: 'Hello, Oak!' })
})
const ac = new AbortController()
const promise = app.listen({ port: 8000, signal: ac.signal })
async function close() {
ac.abort()
await promise
// never reached
console.log('Server closed')
}
await close()
The readme file in the Oak repository includes a code example (closing the server) which shows the equivalent of the code in your question, but — at the time I write this answer — there appears to be a bug in Oak (v17.1.6
) that prevents the promise returned by the Application.prototype.listen
method from fulfilling. You can see more info and subscribe for updates at the following GitHub PR:
oakserver/oak#685
— fix: make Application listen promise resolve after aborted
In the meantime, you can create your own promise… and then resolve it in a callback function that is provided to the Application.prototype.addEventListener
method like this:
example.ts
:
import { Application } from "jsr:@oak/oak@17.1.6";
import type {
ApplicationCloseEvent,
ApplicationListenEvent,
} from "jsr:@oak/oak@17.1.6/application";
const app = new Application();
app.use((ctx) => ctx.response.body = "hello world");
const serverListening = Promise.withResolvers<ApplicationListenEvent>();
app.addEventListener("listen", serverListening.resolve, { once: true });
const serverClosed = Promise.withResolvers<ApplicationCloseEvent>();
app.addEventListener("close", serverClosed.resolve, { once: true });
const controller = new AbortController();
app.listen({ hostname: "localhost", port: 8000, signal: controller.signal });
const listenEvent = await serverListening.promise;
console.log(`Server listening on port ${listenEvent.port}`);
controller.abort();
await serverClosed.promise;
console.log("Server closed");
Running with Deno (v2.4.5
):
> deno run --no-config --allow-net=localhost:8000 example.ts
Server listening on port 8000
Server closed
Here's a reproducible test module based on the code in your question:
test.ts
:
import { assert } from "jsr:@std/assert@1.0.14/assert";
import { assertRejects } from "jsr:@std/assert@1.0.14/rejects";
import {
Application,
type ListenOptionsBase,
Router,
} from "jsr:@oak/oak@17.1.6";
import type {
ApplicationCloseEvent,
ApplicationListenEvent,
} from "jsr:@oak/oak@17.1.6/application";
Deno.test("oak server", async (t) => {
const app = new Application();
const router = new Router();
app.use(router.routes());
app.use(router.allowedMethods());
const message = "Hello, Oak!";
router.get("/", (ctx) => {
ctx.response.body = JSON.stringify({ message });
});
const serverListening = Promise.withResolvers<ApplicationListenEvent>();
app.addEventListener("listen", serverListening.resolve, { once: true });
const serverClosed = Promise.withResolvers<ApplicationCloseEvent>();
app.addEventListener("close", serverClosed.resolve, { once: true });
// let serverStopped: Promise<void>;
const ac = new AbortController();
const listenOptions: ListenOptionsBase = {
hostname: "localhost",
port: 8000,
signal: ac.signal,
};
await t.step("starts", async () => {
// Ref: https://github.com/oakserver/oak/pull/685
/* serverStopped = */ app.listen(listenOptions);
await serverListening.promise;
});
const assertExpectedResponse: () => Promise<void> = async () => {
const url = `http://${listenOptions.hostname}:${listenOptions.port}`;
const response = await fetch(url);
assert(response.ok);
const actualMessage = (await response.json()).message;
assert(actualMessage === message);
};
await t.step("responds", assertExpectedResponse);
await t.step("stops", async () => {
ac.abort();
// await serverStopped;
await serverClosed.promise;
});
await t.step("no longer responds", async () => {
await assertRejects(assertExpectedResponse, TypeError);
});
});
Running with Deno's test runner:
> deno --version
deno 2.4.5 (stable, release, aarch64-apple-darwin)
v8 13.7.152.14-rusty
typescript 5.8.3
> deno test --no-config --allow-net=localhost:8000 test.ts
running 1 test from ./test.ts
oak server ...
starts ... ok (2ms)
responds ... ok (3ms)
stops ... ok (0ms)
no longer responds ... ok (1ms)
oak server ... ok (7ms)
ok | 1 passed (4 steps) | 0 failed (8ms)