View repository on GitHub

This file has been compiled to JS. View the original version here.
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
const { listen, copy, toAsyncIterator } = Deno;
import { BufReader, BufWriter, UnexpectedEOFError } from "../io/bufio.ts?js";
import { TextProtoReader } from "../textproto/mod.ts?js";
import { STATUS_TEXT } from "./http_status.ts?js";
import { assert } from "../testing/asserts.ts?js";
import { collectUint8Arrays, deferred, MuxAsyncIterator } from "../util/async.ts?js";
function bufWriter(w) {
    if (w instanceof BufWriter) {
        return w;
    }
    else {
        return new BufWriter(w);
    }
}
export function setContentLength(r) {
    if (!r.headers) {
        r.headers = new Headers();
    }
    if (r.body) {
        if (!r.headers.has("content-length")) {
            if (r.body instanceof Uint8Array) {
                const bodyLength = r.body.byteLength;
                r.headers.append("Content-Length", bodyLength.toString());
            }
            else {
                r.headers.append("Transfer-Encoding", "chunked");
            }
        }
    }
}
async function writeChunkedBody(w, r) {
    const writer = bufWriter(w);
    const encoder = new TextEncoder();
    for await (const chunk of toAsyncIterator(r)) {
        if (chunk.byteLength <= 0)
            continue;
        const start = encoder.encode(`${chunk.byteLength.toString(16)}\r\n`);
        const end = encoder.encode("\r\n");
        await writer.write(start);
        await writer.write(chunk);
        await writer.write(end);
    }
    const endChunk = encoder.encode("0\r\n\r\n");
    await writer.write(endChunk);
}
export async function writeResponse(w, r) {
    const protoMajor = 1;
    const protoMinor = 1;
    const statusCode = r.status || 200;
    const statusText = STATUS_TEXT.get(statusCode);
    const writer = bufWriter(w);
    if (!statusText) {
        throw Error("bad status code");
    }
    if (!r.body) {
        r.body = new Uint8Array();
    }
    let out = `HTTP/${protoMajor}.${protoMinor} ${statusCode} ${statusText}\r\n`;
    setContentLength(r);
    const headers = r.headers;
    for (const [key, value] of headers) {
        out += `${key}: ${value}\r\n`;
    }
    out += "\r\n";
    const header = new TextEncoder().encode(out);
    const n = await writer.write(header);
    assert(n === header.byteLength);
    if (r.body instanceof Uint8Array) {
        const n = await writer.write(r.body);
        assert(n === r.body.byteLength);
    }
    else if (headers.has("content-length")) {
        const bodyLength = parseInt(headers.get("content-length"));
        const n = await copy(writer, r.body);
        assert(n === bodyLength);
    }
    else {
        await writeChunkedBody(writer, r.body);
    }
    await writer.flush();
}
export class ServerRequest {
    constructor() {
        this.done = deferred();
    }
    async *bodyStream() {
        if (this.headers.has("content-length")) {
            const len = +this.headers.get("content-length");
            if (Number.isNaN(len)) {
                return new Uint8Array(0);
            }
            let buf = new Uint8Array(1024);
            let rr = await this.r.read(buf);
            let nread = rr === Deno.EOF ? 0 : rr;
            let nreadTotal = nread;
            while (rr !== Deno.EOF && nreadTotal < len) {
                yield buf.subarray(0, nread);
                buf = new Uint8Array(1024);
                rr = await this.r.read(buf);
                nread = rr === Deno.EOF ? 0 : rr;
                nreadTotal += nread;
            }
            yield buf.subarray(0, nread);
        }
        else {
            if (this.headers.has("transfer-encoding")) {
                const transferEncodings = this.headers
                    .get("transfer-encoding")
                    .split(",")
                    .map((e) => e.trim().toLowerCase());
                if (transferEncodings.includes("chunked")) {
                    // Based on https://tools.ietf.org/html/rfc2616#section-19.4.6
                    const tp = new TextProtoReader(this.r);
                    let line = await tp.readLine();
                    if (line === Deno.EOF)
                        throw new UnexpectedEOFError();
                    // TODO: handle chunk extension
                    let [chunkSizeString] = line.split(";");
                    let chunkSize = parseInt(chunkSizeString, 16);
                    if (Number.isNaN(chunkSize) || chunkSize < 0) {
                        throw new Error("Invalid chunk size");
                    }
                    while (chunkSize > 0) {
                        const data = new Uint8Array(chunkSize);
                        if ((await this.r.readFull(data)) === Deno.EOF) {
                            throw new UnexpectedEOFError();
                        }
                        yield data;
                        await this.r.readLine(); // Consume \r\n
                        line = await tp.readLine();
                        if (line === Deno.EOF)
                            throw new UnexpectedEOFError();
                        chunkSize = parseInt(line, 16);
                    }
                    const entityHeaders = await tp.readMIMEHeader();
                    if (entityHeaders !== Deno.EOF) {
                        for (let [k, v] of entityHeaders) {
                            this.headers.set(k, v);
                        }
                    }
                    /* Pseudo code from https://tools.ietf.org/html/rfc2616#section-19.4.6
                    length := 0
                    read chunk-size, chunk-extension (if any) and CRLF
                    while (chunk-size > 0) {
                      read chunk-data and CRLF
                      append chunk-data to entity-body
                      length := length + chunk-size
                      read chunk-size and CRLF
                    }
                    read entity-header
                    while (entity-header not empty) {
                      append entity-header to existing header fields
                      read entity-header
                    }
                    Content-Length := length
                    Remove "chunked" from Transfer-Encoding
                    */
                    return; // Must return here to avoid fall through
                }
                // TODO: handle other transfer-encoding types
            }
            // Otherwise...
            yield new Uint8Array(0);
        }
    }
    // Read the body of the request into a single Uint8Array
    async body() {
        return collectUint8Arrays(this.bodyStream());
    }
    async respond(r) {
        // Write our response!
        await writeResponse(this.w, r);
        // Signal that this request has been processed and the next pipelined
        // request on the same connection can be accepted.
        this.done.resolve();
    }
}
function fixLength(req) {
    const contentLength = req.headers.get("Content-Length");
    if (contentLength) {
        const arrClen = contentLength.split(",");
        if (arrClen.length > 1) {
            const distinct = [...new Set(arrClen.map((e) => e.trim()))];
            if (distinct.length > 1) {
                throw Error("cannot contain multiple Content-Length headers");
            }
            else {
                req.headers.set("Content-Length", distinct[0]);
            }
        }
        const c = req.headers.get("Content-Length");
        if (req.method === "HEAD" && c && c !== "0") {
            throw Error("http: method cannot contain a Content-Length");
        }
        if (c && req.headers.has("transfer-encoding")) {
            // A sender MUST NOT send a Content-Length header field in any message
            // that contains a Transfer-Encoding header field.
            // rfc: https://tools.ietf.org/html/rfc7230#section-3.3.2
            throw new Error("http: Transfer-Encoding and Content-Length cannot be send together");
        }
    }
}
// ParseHTTPVersion parses a HTTP version string.
// "HTTP/1.0" returns (1, 0, true).
// Ported from https://github.com/golang/go/blob/f5c43b9/src/net/http/request.go#L766-L792
export function parseHTTPVersion(vers) {
    switch (vers) {
        case "HTTP/1.1":
            return [1, 1];
        case "HTTP/1.0":
            return [1, 0];
        default: {
            const Big = 1000000; // arbitrary upper bound
            const digitReg = /^\d+$/; // test if string is only digit
            let major;
            let minor;
            if (!vers.startsWith("HTTP/")) {
                break;
            }
            const dot = vers.indexOf(".");
            if (dot < 0) {
                break;
            }
            let majorStr = vers.substring(vers.indexOf("/") + 1, dot);
            major = parseInt(majorStr);
            if (!digitReg.test(majorStr) ||
                isNaN(major) ||
                major < 0 ||
                major > Big) {
                break;
            }
            let minorStr = vers.substring(dot + 1);
            minor = parseInt(minorStr);
            if (!digitReg.test(minorStr) ||
                isNaN(minor) ||
                minor < 0 ||
                minor > Big) {
                break;
            }
            return [major, minor];
        }
    }
    throw new Error(`malformed HTTP version ${vers}`);
}
export async function readRequest(bufr) {
    const tp = new TextProtoReader(bufr);
    const firstLine = await tp.readLine(); // e.g. GET /index.html HTTP/1.0
    if (firstLine === Deno.EOF)
        return Deno.EOF;
    const headers = await tp.readMIMEHeader();
    if (headers === Deno.EOF)
        throw new UnexpectedEOFError();
    const req = new ServerRequest();
    req.r = bufr;
    [req.method, req.url, req.proto] = firstLine.split(" ", 3);
    [req.protoMinor, req.protoMajor] = parseHTTPVersion(req.proto);
    req.headers = headers;
    fixLength(req);
    return req;
}
export class Server {
    constructor(listener) {
        this.listener = listener;
        this.closing = false;
    }
    close() {
        this.closing = true;
        this.listener.close();
    }
    // Yields all HTTP requests on a single TCP connection.
    async *iterateHttpRequests(conn) {
        const bufr = new BufReader(conn);
        const w = new BufWriter(conn);
        let req;
        let err;
        while (!this.closing) {
            try {
                req = await readRequest(bufr);
            }
            catch (e) {
                err = e;
                break;
            }
            if (req === Deno.EOF) {
                break;
            }
            req.w = w;
            yield req;
            // Wait for the request to be processed before we accept a new request on
            // this connection.
            await req.done;
        }
        if (req === Deno.EOF) {
            // The connection was gracefully closed.
        }
        else if (err) {
            // An error was thrown while parsing request headers.
            await writeResponse(req.w, {
                status: 400,
                body: new TextEncoder().encode(`${err.message}\r\n\r\n`)
            });
        }
        else if (this.closing) {
            // There are more requests incoming but the server is closing.
            // TODO(ry): send a back a HTTP 503 Service Unavailable status.
        }
        conn.close();
    }
    // Accepts a new TCP connection and yields all HTTP requests that arrive on
    // it. When a connection is accepted, it also creates a new iterator of the
    // same kind and adds it to the request multiplexer so that another TCP
    // connection can be accepted.
    async *acceptConnAndIterateHttpRequests(mux) {
        if (this.closing)
            return;
        // Wait for a new connection.
        const conn = await this.listener.accept();
        // Try to accept another connection and add it to the multiplexer.
        mux.add(this.acceptConnAndIterateHttpRequests(mux));
        // Yield the requests that arrive on the just-accepted connection.
        yield* this.iterateHttpRequests(conn);
    }
    [Symbol.asyncIterator]() {
        const mux = new MuxAsyncIterator();
        mux.add(this.acceptConnAndIterateHttpRequests(mux));
        return mux.iterate();
    }
}
export function serve(addr) {
    const listener = listen("tcp", addr);
    return new Server(listener);
}
export async function listenAndServe(addr, handler) {
    const server = serve(addr);
    for await (const request of server) {
        handler(request);
    }
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kdWxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsibW9kdWxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLDBFQUEwRTtBQUMxRSxNQUFNLEVBQUUsTUFBTSxFQUFFLElBQUksRUFBRSxlQUFlLEVBQUUsR0FBRyxJQUFJLENBQUM7QUFLL0MsT0FBTyxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsa0JBQWtCLEVBQUUsMEJBQXVCO0FBQzFFLE9BQU8sRUFBRSxlQUFlLEVBQUUsK0JBQTRCO0FBQ3RELE9BQU8sRUFBRSxXQUFXLEVBQUUsNEJBQXlCO0FBQy9DLE9BQU8sRUFBRSxNQUFNLEVBQUUsaUNBQThCO0FBQy9DLE9BQU8sRUFDTCxrQkFBa0IsRUFDbEIsUUFBUSxFQUVSLGdCQUFnQixFQUNqQiw0QkFBeUI7QUFFMUIsU0FBUyxTQUFTLENBQUMsQ0FBUztJQUMxQixJQUFJLENBQUMsWUFBWSxTQUFTLEVBQUU7UUFDMUIsT0FBTyxDQUFDLENBQUM7S0FDVjtTQUFNO1FBQ0wsT0FBTyxJQUFJLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQztLQUN6QjtBQUNILENBQUM7QUFFRCxNQUFNLFVBQVUsZ0JBQWdCLENBQUMsQ0FBVztJQUMxQyxJQUFJLENBQUMsQ0FBQyxDQUFDLE9BQU8sRUFBRTtRQUNkLENBQUMsQ0FBQyxPQUFPLEdBQUcsSUFBSSxPQUFPLEVBQUUsQ0FBQztLQUMzQjtJQUVELElBQUksQ0FBQyxDQUFDLElBQUksRUFBRTtRQUNWLElBQUksQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxnQkFBZ0IsQ0FBQyxFQUFFO1lBQ3BDLElBQUksQ0FBQyxDQUFDLElBQUksWUFBWSxVQUFVLEVBQUU7Z0JBQ2hDLE1BQU0sVUFBVSxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsVUFBVSxDQUFDO2dCQUNyQyxDQUFDLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxnQkFBZ0IsRUFBRSxVQUFVLENBQUMsUUFBUSxFQUFFLENB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