node/test/sequential/test-http-max-http-headers.js
Anna Henningsen aa943d098e http: make parser choice a runtime flag
Add a `--http-parser=llhttp` vs `--http-parser=traditional`
command line switch, to make testing and comparing the new
llhttp-based implementation easier.

PR-URL: https://github.com/nodejs/node/pull/24739
Refs: https://github.com/nodejs/node/issues/24730
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Matheus Marchini <mat@mmarchini.me>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: Ali Ijaz Sheikh <ofrobots@google.com>
2018-12-06 05:21:36 +01:00

166 lines
4.0 KiB
JavaScript

// Flags: --expose-internals
'use strict';
const common = require('../common');
const assert = require('assert');
const http = require('http');
const net = require('net');
const MAX = 8 * 1024; // 8KB
const { getOptionValue } = require('internal/options');
// Verify that we cannot receive more than 8KB of headers.
function once(cb) {
let called = false;
return () => {
if (!called) {
called = true;
cb();
}
};
}
function finished(client, callback) {
'abort error end'.split(' ').forEach((e) => {
client.on(e, once(() => setImmediate(callback)));
});
}
function fillHeaders(headers, currentSize, valid = false) {
// llhttp counts actual header name/value sizes, excluding the whitespace and
// stripped chars.
if (getOptionValue('--http-parser') === 'llhttp') {
// OK, Content-Length, 0, X-CRASH, aaa...
headers += 'a'.repeat(MAX - currentSize);
} else {
headers += 'a'.repeat(MAX - headers.length - 3);
}
// Generate valid headers
if (valid) {
// TODO(mcollina): understand why -9 is needed instead of -1
headers = headers.slice(0, -9);
}
return headers + '\r\n\r\n';
}
const timeout = common.platformTimeout(10);
function writeHeaders(socket, headers) {
const array = [];
// this is off from 1024 so that \r\n does not get split
const chunkSize = 1000;
let last = 0;
for (let i = 0; i < headers.length / chunkSize; i++) {
const current = (i + 1) * chunkSize;
array.push(headers.slice(last, current));
last = current;
}
// safety check we are chunking correctly
assert.strictEqual(array.join(''), headers);
next();
function next() {
if (socket.write(array.shift())) {
if (array.length === 0) {
socket.end();
} else {
setTimeout(next, timeout);
}
} else {
socket.once('drain', next);
}
}
}
function test1() {
let headers =
'HTTP/1.1 200 OK\r\n' +
'Content-Length: 0\r\n' +
'X-CRASH: ';
// OK, Content-Length, 0, X-CRASH, aaa...
const currentSize = 2 + 14 + 1 + 7;
headers = fillHeaders(headers, currentSize);
const server = net.createServer((sock) => {
sock.once('data', (chunk) => {
writeHeaders(sock, headers);
sock.resume();
});
});
server.listen(0, common.mustCall(() => {
const port = server.address().port;
const client = http.get({ port: port }, common.mustNotCall());
client.on('error', common.mustCall((err) => {
assert.strictEqual(err.code, 'HPE_HEADER_OVERFLOW');
server.close();
setImmediate(test2);
}));
}));
}
const test2 = common.mustCall(() => {
let headers =
'GET / HTTP/1.1\r\n' +
'Host: localhost\r\n' +
'Agent: node\r\n' +
'X-CRASH: ';
// /, Host, localhost, Agent, node, X-CRASH, a...
const currentSize = 1 + 4 + 9 + 5 + 4 + 7;
headers = fillHeaders(headers, currentSize);
const server = http.createServer(common.mustNotCall());
server.on('clientError', common.mustCall((err) => {
assert.strictEqual(err.code, 'HPE_HEADER_OVERFLOW');
}));
server.listen(0, common.mustCall(() => {
const client = net.connect(server.address().port);
client.on('connect', () => {
writeHeaders(client, headers);
client.resume();
});
finished(client, common.mustCall((err) => {
server.close();
setImmediate(test3);
}));
}));
});
const test3 = common.mustCall(() => {
let headers =
'GET / HTTP/1.1\r\n' +
'Host: localhost\r\n' +
'Agent: node\r\n' +
'X-CRASH: ';
// /, Host, localhost, Agent, node, X-CRASH, a...
const currentSize = 1 + 4 + 9 + 5 + 4 + 7;
headers = fillHeaders(headers, currentSize, true);
const server = http.createServer(common.mustCall((req, res) => {
res.end('hello world');
setImmediate(server.close.bind(server));
}));
server.listen(0, common.mustCall(() => {
const client = net.connect(server.address().port);
client.on('connect', () => {
writeHeaders(client, headers);
client.resume();
});
}));
});
test1();