1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-12-24 08:09:08 -05:00

http: fix content-length checking (denoland/deno_std#437)

Original: ce4e3ccdc3
This commit is contained in:
Vincent LE GOFF 2019-05-23 17:59:34 +02:00 committed by Ryan Dahl
parent b7082f1640
commit 632fbd7734
2 changed files with 70 additions and 34 deletions

View file

@ -196,6 +196,25 @@ export class ServerRequest {
} }
} }
function fixLength(req: ServerRequest): void {
const contentLength = req.headers.get("Content-Length");
if (contentLength) {
const arrClen = contentLength.split(",");
if (arrClen.length > 1) {
const distinct = [...new Set(arrClen.map((e): string => 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");
}
}
}
export async function readRequest( export async function readRequest(
bufr: BufReader bufr: BufReader
): Promise<[ServerRequest, BufState]> { ): Promise<[ServerRequest, BufState]> {
@ -211,6 +230,11 @@ export async function readRequest(
} }
[req.method, req.url, req.proto] = firstLine.split(" ", 3); [req.method, req.url, req.proto] = firstLine.split(" ", 3);
[req.headers, err] = await tp.readMIMEHeader(); [req.headers, err] = await tp.readMIMEHeader();
fixLength(req);
// TODO(zekth) : add parsing of headers eg:
// rfc: https://tools.ietf.org/html/rfc7230#section-3.3.2
// A sender MUST NOT send a Content-Length header field in any message
// that contains a Transfer-Encoding header field.
return [req, err]; return [req, err];
} }

View file

@ -322,55 +322,67 @@ test(async function testReadRequestError(): Promise<void> {
}, },
1: { in: "GET / HTTP/1.1\r\nheader:foo\r\n", err: "EOF", headers: [] }, 1: { in: "GET / HTTP/1.1\r\nheader:foo\r\n", err: "EOF", headers: [] },
2: { in: "", err: "EOF", headers: [] }, 2: { in: "", err: "EOF", headers: [] },
// 3: { 3: {
// in: "HEAD / HTTP/1.1\r\nContent-Length:4\r\n\r\n", in: "HEAD / HTTP/1.1\r\nContent-Length:4\r\n\r\n",
// err: "http: method cannot contain a Content-Length" err: "http: method cannot contain a Content-Length"
// }, },
4: { 4: {
in: "HEAD / HTTP/1.1\r\n\r\n", in: "HEAD / HTTP/1.1\r\n\r\n",
headers: [], headers: [],
err: null err: null
} },
// Multiple Content-Length values should either be // Multiple Content-Length values should either be
// deduplicated if same or reject otherwise // deduplicated if same or reject otherwise
// See Issue 16490. // See Issue 16490.
// 5: { 5: {
// in: in:
// "POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Length: 0\r\n\r\nGopher hey\r\n", "POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Length: 0\r\n\r\nGopher hey\r\n",
// err: "cannot contain multiple Content-Length headers" err: "cannot contain multiple Content-Length headers"
// }, },
// 6: { 6: {
// in: in:
// "POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Length: 6\r\n\r\nGopher\r\n", "POST / HTTP/1.1\r\nContent-Length: 10\r\nContent-Length: 6\r\n\r\nGopher\r\n",
// err: "cannot contain multiple Content-Length headers" err: "cannot contain multiple Content-Length headers"
// }, },
// 7: { 7: {
// in: in:
// "PUT / HTTP/1.1\r\nContent-Length: 6 \r\nContent-Length: 6\r\nContent-Length:6\r\n\r\nGopher\r\n", "PUT / HTTP/1.1\r\nContent-Length: 6 \r\nContent-Length: 6\r\nContent-Length:6\r\n\r\nGopher\r\n",
// err: null, err: null,
// headers: [{ key: "Content-Length", value: "6" }] headers: [{ key: "Content-Length", value: "6" }]
// }, },
// 8: { 8: {
// in: "PUT / HTTP/1.1\r\nContent-Length: 1\r\nContent-Length: 6 \r\n\r\n", in: "PUT / HTTP/1.1\r\nContent-Length: 1\r\nContent-Length: 6 \r\n\r\n",
// err: "cannot contain multiple Content-Length headers" err: "cannot contain multiple Content-Length headers"
// }, },
// Setting an empty header is swallowed by textproto
// see: readMIMEHeader()
// 9: { // 9: {
// in: "POST / HTTP/1.1\r\nContent-Length:\r\nContent-Length: 3\r\n\r\n", // in: "POST / HTTP/1.1\r\nContent-Length:\r\nContent-Length: 3\r\n\r\n",
// err: "cannot contain multiple Content-Length headers" // err: "cannot contain multiple Content-Length headers"
// }, // },
// 10: { 10: {
// in: "HEAD / HTTP/1.1\r\nContent-Length:0\r\nContent-Length: 0\r\n\r\n", in: "HEAD / HTTP/1.1\r\nContent-Length:0\r\nContent-Length: 0\r\n\r\n",
// headers: [{ key: "Content-Length", value: "0" }], headers: [{ key: "Content-Length", value: "0" }],
// err: null err: null
// } }
}; };
for (const p in testCases) { for (const p in testCases) {
const test = testCases[p]; const test = testCases[p];
const reader = new BufReader(new StringReader(test.in)); const reader = new BufReader(new StringReader(test.in));
const [req, err] = await readRequest(reader); let _err;
assertEquals(test.err, err); if (test.err && test.err != "EOF") {
for (const h of test.headers) { try {
assertEquals(req.headers.get(h.key), h.value); await readRequest(reader);
} catch (e) {
_err = e;
}
assertEquals(_err.message, test.err);
} else {
const [req, err] = await readRequest(reader);
assertEquals(test.err, err);
for (const h of test.headers) {
assertEquals(req.headers.get(h.key), h.value);
}
} }
} }
}); });