import listen = Deno.listen;import Conn = Deno.Conn;import Reader = Deno.Reader;import { BufReader, BufWriter } from "./vendor/https/deno.land/std/io/bufio.ts";import { defer, promiseInterrupter } from "./promises.ts";import { initServeOptions, readRequest } from "./serveio.ts";import { createResponder, ServerResponder } from "./responder.ts";import ListenOptions = Deno.ListenOptions;import Listener = Deno.Listener;import { BodyReader } from "./readers.ts";
export type ClientRequest = { url: string; method: string; headers?: Headers; body?: string | Uint8Array | Reader;};
export type ServerResponse = { status: number; headers?: Headers; body?: string | Uint8Array | Reader;};
export type IncomingHttpRequest = { url: string; method: string; proto: string; headers: Headers; body?: BodyReader; trailers?: Headers; keepAlive?: KeepAlive; finalize: () => Promise<void>;};
export type KeepAlive = { timeout: number; max: number;};
export type ServerRequest = IncomingHttpRequest & { conn: Conn; bufWriter: BufWriter; bufReader: BufReader;} & ServerResponder;
export type IncomingHttpResponse = { proto: string; status: number; statusText: string; headers: Headers; body?: BodyReader; trailers?: Headers; finalize: () => Promise<void>;};
export type ClientResponse = IncomingHttpResponse & { conn: Conn; bufWriter: BufWriter; bufReader: BufReader;};
export type ServeOptions = { cancel?: Promise<void>; keepAliveTimeout?: number; readTimeout?: number;};
export type ServeListener = Deno.Closer;
function createListener(listenOptions: string | ListenOptions): Listener { if (typeof listenOptions === "string") { const [h, p] = listenOptions.split(":"); if (!p) { throw new Error("server: port must be specified"); } listenOptions = { port: parseInt(p) }; if (h) { listenOptions.hostname = h; } return listen(listenOptions); } else { return listen(listenOptions); }}
export function listenAndServe( addr: string, handler: (req: ServerRequest) => Promise<void>, opts?: ServeOptions): ServeListener;export function listenAndServe( listenOptions: ListenOptions, handler: (req: ServerRequest) => Promise<void>, opts?: ServeOptions): ServeListener;export function listenAndServe( listenOptions: string | ListenOptions, handler: (req: ServerRequest) => Promise<void>, opts: ServeOptions = {}): ServeListener { opts = initServeOptions(opts); let listener = createListener(listenOptions); let cancel: Promise<void>; let d = defer(); if (opts.cancel) { cancel = Promise.race([opts.cancel, d.promise]); } else { cancel = d.promise; } const throwIfCancelled = promiseInterrupter({ cancel }); let closed = false; const close = () => { if (!closed) { d.resolve(); listener.close(); closed = true; } }; const acceptRoutine = () => { if (closed) return; throwIfCancelled(listener.accept()) .then(conn => { handleKeepAliveConn(conn, handler, opts); acceptRoutine(); }) .catch(close); }; acceptRoutine(); return { close };}
function handleKeepAliveConn( conn: Conn, handler: (req: ServerRequest) => Promise<void>, opts: ServeOptions = {}) { const bufReader = new BufReader(conn); const bufWriter = new BufWriter(conn); const originalOpts = opts; scheduleReadRequest({ keepAliveTimeout: opts.readTimeout, readTimeout: opts.readTimeout, cancel: opts.cancel });
function scheduleReadRequest(opts: ServeOptions) { processRequest(opts) .then(scheduleReadRequest) .catch(() => conn.close()); }
async function processRequest(opts: ServeOptions): Promise<ServeOptions> { const req = await readRequest(bufReader, opts); const responder = createResponder(bufWriter); const nextReq: ServerRequest = { ...req, bufWriter, bufReader, conn, ...responder }; await handler(nextReq); await req.finalize(); let keepAliveTimeout = originalOpts.keepAliveTimeout; if (req.keepAlive && req.keepAlive.max <= 0) { throw Deno.EOF; } if (req.headers.get("connection") === "close") { throw Deno.EOF; } if (req.keepAlive) { keepAliveTimeout = Math.min( keepAliveTimeout!, req.keepAlive.timeout * 1000 ); } return { keepAliveTimeout, readTimeout: opts.readTimeout, cancel: opts.cancel }; }}