1// Flags: --expose-internals 2'use strict'; 3const common = require('../common'); 4const assert = require('assert'); 5const http = require('http'); 6const net = require('net'); 7const MAX = +(process.argv[2] || 8 * 1024); // Command line option, or 8KB. 8 9const { getOptionValue } = require('internal/options'); 10 11console.log('pid is', process.pid); 12console.log('max header size is', getOptionValue('--max-http-header-size')); 13console.log('current http parser is', getOptionValue('--http-parser')); 14 15// Verify that we cannot receive more than 8KB of headers. 16 17function once(cb) { 18 let called = false; 19 return () => { 20 if (!called) { 21 called = true; 22 cb(); 23 } 24 }; 25} 26 27function finished(client, callback) { 28 ['abort', 'error', 'end'].forEach((e) => { 29 client.on(e, once(() => setImmediate(callback))); 30 }); 31} 32 33function fillHeaders(headers, currentSize, valid = false) { 34 // `llhttp` counts actual header name/value sizes, excluding the whitespace 35 // and stripped chars. 36 if (getOptionValue('--http-parser') === 'llhttp') { 37 // OK, Content-Length, 0, X-CRASH, aaa... 38 headers += 'a'.repeat(MAX - currentSize); 39 } else { 40 headers += 'a'.repeat(MAX - headers.length - 3); 41 } 42 43 // Generate valid headers 44 if (valid) { 45 // TODO(mcollina): understand why -32 is needed instead of -1 46 headers = headers.slice(0, -32); 47 } 48 return headers + '\r\n\r\n'; 49} 50 51function writeHeaders(socket, headers) { 52 const array = []; 53 const chunkSize = 100; 54 let last = 0; 55 56 for (let i = 0; i < headers.length / chunkSize; i++) { 57 const current = (i + 1) * chunkSize; 58 array.push(headers.slice(last, current)); 59 last = current; 60 } 61 62 // Safety check we are chunking correctly 63 assert.strictEqual(array.join(''), headers); 64 65 next(); 66 67 function next() { 68 if (socket.destroyed) { 69 console.log('socket was destroyed early, data left to write:', 70 array.join('').length); 71 return; 72 } 73 74 const chunk = array.shift(); 75 76 if (chunk) { 77 console.log('writing chunk of size', chunk.length); 78 socket.write(chunk, next); 79 } else { 80 socket.end(); 81 } 82 } 83} 84 85function test1() { 86 console.log('test1'); 87 let headers = 88 'HTTP/1.1 200 OK\r\n' + 89 'Content-Length: 0\r\n' + 90 'X-CRASH: '; 91 92 // OK, Content-Length, 0, X-CRASH, aaa... 93 const currentSize = 2 + 14 + 1 + 7; 94 headers = fillHeaders(headers, currentSize); 95 96 const server = net.createServer((sock) => { 97 sock.once('data', () => { 98 writeHeaders(sock, headers); 99 sock.resume(); 100 }); 101 102 // The socket might error but that's ok 103 sock.on('error', () => {}); 104 }); 105 106 server.listen(0, common.mustCall(() => { 107 const port = server.address().port; 108 const client = http.get({ port: port }, common.mustNotCall()); 109 110 client.on('error', common.mustCall((err) => { 111 assert.strictEqual(err.code, 'HPE_HEADER_OVERFLOW'); 112 server.close(test2); 113 })); 114 })); 115} 116 117const test2 = common.mustCall(() => { 118 console.log('test2'); 119 let headers = 120 'GET / HTTP/1.1\r\n' + 121 'Host: localhost\r\n' + 122 'Agent: nod2\r\n' + 123 'X-CRASH: '; 124 125 // /, Host, localhost, Agent, node, X-CRASH, a... 126 const currentSize = 1 + 4 + 9 + 5 + 4 + 7; 127 headers = fillHeaders(headers, currentSize); 128 129 const server = http.createServer(common.mustNotCall()); 130 131 server.once('clientError', common.mustCall((err) => { 132 assert.strictEqual(err.code, 'HPE_HEADER_OVERFLOW'); 133 })); 134 135 server.listen(0, common.mustCall(() => { 136 const client = net.connect(server.address().port); 137 client.on('connect', () => { 138 writeHeaders(client, headers); 139 client.resume(); 140 }); 141 142 finished(client, common.mustCall(() => { 143 server.close(test3); 144 })); 145 })); 146}); 147 148const test3 = common.mustCall(() => { 149 console.log('test3'); 150 let headers = 151 'GET / HTTP/1.1\r\n' + 152 'Host: localhost\r\n' + 153 'Agent: nod3\r\n' + 154 'X-CRASH: '; 155 156 // /, Host, localhost, Agent, node, X-CRASH, a... 157 const currentSize = 1 + 4 + 9 + 5 + 4 + 7; 158 headers = fillHeaders(headers, currentSize, true); 159 160 console.log('writing', headers.length); 161 162 const server = http.createServer(common.mustCall((req, res) => { 163 res.end('hello from test3 server'); 164 server.close(); 165 })); 166 167 server.on('clientError', (err) => { 168 console.log(err.code); 169 if (err.code === 'HPE_HEADER_OVERFLOW') { 170 console.log(err.rawPacket.toString('hex')); 171 } 172 }); 173 server.on('clientError', common.mustNotCall()); 174 175 server.listen(0, common.mustCall(() => { 176 const client = net.connect(server.address().port); 177 client.on('connect', () => { 178 writeHeaders(client, headers); 179 client.resume(); 180 }); 181 182 client.pipe(process.stdout); 183 })); 184}); 185 186test1(); 187