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