1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-10 16:11:13 -05:00

feat(std/http/server): Respond with 400 on request parse failure (#4551)

This commit is contained in:
Nayeem Rahman 2020-04-01 09:24:05 +01:00 committed by GitHub
parent 857d96001d
commit 017a611131
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 14 deletions

View file

@ -342,17 +342,38 @@ export function parseHTTPVersion(vers: string): [number, number] {
export async function readRequest( export async function readRequest(
conn: Deno.Conn, conn: Deno.Conn,
bufr: BufReader // The reader and writer buffers may be constructed externally so they can be
// shared by requests on the same connection -- see `Server`.
reader?: BufReader,
writer?: BufWriter
): Promise<ServerRequest | Deno.EOF> { ): Promise<ServerRequest | Deno.EOF> {
const tp = new TextProtoReader(bufr); reader = reader ?? new BufReader(conn);
const firstLine = await tp.readLine(); // e.g. GET /index.html HTTP/1.0 writer = writer ?? new BufWriter(conn);
if (firstLine === Deno.EOF) return Deno.EOF; const tp = new TextProtoReader(reader);
const headers = await tp.readMIMEHeader(); let firstLine: string | Deno.EOF;
if (headers === Deno.EOF) throw new Deno.errors.UnexpectedEof(); let headers: Headers | Deno.EOF;
try {
firstLine = await tp.readLine(); // e.g. GET /index.html HTTP/1.0
if (firstLine == Deno.EOF) {
return Deno.EOF;
}
headers = await tp.readMIMEHeader();
if (headers == Deno.EOF) {
throw new Deno.errors.UnexpectedEof();
}
} catch (error) {
// An error was thrown while parsing request headers.
await writeResponse(writer, {
status: 400,
body: encoder.encode(`${error.message}\r\n\r\n`),
});
throw error;
}
const req = new ServerRequest(); const req = new ServerRequest();
req.conn = conn; req.conn = conn;
req.r = bufr; req.r = reader;
req.w = writer;
[req.method, req.url, req.proto] = firstLine.split(" ", 3); [req.method, req.url, req.proto] = firstLine.split(" ", 3);
[req.protoMinor, req.protoMajor] = parseHTTPVersion(req.proto); [req.protoMinor, req.protoMajor] = parseHTTPVersion(req.proto);
req.headers = headers; req.headers = headers;

View file

@ -5,6 +5,7 @@ import {
assert, assert,
assertNotEOF, assertNotEOF,
assertNotEquals, assertNotEquals,
assertMatch,
} from "../testing/asserts.ts"; } from "../testing/asserts.ts";
import { import {
bodyReader, bodyReader,
@ -349,13 +350,28 @@ malformedHeader
`; `;
const reader = new BufReader(new StringReader(input)); const reader = new BufReader(new StringReader(input));
let err; let err;
let responseString: string;
try { try {
await readRequest(mockConn(), reader); // Capture whatever `readRequest()` attempts to write to the connection on
// error. We expect it to be a 400 response.
await readRequest(
mockConn({
write(p: Uint8Array): Promise<number> {
responseString = decode(p);
return Promise.resolve(p.length);
},
}),
reader
);
} catch (e) { } catch (e) {
err = e; err = e;
} }
assert(err instanceof Error); assert(err instanceof Error);
assertEquals(err.message, "malformed MIME header line: malformedHeader"); assertEquals(err.message, "malformed MIME header line: malformedHeader");
assertMatch(
responseString!,
/^HTTP\/1\.1 400 Bad Request\r\ncontent-length: \d+\r\n\r\n.*\r\n\r\n$/ms
);
}); });
// Ported from Go // Ported from Go

View file

@ -17,8 +17,8 @@ export function mockConn(base: Partial<Deno.Conn> = {}): Deno.Conn {
read: (): Promise<number | Deno.EOF> => { read: (): Promise<number | Deno.EOF> => {
return Promise.resolve(0); return Promise.resolve(0);
}, },
write: (): Promise<number> => { write: (p: Uint8Array): Promise<number> => {
return Promise.resolve(-1); return Promise.resolve(p.length);
}, },
close: (): void => {}, close: (): void => {},
...base, ...base,

View file

@ -146,14 +146,14 @@ export class Server implements AsyncIterable<ServerRequest> {
private async *iterateHttpRequests( private async *iterateHttpRequests(
conn: Conn conn: Conn
): AsyncIterableIterator<ServerRequest> { ): AsyncIterableIterator<ServerRequest> {
const bufr = new BufReader(conn); const reader = new BufReader(conn);
const w = new BufWriter(conn); const writer = new BufWriter(conn);
let req: ServerRequest | Deno.EOF = Deno.EOF; let req: ServerRequest | Deno.EOF = Deno.EOF;
let err: Error | undefined; let err: Error | undefined;
while (!this.closing) { while (!this.closing) {
try { try {
req = await readRequest(conn, bufr); req = await readRequest(conn, reader, writer);
} catch (e) { } catch (e) {
err = e; err = e;
} }
@ -161,7 +161,6 @@ export class Server implements AsyncIterable<ServerRequest> {
break; break;
} }
req.w = w;
yield req; yield req;
// Wait for the request to be processed before we accept a new request on // Wait for the request to be processed before we accept a new request on