Skip to content

Commit

Permalink
feat: stabilize Deno.HttpServer.shutdown and Unix socket support (den…
Browse files Browse the repository at this point in the history
…oland#21463)

This commit stabilizes "Deno.HttpServer.shutdown" API as well as
Unix socket support in "Deno.serve" API.

---------

Co-authored-by: Yoshiya Hinosawa <[email protected]>
  • Loading branch information
bartlomieju and kt3k committed Dec 6, 2023
1 parent e372fc7 commit a931a47
Show file tree
Hide file tree
Showing 3 changed files with 130 additions and 142 deletions.
125 changes: 125 additions & 0 deletions cli/tsc/dts/lib.deno.ns.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5939,6 +5939,50 @@ declare namespace Deno {
handler: ServeHandler;
}

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;
}

/** An instance of the server created using `Deno.serve()` API.
*
* @category HTTP Server
Expand All @@ -5959,6 +6003,11 @@ declare namespace Deno {

/** Make the server not block the event loop from finishing. */
unref(): void;

/** Gracefully close the server. No more new connections will be accepted,
* while pending requests will be allowed to finish.
*/
shutdown(): Promise<void>;
}

/**
Expand All @@ -5978,6 +6027,55 @@ declare namespace Deno {
* @category HTTP Server
*/
export function serve(handler: ServeHandler): HttpServer;
/** 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,
): HttpServer;
/** Serves HTTP requests with the given option bag and handler.
*
* You can specify an object with a port and hostname option, which is the
Expand Down Expand Up @@ -6038,6 +6136,33 @@ declare namespace Deno {
options: ServeOptions | ServeTlsOptions,
handler: ServeHandler,
): HttpServer;
/** 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,
): HttpServer;
/** Serves HTTP requests with the given option bag.
*
* You can specify an object with a port and hostname option, which is the
Expand Down
132 changes: 0 additions & 132 deletions cli/tsc/dts/lib.deno.unstable.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2099,138 +2099,6 @@ declare namespace Deno {
readonly value: bigint;
}

/** An instance of the server created using `Deno.serve()` API.
*
* @category HTTP Server
*/
export interface HttpServer {
/** Gracefully close the server. No more new connections will be accepted,
* while pending requests will be allowed to finish.
*/
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.
*
Expand Down
15 changes: 5 additions & 10 deletions ext/http/http_next.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ static USE_WRITEV: Lazy<bool> = Lazy::new(|| {
false
});

// NOTE(bartlomieju): currently we don't have any unstable HTTP features,
// but let's keep this const here, because:
// a) we still need to support `--unstable-http` flag to not break user's CLI;
// b) we might add more unstable features in the future.
#[allow(dead_code)]
pub const UNSTABLE_FEATURE_NAME: &str = "http";

/// All HTTP/2 connections start with this byte string.
Expand Down Expand Up @@ -1183,16 +1188,6 @@ pub async fn op_http_close(

if graceful {
http_general_trace!("graceful shutdown");
// TODO(bartlomieju): replace with `state.feature_checker.check_or_exit`
// once we phase out `check_or_exit_with_legacy_fallback`
state
.borrow()
.feature_checker
.check_or_exit_with_legacy_fallback(
UNSTABLE_FEATURE_NAME,
"Deno.Server.shutdown",
);

// In a graceful shutdown, we close the listener and allow all the remaining connections to drain
join_handle.listen_cancel_handle().cancel();
poll_fn(|cx| join_handle.server_state.poll_complete(cx)).await;
Expand Down

0 comments on commit a931a47

Please sign in to comment.