// 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')); // 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'].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. // OK, Content-Length, 0, X-CRASH, aaa... headers += 'a'.repeat(MAX - currentSize); // Generate valid headers if (valid) { headers = headers.slice(0, -1); } 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', () => { 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(() => { 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();