1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-11 16:42:21 -05:00

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.
This commit is contained in:
Bartek Iwańczuk 2023-05-19 02:59:23 +02:00 committed by GitHub
parent 8724ba9d08
commit 5b07522349
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 65 additions and 42 deletions

View file

@ -6,7 +6,6 @@ import { TextProtoReader } from "../testdata/run/textproto.ts";
import { import {
assert, assert,
assertEquals, assertEquals,
assertRejects,
assertStringIncludes, assertStringIncludes,
assertThrows, assertThrows,
Deferred, Deferred,
@ -50,7 +49,7 @@ Deno.test(async function httpServerShutsDownPortBeforeResolving() {
assertThrows(() => Deno.listen({ port: 4501 })); assertThrows(() => Deno.listen({ port: 4501 }));
ac.abort(); ac.abort();
await server; await server.finished;
const listener = Deno.listen({ port: 4501 }); const listener = Deno.listen({ port: 4501 });
listener!.close(); listener!.close();
@ -93,7 +92,7 @@ Deno.test(async function httpServerRejectsOnAddrInUse() {
}); });
await listeningPromise; await listeningPromise;
await assertRejects( assertThrows(
() => () =>
Deno.serve({ Deno.serve({
handler: (_req) => new Response("ok"), handler: (_req) => new Response("ok"),
@ -284,18 +283,18 @@ Deno.test({ permissions: { net: true } }, async function httpServerOverload2() {
Deno.test( Deno.test(
{ permissions: { net: true } }, { permissions: { net: true } },
async function httpServerErrorOverloadMissingHandler() { function httpServerErrorOverloadMissingHandler() {
// @ts-ignore - testing invalid overload // @ts-ignore - testing invalid overload
await assertRejects(() => Deno.serve(), TypeError, "handler"); assertThrows(() => Deno.serve(), TypeError, "handler");
// @ts-ignore - testing invalid overload // @ts-ignore - testing invalid overload
await assertRejects(() => Deno.serve({}), TypeError, "handler"); assertThrows(() => Deno.serve({}), TypeError, "handler");
await assertRejects( assertThrows(
// @ts-ignore - testing invalid overload // @ts-ignore - testing invalid overload
() => Deno.serve({ handler: undefined }), () => Deno.serve({ handler: undefined }),
TypeError, TypeError,
"handler", "handler",
); );
await assertRejects( assertThrows(
// @ts-ignore - testing invalid overload // @ts-ignore - testing invalid overload
() => Deno.serve(undefined, { handler: () => {} }), () => Deno.serve(undefined, { handler: () => {} }),
TypeError, TypeError,

View file

@ -1303,6 +1303,16 @@ declare namespace Deno {
handler: ServeHandler; handler: ServeHandler;
} }
/** **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. /** **UNSTABLE**: New API, yet to be vetted.
* *
* Serves HTTP requests with the given handler. * Serves HTTP requests with the given handler.
@ -1331,8 +1341,11 @@ declare namespace Deno {
* ```ts * ```ts
* const ac = new AbortController(); * const ac = new AbortController();
* *
* Deno.serve({ signal: ac.signal }, (_req) => new Response("Hello, world")) * const server = Deno.serve(
* .then(() => console.log("Server closed")); * { signal: ac.signal },
* (_req) => new Response("Hello, world")
* );
* server.finished.then(() => console.log("Server closed"));
* *
* console.log("Closing server..."); * console.log("Closing server...");
* ac.abort(); * ac.abort();
@ -1362,7 +1375,7 @@ declare namespace Deno {
* *
* @category HTTP Server * @category HTTP Server
*/ */
export function serve(handler: ServeHandler): Promise<void>; export function serve(handler: ServeHandler): Server;
/** **UNSTABLE**: New API, yet to be vetted. /** **UNSTABLE**: New API, yet to be vetted.
* *
* Serves HTTP requests with the given handler. * Serves HTTP requests with the given handler.
@ -1391,8 +1404,11 @@ declare namespace Deno {
* ```ts * ```ts
* const ac = new AbortController(); * const ac = new AbortController();
* *
* Deno.serve({ signal: ac.signal }, (_req) => new Response("Hello, world")) * const server = Deno.serve(
* .then(() => console.log("Server closed")); * { signal: ac.signal },
* (_req) => new Response("Hello, world")
* );
* server.finished.then(() => console.log("Server closed"));
* *
* console.log("Closing server..."); * console.log("Closing server...");
* ac.abort(); * ac.abort();
@ -1425,7 +1441,7 @@ declare namespace Deno {
export function serve( export function serve(
options: ServeOptions | ServeTlsOptions, options: ServeOptions | ServeTlsOptions,
handler: ServeHandler, handler: ServeHandler,
): Promise<void>; ): Server;
/** **UNSTABLE**: New API, yet to be vetted. /** **UNSTABLE**: New API, yet to be vetted.
* *
* Serves HTTP requests with the given handler. * Serves HTTP requests with the given handler.
@ -1454,8 +1470,11 @@ declare namespace Deno {
* ```ts * ```ts
* const ac = new AbortController(); * const ac = new AbortController();
* *
* Deno.serve({ signal: ac.signal }, (_req) => new Response("Hello, world")) * const server = Deno.serve(
* .then(() => console.log("Server closed")); * { signal: ac.signal },
* (_req) => new Response("Hello, world")
* );
* server.finished.then(() => console.log("Server closed"));
* *
* console.log("Closing server..."); * console.log("Closing server...");
* ac.abort(); * ac.abort();
@ -1487,7 +1506,7 @@ declare namespace Deno {
*/ */
export function serve( export function serve(
options: ServeInit & (ServeOptions | ServeTlsOptions), options: ServeInit & (ServeOptions | ServeTlsOptions),
): Promise<void>; ): Server;
/** **UNSTABLE**: New API, yet to be vetted. /** **UNSTABLE**: New API, yet to be vetted.
* *

View file

@ -563,7 +563,7 @@ function mapToCallback(responseBodies, context, signal, callback, onError) {
}; };
} }
async function serve(arg1, arg2) { function serve(arg1, arg2) {
let options = undefined; let options = undefined;
let handler = undefined; let handler = undefined;
if (typeof arg1 === "function") { if (typeof arg1 === "function") {
@ -653,33 +653,38 @@ async function serve(arg1, arg2) {
onListen({ port: listenOpts.port }); onListen({ port: listenOpts.port });
while (true) { // Run the server
const rid = context.serverRid; const finished = (async () => {
let req; while (true) {
try { const rid = context.serverRid;
req = await op_http_wait(rid); let req;
} catch (error) { try {
if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) { req = await op_http_wait(rid);
} catch (error) {
if (ObjectPrototypeIsPrototypeOf(BadResourcePrototype, error)) {
break;
}
throw new Deno.errors.Http(error);
}
if (req === 0xffffffff) {
break; 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;
}
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)) { for (const streamRid of new SafeSetIterator(responseBodies)) {
core.tryClose(streamRid); core.tryClose(streamRid);
} }
})();
return { finished };
} }
internals.upgradeHttpRaw = upgradeHttpRaw; internals.upgradeHttpRaw = upgradeHttpRaw;

View file

@ -1577,7 +1577,7 @@ class ServerImpl extends EventEmitter {
this.emit("listening"); this.emit("listening");
}, },
}, },
).then(() => this.#servePromise!.resolve()); ).finished.then(() => this.#servePromise!.resolve());
} }
setTimeout() { setTimeout() {