diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2023-05-19 02:59:23 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-19 02:59:23 +0200 |
commit | 5b0752234993ee69e47c32db478d2a296f73f396 (patch) | |
tree | 6501e364a82d6ff98845d87fe38744bda7fd1a70 | |
parent | 8724ba9d084127147e2bb2c997a6bf2f38c9b3d2 (diff) |
BREAKING(unstable): change return type of Deno.serve() API (#19189)
This commit changes the return type of an unstable `Deno.serve()` API
to instead return a `Deno.Server` object that has a `finished` field.
This change is done in preparation to be able to ref/unref the HTTP
server.
-rw-r--r-- | cli/tests/unit/serve_test.ts | 15 | ||||
-rw-r--r-- | cli/tsc/dts/lib.deno.unstable.d.ts | 37 | ||||
-rw-r--r-- | ext/http/00_serve.js | 51 | ||||
-rw-r--r-- | ext/node/polyfills/http.ts | 2 |
4 files changed, 64 insertions, 41 deletions
diff --git a/cli/tests/unit/serve_test.ts b/cli/tests/unit/serve_test.ts index 5da300dc9..c6cfc45f3 100644 --- a/cli/tests/unit/serve_test.ts +++ b/cli/tests/unit/serve_test.ts @@ -6,7 +6,6 @@ import { TextProtoReader } from "../testdata/run/textproto.ts"; import { assert, assertEquals, - assertRejects, assertStringIncludes, assertThrows, Deferred, @@ -50,7 +49,7 @@ Deno.test(async function httpServerShutsDownPortBeforeResolving() { assertThrows(() => Deno.listen({ port: 4501 })); ac.abort(); - await server; + await server.finished; const listener = Deno.listen({ port: 4501 }); listener!.close(); @@ -93,7 +92,7 @@ Deno.test(async function httpServerRejectsOnAddrInUse() { }); await listeningPromise; - await assertRejects( + assertThrows( () => Deno.serve({ handler: (_req) => new Response("ok"), @@ -284,18 +283,18 @@ Deno.test({ permissions: { net: true } }, async function httpServerOverload2() { Deno.test( { permissions: { net: true } }, - async function httpServerErrorOverloadMissingHandler() { + function httpServerErrorOverloadMissingHandler() { // @ts-ignore - testing invalid overload - await assertRejects(() => Deno.serve(), TypeError, "handler"); + assertThrows(() => Deno.serve(), TypeError, "handler"); // @ts-ignore - testing invalid overload - await assertRejects(() => Deno.serve({}), TypeError, "handler"); - await assertRejects( + assertThrows(() => Deno.serve({}), TypeError, "handler"); + assertThrows( // @ts-ignore - testing invalid overload () => Deno.serve({ handler: undefined }), TypeError, "handler", ); - await assertRejects( + assertThrows( // @ts-ignore - testing invalid overload () => Deno.serve(undefined, { handler: () => {} }), TypeError, diff --git a/cli/tsc/dts/lib.deno.unstable.d.ts b/cli/tsc/dts/lib.deno.unstable.d.ts index 3a4344bd8..c0c0d16ad 100644 --- a/cli/tsc/dts/lib.deno.unstable.d.ts +++ b/cli/tsc/dts/lib.deno.unstable.d.ts @@ -1305,6 +1305,16 @@ declare namespace Deno { /** **UNSTABLE**: New API, yet to be vetted. * + * @category HTTP Server + */ + export interface Server { + /** A promise that resolves once server finishes - eg. when aborted using + * the signal passed to {@linkcode ServeOptions.signal}. + */ + finished: Promise<void>; + } + /** **UNSTABLE**: New API, yet to be vetted. + * * Serves HTTP requests with the given handler. * * You can specify an object with a port and hostname option, which is the @@ -1331,8 +1341,11 @@ declare namespace Deno { * ```ts * const ac = new AbortController(); * - * Deno.serve({ signal: ac.signal }, (_req) => new Response("Hello, world")) - * .then(() => console.log("Server closed")); + * const server = Deno.serve( + * { signal: ac.signal }, + * (_req) => new Response("Hello, world") + * ); + * server.finished.then(() => console.log("Server closed")); * * console.log("Closing server..."); * ac.abort(); @@ -1362,7 +1375,7 @@ declare namespace Deno { * * @category HTTP Server */ - export function serve(handler: ServeHandler): Promise<void>; + export function serve(handler: ServeHandler): Server; /** **UNSTABLE**: New API, yet to be vetted. * * Serves HTTP requests with the given handler. @@ -1391,8 +1404,11 @@ declare namespace Deno { * ```ts * const ac = new AbortController(); * - * Deno.serve({ signal: ac.signal }, (_req) => new Response("Hello, world")) - * .then(() => console.log("Server closed")); + * const server = Deno.serve( + * { signal: ac.signal }, + * (_req) => new Response("Hello, world") + * ); + * server.finished.then(() => console.log("Server closed")); * * console.log("Closing server..."); * ac.abort(); @@ -1425,7 +1441,7 @@ declare namespace Deno { export function serve( options: ServeOptions | ServeTlsOptions, handler: ServeHandler, - ): Promise<void>; + ): Server; /** **UNSTABLE**: New API, yet to be vetted. * * Serves HTTP requests with the given handler. @@ -1454,8 +1470,11 @@ declare namespace Deno { * ```ts * const ac = new AbortController(); * - * Deno.serve({ signal: ac.signal }, (_req) => new Response("Hello, world")) - * .then(() => console.log("Server closed")); + * const server = Deno.serve( + * { signal: ac.signal }, + * (_req) => new Response("Hello, world") + * ); + * server.finished.then(() => console.log("Server closed")); * * console.log("Closing server..."); * ac.abort(); @@ -1487,7 +1506,7 @@ declare namespace Deno { */ export function serve( options: ServeInit & (ServeOptions | ServeTlsOptions), - ): Promise<void>; + ): Server; /** **UNSTABLE**: New API, yet to be vetted. * diff --git a/ext/http/00_serve.js b/ext/http/00_serve.js index 35af49b04..9075ae651 100644 --- a/ext/http/00_serve.js +++ b/ext/http/00_serve.js @@ -563,7 +563,7 @@ function mapToCallback(responseBodies, context, signal, callback, onError) { }; } -async function serve(arg1, arg2) { +function serve(arg1, arg2) { let options = undefined; let handler = undefined; if (typeof arg1 === "function") { @@ -653,33 +653,38 @@ async function serve(arg1, arg2) { onListen({ port: listenOpts.port }); - while (true) { - const rid = context.serverRid; - let req; - try { - req = await op_http_wait(rid); - } catch (error) { - if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { + // Run the server + const finished = (async () => { + while (true) { + const rid = context.serverRid; + let req; + try { + req = await op_http_wait(rid); + } catch (error) { + if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { + break; + } + throw new Deno.errors.Http(error); + } + if (req === 0xffffffff) { break; } - throw new Deno.errors.Http(error); + PromisePrototypeCatch(callback(req), (error) => { + // Abnormal exit + console.error( + "Terminating Deno.serve loop due to unexpected error", + error, + ); + context.close(); + }); } - if (req === 0xffffffff) { - break; + + for (const streamRid of new SafeSetIterator(responseBodies)) { + core.tryClose(streamRid); } - PromisePrototypeCatch(callback(req), (error) => { - // Abnormal exit - console.error( - "Terminating Deno.serve loop due to unexpected error", - error, - ); - context.close(); - }); - } + })(); - for (const streamRid of new SafeSetIterator(responseBodies)) { - core.tryClose(streamRid); - } + return { finished }; } internals.upgradeHttpRaw = upgradeHttpRaw; diff --git a/ext/node/polyfills/http.ts b/ext/node/polyfills/http.ts index adc5845b5..065ad2e0f 100644 --- a/ext/node/polyfills/http.ts +++ b/ext/node/polyfills/http.ts @@ -1577,7 +1577,7 @@ class ServerImpl extends EventEmitter { this.emit("listening"); }, }, - ).then(() => this.#servePromise!.resolve()); + ).finished.then(() => this.#servePromise!.resolve()); } setTimeout() { |