• 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] || 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