diff options
| author | Yoshiya Hinosawa <stibium121@gmail.com> | 2023-10-04 11:37:39 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-10-04 11:37:39 +0900 |
| commit | da0b945804f19903beac71b23ff1040ebdb9b554 (patch) | |
| tree | 8ec0508fbc04839f113b2d58169090d14b474cdd /cli | |
| parent | 8c1677ecbcbb474fc6a5ac9b5f73b562677bb829 (diff) | |
feat(unstable): add unix domain socket support to Deno.serve (#20759)
Diffstat (limited to 'cli')
| -rw-r--r-- | cli/tests/unit/net_test.ts | 7 | ||||
| -rw-r--r-- | cli/tests/unit/serve_test.ts | 35 | ||||
| -rw-r--r-- | cli/tests/unit/test_util.ts | 7 | ||||
| -rw-r--r-- | cli/tsc/dts/lib.deno.unstable.d.ts | 121 |
4 files changed, 163 insertions, 7 deletions
diff --git a/cli/tests/unit/net_test.ts b/cli/tests/unit/net_test.ts index 54edf31fc..2a98b5e26 100644 --- a/cli/tests/unit/net_test.ts +++ b/cli/tests/unit/net_test.ts @@ -9,8 +9,8 @@ import { delay, execCode, execCode2, + tmpUnixSocketPath, } from "./test_util.ts"; -import { join } from "../../../test_util/std/path/mod.ts"; // Since these tests may run in parallel, ensure this port is unique to this file const listenPort = 4503; @@ -49,11 +49,6 @@ Deno.test( }, ); -function tmpUnixSocketPath(): string { - const folder = Deno.makeTempDirSync(); - return join(folder, "socket"); -} - Deno.test( { ignore: Deno.build.os === "windows", diff --git a/cli/tests/unit/serve_test.ts b/cli/tests/unit/serve_test.ts index 193b04ed1..6f58db006 100644 --- a/cli/tests/unit/serve_test.ts +++ b/cli/tests/unit/serve_test.ts @@ -15,6 +15,7 @@ import { deferred, execCode, fail, + tmpUnixSocketPath, } from "./test_util.ts"; // Since these tests may run in parallel, ensure this port is unique to this file @@ -3715,3 +3716,37 @@ async function curlRequestWithStdErr(args: string[]) { assert(success); return [new TextDecoder().decode(stdout), new TextDecoder().decode(stderr)]; } + +Deno.test( + { + ignore: Deno.build.os === "windows", + permissions: { run: true, read: true, write: true }, + }, + async function httpServerUnixDomainSocket() { + const d = deferred(); + const ac = new AbortController(); + const filePath = tmpUnixSocketPath(); + const server = Deno.serve( + { + signal: ac.signal, + path: filePath, + onListen(info) { + d.resolve(info); + }, + onError: createOnErrorCb(ac), + }, + (_req, { remoteAddr }) => { + assertEquals(remoteAddr, { path: filePath, transport: "unix" }); + return new Response("hello world!"); + }, + ); + + assertEquals(await d, { path: filePath }); + assertEquals( + "hello world!", + await curlRequest(["--unix-socket", filePath, "http://localhost"]), + ); + ac.abort(); + await server.finished; + }, +); diff --git a/cli/tests/unit/test_util.ts b/cli/tests/unit/test_util.ts index 23713faf4..de1e8e8c5 100644 --- a/cli/tests/unit/test_util.ts +++ b/cli/tests/unit/test_util.ts @@ -2,7 +2,7 @@ import * as colors from "../../../test_util/std/fmt/colors.ts"; export { colors }; -import { resolve } from "../../../test_util/std/path/mod.ts"; +import { join, resolve } from "../../../test_util/std/path/mod.ts"; export { assert, assertEquals, @@ -81,3 +81,8 @@ export function execCode2(code: string) { }, }; } + +export function tmpUnixSocketPath(): string { + const folder = Deno.makeTempDirSync(); + return join(folder, "socket"); +} diff --git a/cli/tsc/dts/lib.deno.unstable.d.ts b/cli/tsc/dts/lib.deno.unstable.d.ts index 11510d144..4d909a789 100644 --- a/cli/tsc/dts/lib.deno.unstable.d.ts +++ b/cli/tsc/dts/lib.deno.unstable.d.ts @@ -1948,6 +1948,127 @@ declare namespace Deno { shutdown(): Promise<void>; } + export interface ServeUnixOptions { + /** The unix domain socket path to listen on. */ + path: string; + + /** An {@linkcode AbortSignal} to close the server and all connections. */ + signal?: AbortSignal; + + /** The handler to invoke when route handlers throw an error. */ + onError?: (error: unknown) => Response | Promise<Response>; + + /** The callback which is called when the server starts listening. */ + onListen?: (params: { path: string }) => void; + } + + /** Information for a unix domain socket HTTP request. + * + * @category HTTP Server + */ + export interface ServeUnixHandlerInfo { + /** The remote address of the connection. */ + remoteAddr: Deno.UnixAddr; + } + + /** A handler for unix domain socket HTTP requests. Consumes a request and returns a response. + * + * If a handler throws, the server calling the handler will assume the impact + * of the error is isolated to the individual request. It will catch the error + * and if necessary will close the underlying connection. + * + * @category HTTP Server + */ + export type ServeUnixHandler = ( + request: Request, + info: ServeUnixHandlerInfo, + ) => Response | Promise<Response>; + + /** + * @category HTTP Server + */ + export interface ServeUnixInit { + /** The handler to invoke to process each incoming request. */ + handler: ServeUnixHandler; + } + + /** Serves HTTP requests with the given option bag and handler. + * + * You can specify the socket path with `path` option. + * + * ```ts + * Deno.serve( + * { path: "path/to/socket" }, + * (_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, path: "path/to/socket" }, + * (_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 path/to/socket` on listening. If you like to + * change this behavior, you can specify a custom `onListen` callback. + * + * ```ts + * Deno.serve({ + * onListen({ path }) { + * console.log(`Server started at ${path}`); + * // ... more info specific to your server .. + * }, + * path: "path/to/socket", + * }, (_req) => new Response("Hello, world")); + * ``` + * + * @category HTTP Server + */ + export function serve( + options: ServeUnixOptions, + handler: ServeUnixHandler, + ): Server; + /** Serves HTTP requests with the given option bag. + * + * You can specify an object with the path option, which is the + * unix domain socket to listen on. + * + * ```ts + * const ac = new AbortController(); + * + * const server = Deno.serve({ + * path: "path/to/socket", + * handler: (_req) => new Response("Hello, world"), + * signal: ac.signal, + * onListen({ path }) { + * console.log(`Server started at ${path}`); + * }, + * }); + * server.finished.then(() => console.log("Server closed")); + * + * console.log("Closing server..."); + * ac.abort(); + * ``` + * + * @category HTTP Server + */ + export function serve( + options: ServeUnixInit & ServeUnixOptions, + ): Server; + /** * A namespace containing runtime APIs available in Jupyter notebooks. * |
