1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-11 08:33:43 -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 {
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,

View file

@ -1303,6 +1303,16 @@ declare namespace Deno {
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.
*
* Serves HTTP requests with the given handler.
@ -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.
*

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 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;
}
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);
}
for (const streamRid of new SafeSetIterator(responseBodies)) {
core.tryClose(streamRid);
}
})();
return { finished };
}
internals.upgradeHttpRaw = upgradeHttpRaw;

View file

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