Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved types for Deno.serve in 2.0 #23710

Open
lucacasonato opened this issue May 6, 2024 · 0 comments
Open

Improved types for Deno.serve in 2.0 #23710

lucacasonato opened this issue May 6, 2024 · 0 comments
Labels
maybe 2.0 a potential feature for Deno 2.0 that needs further discussion public API related to "Deno" namespace in JS

Comments

@lucacasonato
Copy link
Member

export namespace Deno {
  /** Additional information for an HTTP request and its connection.
   *
   * @category HTTP Server
   */
  export interface ServeHandlerInfo<Addr extends Deno.Addr = Deno.Addr> {
    /** The remote address of the connection. */
    remoteAddr: Addr;
  }

  /** A handler for 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 ServeHandler<Addr extends Deno.Addr = Deno.Addr> = (
    request: Request,
    info: ServeHandlerInfo<Addr>,
  ) => Response | Promise<Response>;

  /** Options which can be set when calling {@linkcode Deno.serve}.
   *
   * @category HTTP Server
   */
  export interface ServeOptions<Addr extends Deno.Addr = Deno.Addr> {
    /** 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?: (localAddr: Addr) => void;
  }

  /**
   * Options that can be passed to `Deno.serve` to create a server listening on
   * a TCP port.
   *
   * @category HTTP Server
   */
  export interface ServeTcpOptions extends ServeOptions<Deno.NetAddr> {
    /** The transport to use. */
    transport?: "tcp";

    /** The port to listen on.
     *
     * @default {8000} */
    port?: number;

    /** A literal IP address or host name that can be resolved to an IP address.
     *
     * __Note about `0.0.0.0`__ While listening `0.0.0.0` works on all platforms,
     * the browsers on Windows don't work with the address `0.0.0.0`.
     * You should show the message like `server running on localhost:8080` instead of
     * `server running on 0.0.0.0:8080` if your program supports Windows.
     *
     * @default {"0.0.0.0"} */
    hostname?: string;

    /** Sets `SO_REUSEPORT` on POSIX systems. */
    reusePort?: boolean;
  }

  /**
   * Options that can be passed to `Deno.serve` to create a server listening on
   * a Unix domain socket.
   *
   * @category HTTP Server
   */
  export interface ServeUnixOptions extends ServeOptions<Deno.UnixAddr> {
    /** The transport to use. */
    transport?: "unix";

    /** The unix domain socket path to listen on. */
    path: string;
  }

  /**
   * @category HTTP Server
   */
  export interface ServeInit<Addr extends Deno.Addr = Deno.Addr> {
    /** The handler to invoke to process each incoming request. */
    handler: ServeHandler<Addr>;
  }

  /** An instance of the server created using `Deno.serve()` API.
   *
   * @category HTTP Server
   */
  export interface HttpServer<Addr 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: Addr;

    /**
     * Make the server block the event loop from finishing.
     *
     * Note: the server blocks the event loop from finishing by default.
     * This method is only meaningful after `.unref()` is called.
     */
    ref(): void;

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

  /** Serves HTTP requests with the given handler.
   *
   * The below example serves with the port `8000` on hostname `"127.0.0.1"`.
   *
   * ```ts
   * Deno.serve((_req) => new Response("Hello, world"));
   * ```
   *
   * @category HTTP Server
   */
  export function serve(
    handler: ServeHandler<Deno.NetAddr>,
  ): HttpServer<Deno.NetAddr>;
   /** 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: ServeHandler<Deno.UnixAddr>,
  ): 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
   * 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:
      & ServeInit<Deno.NetAddr>
      & (ServeTcpOptions | (ServeTcpOptions & TlsCertifiedKeyOptions)),
  ): 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
   * 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:
      | ServeTcpOptions
      | (ServeTcpOptions & TlsCertifiedKeyOptions),
    handler: ServeHandler<Deno.NetAddr>,
  ): HttpServer<Deno.NetAddr>;
  /** 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: ServeInit<Deno.UnixAddr> & ServeUnixOptions,
  ): HttpServer<Deno.UnixAddr>;
}

Improvements:

  • remove ServeTlsOptions
  • remove ServeTlsInit (identical to ServeInit)
  • remove ServeUnixHandlerInfo
  • remove ServeUnixHandler
  • remove ServeUnixInit
  • remove 3 overloads of Deno.serve

Biggest breaking change is that most users of ServeOptions will now have to use ServeTcpOptions instead.

@lucacasonato lucacasonato added maybe 2.0 a potential feature for Deno 2.0 that needs further discussion public API related to "Deno" namespace in JS labels May 6, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
maybe 2.0 a potential feature for Deno 2.0 that needs further discussion public API related to "Deno" namespace in JS
Projects
None yet
Development

No branches or pull requests

1 participant