summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/tsc/dts/lib.deno.ns.d.ts122
-rw-r--r--ext/http/00_serve.js28
-rw-r--r--tests/unit/serve_test.ts19
3 files changed, 137 insertions, 32 deletions
diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts
index c63b4a261..fc64876f4 100644
--- a/cli/tsc/dts/lib.deno.ns.d.ts
+++ b/cli/tsc/dts/lib.deno.ns.d.ts
@@ -6217,7 +6217,7 @@ declare namespace Deno {
*/
export function gid(): number | null;
- /** Information for a HTTP request.
+ /** Additional information for an HTTP request and its connection.
*
* @category HTTP Server
*/
@@ -6269,7 +6269,7 @@ declare namespace Deno {
onError?: (error: unknown) => Response | Promise<Response>;
/** The callback which is called when the server starts listening. */
- onListen?: (params: { hostname: string; port: number }) => void;
+ onListen?: (localAddr: Deno.NetAddr) => void;
}
/** Additional options which are used when opening a TLS (HTTPS) server.
@@ -6304,6 +6304,14 @@ declare namespace Deno {
handler: ServeHandler;
}
+ /**
+ * @category HTTP Server
+ */
+ export interface ServeTlsInit {
+ /** The handler to invoke to process each incoming request. */
+ handler: ServeHandler;
+ }
+
/** @category HTTP Server */
export interface ServeUnixOptions {
/** The unix domain socket path to listen on. */
@@ -6316,7 +6324,7 @@ declare namespace Deno {
onError?: (error: unknown) => Response | Promise<Response>;
/** The callback which is called when the server starts listening. */
- onListen?: (params: { path: string }) => void;
+ onListen?: (localAddr: Deno.UnixAddr) => void;
}
/** Information for a unix domain socket HTTP request.
@@ -6353,12 +6361,16 @@ declare namespace Deno {
*
* @category HTTP Server
*/
- export interface HttpServer extends AsyncDisposable {
+ export interface HttpServer<A extends Deno.Addr = Deno.Addr>
+ extends AsyncDisposable {
/** A promise that resolves once server finishes - eg. when aborted using
* the signal passed to {@linkcode ServeOptions.signal}.
*/
finished: Promise<void>;
+ /** The local address this server is listening on. */
+ addr: A;
+
/**
* Make the server block the event loop from finishing.
*
@@ -6395,7 +6407,7 @@ declare namespace Deno {
*
* @category HTTP Server
*/
- export function serve(handler: ServeHandler): HttpServer;
+ export function serve(handler: ServeHandler): HttpServer<Deno.NetAddr>;
/** Serves HTTP requests with the given option bag and handler.
*
* You can specify the socket path with `path` option.
@@ -6444,7 +6456,67 @@ declare namespace Deno {
export function serve(
options: ServeUnixOptions,
handler: ServeUnixHandler,
- ): HttpServer;
+ ): HttpServer<Deno.UnixAddr>;
+ /** Serves HTTP requests with the given option bag and handler.
+ *
+ * You can specify an object with a port and hostname option, which is the
+ * address to listen on. The default is port `8000` on hostname `"127.0.0.1"`.
+ *
+ * You can change the address to listen on using the `hostname` and `port`
+ * options. The below example serves on port `3000` and hostname `"0.0.0.0"`.
+ *
+ * ```ts
+ * Deno.serve(
+ * { port: 3000, hostname: "0.0.0.0" },
+ * (_req) => new Response("Hello, world")
+ * );
+ * ```
+ *
+ * You can stop the server with an {@linkcode AbortSignal}. The abort signal
+ * needs to be passed as the `signal` option in the options bag. The server
+ * aborts when the abort signal is aborted. To wait for the server to close,
+ * await the promise returned from the `Deno.serve` API.
+ *
+ * ```ts
+ * const ac = new AbortController();
+ *
+ * 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();
+ * ```
+ *
+ * By default `Deno.serve` prints the message
+ * `Listening on http://<hostname>:<port>/` on listening. If you like to
+ * change this behavior, you can specify a custom `onListen` callback.
+ *
+ * ```ts
+ * Deno.serve({
+ * onListen({ port, hostname }) {
+ * console.log(`Server started at http://${hostname}:${port}`);
+ * // ... more info specific to your server ..
+ * },
+ * }, (_req) => new Response("Hello, world"));
+ * ```
+ *
+ * To enable TLS you must specify the `key` and `cert` options.
+ *
+ * ```ts
+ * const cert = "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n";
+ * const key = "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n";
+ * Deno.serve({ cert, key }, (_req) => new Response("Hello, world"));
+ * ```
+ *
+ * @category HTTP Server
+ */
+ export function serve(
+ options: ServeOptions,
+ handler: ServeHandler,
+ ): HttpServer<Deno.NetAddr>;
/** Serves HTTP requests with the given option bag and handler.
*
* You can specify an object with a port and hostname option, which is the
@@ -6503,11 +6575,10 @@ declare namespace Deno {
*/
export function serve(
options:
- | ServeOptions
| ServeTlsOptions
| (ServeTlsOptions & TlsCertifiedKeyOptions),
handler: ServeHandler,
- ): HttpServer;
+ ): HttpServer<Deno.NetAddr>;
/** Serves HTTP requests with the given option bag.
*
* You can specify an object with the path option, which is the
@@ -6534,7 +6605,7 @@ declare namespace Deno {
*/
export function serve(
options: ServeUnixInit & ServeUnixOptions,
- ): HttpServer;
+ ): HttpServer<Deno.UnixAddr>;
/** Serves HTTP requests with the given option bag.
*
* You can specify an object with a port and hostname option, which is the
@@ -6563,10 +6634,39 @@ declare namespace Deno {
export function serve(
options:
& ServeInit
+ & ServeOptions,
+ ): HttpServer<Deno.NetAddr>;
+ /** Serves HTTP requests with the given option bag.
+ *
+ * You can specify an object with a port and hostname option, which is the
+ * address to listen on. The default is port `8000` on hostname `"127.0.0.1"`.
+ *
+ * ```ts
+ * const ac = new AbortController();
+ *
+ * const server = Deno.serve({
+ * port: 3000,
+ * hostname: "0.0.0.0",
+ * handler: (_req) => new Response("Hello, world"),
+ * signal: ac.signal,
+ * onListen({ port, hostname }) {
+ * console.log(`Server started at http://${hostname}:${port}`);
+ * },
+ * });
+ * server.finished.then(() => console.log("Server closed"));
+ *
+ * console.log("Closing server...");
+ * ac.abort();
+ * ```
+ *
+ * @category HTTP Server
+ */
+ export function serve(
+ options:
+ & ServeTlsInit
& (
- | ServeOptions
| ServeTlsOptions
| (ServeTlsOptions & TlsCertifiedKeyOptions)
),
- ): HttpServer;
+ ): HttpServer<Deno.NetAddr>;
}
diff --git a/ext/http/00_serve.js b/ext/http/00_serve.js
index 660287edb..52b833f10 100644
--- a/ext/http/00_serve.js
+++ b/ext/http/00_serve.js
@@ -552,7 +552,7 @@ function serve(arg1, arg2) {
const path = listener.addr.path;
return serveHttpOnListener(listener, signal, handler, onError, () => {
if (options.onListen) {
- options.onListen({ path });
+ options.onListen(listener.addr);
} else {
console.log(`Listening on ${path}`);
}
@@ -593,19 +593,18 @@ function serve(arg1, arg2) {
listenOpts.port = listener.addr.port;
}
- const onListen = (scheme) => {
- // If the hostname is "0.0.0.0", we display "localhost" in console
- // because browsers in Windows don't resolve "0.0.0.0".
- // See the discussion in https://github.com/denoland/deno_std/issues/1165
- const hostname = listenOpts.hostname == "0.0.0.0"
- ? "localhost"
- : listenOpts.hostname;
- const port = listenOpts.port;
+ const addr = listener.addr;
+ // If the hostname is "0.0.0.0", we display "localhost" in console
+ // because browsers in Windows don't resolve "0.0.0.0".
+ // See the discussion in https://github.com/denoland/deno_std/issues/1165
+ const hostname = addr.hostname == "0.0.0.0" ? "localhost" : addr.hostname;
+ addr.hostname = hostname;
+ const onListen = (scheme) => {
if (options.onListen) {
- options.onListen({ hostname, port });
+ options.onListen(addr);
} else {
- console.log(`Listening on ${scheme}${hostname}:${port}/`);
+ console.log(`Listening on ${scheme}${addr.hostname}:${addr.port}/`);
}
};
@@ -625,7 +624,7 @@ function serveHttpOnListener(listener, signal, handler, onError, onListen) {
onListen(context.scheme);
- return serveHttpOn(context, callback);
+ return serveHttpOn(context, listener.addr, callback);
}
/**
@@ -641,10 +640,10 @@ function serveHttpOnConnection(connection, signal, handler, onError, onListen) {
onListen(context.scheme);
- return serveHttpOn(context, callback);
+ return serveHttpOn(context, connection.localAddr, callback);
}
-function serveHttpOn(context, callback) {
+function serveHttpOn(context, addr, callback) {
let ref = true;
let currentPromise = null;
@@ -700,6 +699,7 @@ function serveHttpOn(context, callback) {
})();
return {
+ addr,
finished,
async shutdown() {
if (!context.closing && !context.closed) {
diff --git a/tests/unit/serve_test.ts b/tests/unit/serve_test.ts
index 9b2870ebd..048529ae9 100644
--- a/tests/unit/serve_test.ts
+++ b/tests/unit/serve_test.ts
@@ -43,7 +43,10 @@ function onListen(
}
async function makeServer(
- handler: (req: Request) => Response | Promise<Response>,
+ handler: (
+ req: Request,
+ info: Deno.ServeHandlerInfo,
+ ) => Response | Promise<Response>,
): Promise<
{
finished: Promise<void>;
@@ -432,7 +435,7 @@ Deno.test(async function httpServerRejectsOnAddrInUse() {
Deno.test({ permissions: { net: true } }, async function httpServerBasic() {
const ac = new AbortController();
const deferred = Promise.withResolvers<void>();
- const listeningDeferred = Promise.withResolvers<void>();
+ const listeningDeferred = Promise.withResolvers<Deno.NetAddr>();
const server = Deno.serve({
handler: async (request, { remoteAddr }) => {
@@ -447,11 +450,13 @@ Deno.test({ permissions: { net: true } }, async function httpServerBasic() {
},
port: servePort,
signal: ac.signal,
- onListen: onListen(listeningDeferred.resolve),
+ onListen: (addr) => listeningDeferred.resolve(addr),
onError: createOnErrorCb(ac),
});
- await listeningDeferred.promise;
+ const addr = await listeningDeferred.promise;
+ assertEquals(addr.hostname, server.addr.hostname);
+ assertEquals(addr.port, server.addr.port);
const resp = await fetch(`http://127.0.0.1:${servePort}/`, {
headers: { "connection": "close" },
});
@@ -472,7 +477,7 @@ Deno.test(
async function httpServerOnListener() {
const ac = new AbortController();
const deferred = Promise.withResolvers<void>();
- const listeningDeferred = Promise.withResolvers<void>();
+ const listeningDeferred = Promise.withResolvers();
const listener = Deno.listen({ port: servePort });
const server = serveHttpOnListener(
listener,
@@ -3812,7 +3817,7 @@ Deno.test(
permissions: { run: true, read: true, write: true },
},
async function httpServerUnixDomainSocket() {
- const { promise, resolve } = Promise.withResolvers<{ path: string }>();
+ const { promise, resolve } = Promise.withResolvers<Deno.UnixAddr>();
const ac = new AbortController();
const filePath = tmpUnixSocketPath();
const server = Deno.serve(
@@ -3830,7 +3835,7 @@ Deno.test(
},
);
- assertEquals(await promise, { path: filePath });
+ assertEquals((await promise).path, filePath);
assertEquals(
"hello world!",
await curlRequest(["--unix-socket", filePath, "http://localhost"]),