diff --git a/http/server.ts b/http/server.ts index baccaacfb9..68a9d8780e 100644 --- a/http/server.ts +++ b/http/server.ts @@ -102,6 +102,8 @@ export class ServerRequest { url: string; method: string; proto: string; + protoMinor: number; + protoMajor: number; headers: Headers; r: BufReader; w: BufWriter; @@ -215,6 +217,45 @@ function fixLength(req: ServerRequest): void { } } +// 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: string): [number, number, boolean] { + const Big = 1000000; // arbitrary upper bound + const digitReg = /^\d+$/; // test if string is only digit + let major: number; + let minor: number; + + switch (vers) { + case "HTTP/1.1": + return [1, 1, true]; + case "HTTP/1.0": + return [1, 0, true]; + } + + if (!vers.startsWith("HTTP/")) { + return [0, 0, false]; + } + + const dot = vers.indexOf("."); + if (dot < 0) { + return [0, 0, false]; + } + + let majorStr = vers.substring(vers.indexOf("/") + 1, dot); + major = parseInt(majorStr); + if (!digitReg.test(majorStr) || isNaN(major) || major < 0 || major > Big) { + return [0, 0, false]; + } + + let minorStr = vers.substring(dot + 1); + minor = parseInt(minorStr); + if (!digitReg.test(minorStr) || isNaN(minor) || minor < 0 || minor > Big) { + return [0, 0, false]; + } + return [major, minor, true]; +} + export async function readRequest( bufr: BufReader ): Promise<[ServerRequest, BufState]> { @@ -229,6 +270,13 @@ export async function readRequest( return [null, err]; } [req.method, req.url, req.proto] = firstLine.split(" ", 3); + + let ok: boolean; + [req.protoMinor, req.protoMajor, ok] = parseHTTPVersion(req.proto); + if (!ok) { + throw Error(`malformed HTTP version ${req.proto}`); + } + [req.headers, err] = await tp.readMIMEHeader(); fixLength(req); // TODO(zekth) : add parsing of headers eg: diff --git a/http/server_test.ts b/http/server_test.ts index 705fea1bae..fbab0234f0 100644 --- a/http/server_test.ts +++ b/http/server_test.ts @@ -12,7 +12,8 @@ import { Response, ServerRequest, writeResponse, - readRequest + readRequest, + parseHTTPVersion } from "./server.ts"; import { BufReader, BufWriter } from "../io/bufio.ts"; import { StringReader } from "../io/readers.ts"; @@ -386,4 +387,29 @@ test(async function testReadRequestError(): Promise { } } }); + +// Ported from https://github.com/golang/go/blob/f5c43b9/src/net/http/request_test.go#L535-L565 +test({ + name: "[http] parseHttpVersion", + fn(): void { + const testCases = [ + { in: "HTTP/0.9", want: [0, 9, true] }, + { in: "HTTP/1.0", want: [1, 0, true] }, + { in: "HTTP/1.1", want: [1, 1, true] }, + { in: "HTTP/3.14", want: [3, 14, true] }, + { in: "HTTP", want: [0, 0, false] }, + { in: "HTTP/one.one", want: [0, 0, false] }, + { in: "HTTP/1.1/", want: [0, 0, false] }, + { in: "HTTP/-1.0", want: [0, 0, false] }, + { in: "HTTP/0.-1", want: [0, 0, false] }, + { in: "HTTP/", want: [0, 0, false] }, + { in: "HTTP/1,0", want: [0, 0, false] } + ]; + for (const t of testCases) { + const r = parseHTTPVersion(t.in); + assertEquals(r, t.want, t.in); + } + } +}); + runIfMain(import.meta);