mirror of
https://github.com/nodejs/node.git
synced 2024-11-30 15:30:56 +01:00
632f263324
Allow the maximum size of HTTP headers to be overridden from the command line. co-authored-by: Matteo Collina <hello@matteocollina.com> PR-URL: https://github.com/nodejs/node/pull/24811 Fixes: https://github.com/nodejs/node/issues/24692 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Myles Borins <myles.borins@gmail.com> Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com> Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
187 lines
4.6 KiB
JavaScript
187 lines
4.6 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 = +(process.argv[2] || 8 * 1024); // Command line option, or 8KB.
|
|
|
|
const { getOptionValue } = require('internal/options');
|
|
|
|
console.log('pid is', process.pid);
|
|
console.log('max header size is', getOptionValue('--max-http-header-size'));
|
|
console.log('current http parser is', getOptionValue('--http-parser'));
|
|
|
|
// 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 -32 is needed instead of -1
|
|
headers = headers.slice(0, -32);
|
|
}
|
|
return headers + '\r\n\r\n';
|
|
}
|
|
|
|
function writeHeaders(socket, headers) {
|
|
const array = [];
|
|
const chunkSize = 100;
|
|
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.destroyed) {
|
|
console.log('socket was destroyed early, data left to write:',
|
|
array.join('').length);
|
|
return;
|
|
}
|
|
|
|
const chunk = array.shift();
|
|
|
|
if (chunk) {
|
|
console.log('writing chunk of size', chunk.length);
|
|
socket.write(chunk, next);
|
|
} else {
|
|
socket.end();
|
|
}
|
|
}
|
|
}
|
|
|
|
function test1() {
|
|
console.log('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();
|
|
});
|
|
|
|
// The socket might error but that's ok
|
|
sock.on('error', () => {});
|
|
});
|
|
|
|
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(test2);
|
|
}));
|
|
}));
|
|
}
|
|
|
|
const test2 = common.mustCall(() => {
|
|
console.log('test2');
|
|
let headers =
|
|
'GET / HTTP/1.1\r\n' +
|
|
'Host: localhost\r\n' +
|
|
'Agent: nod2\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.once('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(test3);
|
|
}));
|
|
}));
|
|
});
|
|
|
|
const test3 = common.mustCall(() => {
|
|
console.log('test3');
|
|
let headers =
|
|
'GET / HTTP/1.1\r\n' +
|
|
'Host: localhost\r\n' +
|
|
'Agent: nod3\r\n' +
|
|
'X-CRASH: ';
|
|
|
|
// /, Host, localhost, Agent, node, X-CRASH, a...
|
|
const currentSize = 1 + 4 + 9 + 5 + 4 + 7;
|
|
headers = fillHeaders(headers, currentSize, true);
|
|
|
|
console.log('writing', headers.length);
|
|
|
|
const server = http.createServer(common.mustCall((req, res) => {
|
|
res.end('hello from test3 server');
|
|
server.close();
|
|
}));
|
|
|
|
server.on('clientError', (err) => {
|
|
console.log(err.code);
|
|
if (err.code === 'HPE_HEADER_OVERFLOW') {
|
|
console.log(err.rawPacket.toString('hex'));
|
|
}
|
|
});
|
|
server.on('clientError', common.mustNotCall());
|
|
|
|
server.listen(0, common.mustCall(() => {
|
|
const client = net.connect(server.address().port);
|
|
client.on('connect', () => {
|
|
writeHeaders(client, headers);
|
|
client.resume();
|
|
});
|
|
|
|
client.pipe(process.stdout);
|
|
}));
|
|
});
|
|
|
|
test1();
|