Skip to content

Commit d6f1e39

Browse files
committed
http: make --insecure-http-parser configurable per-stream or per-server
From the issue: > Some servers deviate from HTTP spec enougth that Node.js can't > communicate with them, but "work" when `--insecure-http-parser` > is enabled globally. It would be useful to be able to use this > mode, as a client, only when connecting to known bad servers. This is largely equivalent to #31446 in terms of code changes. Fixes: #31440 Refs: #31446 PR-URL: #31448 Reviewed-By: Sam Roberts <vieuxtech@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Rich Trott <rtrott@gmail.com>
1 parent 748eae9 commit d6f1e39

File tree

4 files changed

+117
-2
lines changed

4 files changed

+117
-2
lines changed

doc/api/http.md

+15
Original file line numberDiff line numberDiff line change
@@ -2031,6 +2031,9 @@ Found'`.
20312031
<!-- YAML
20322032
added: v0.1.13
20332033
changes:
2034+
- version: REPLACEME
2035+
pr-url: https://github.com/nodejs/node/pull/31448
2036+
description: The `insecureHTTPParser` option is supported now.
20342037
- version: v13.3.0
20352038
pr-url: https://github.com/nodejs/node/pull/30570
20362039
description: The `maxHeaderSize` option is supported now.
@@ -2046,6 +2049,10 @@ changes:
20462049
* `ServerResponse` {http.ServerResponse} Specifies the `ServerResponse` class
20472050
to be used. Useful for extending the original `ServerResponse`. **Default:**
20482051
`ServerResponse`.
2052+
* `insecureHTTPParser` {boolean} Use an insecure HTTP parser that accepts
2053+
invalid HTTP headers when `true`. Using the insecure parser should be
2054+
avoided. See [`--insecure-http-parser`][] for more information.
2055+
**Default:** `false`
20492056
* `maxHeaderSize` {number} Optionally overrides the value of
20502057
[`--max-http-header-size`][] for requests received by this server, i.e.
20512058
the maximum length of request headers in bytes.
@@ -2155,6 +2162,9 @@ This can be overridden for servers and client requests by passing the
21552162
<!-- YAML
21562163
added: v0.3.6
21572164
changes:
2165+
- version: REPLACEME
2166+
pr-url: https://github.com/nodejs/node/pull/31448
2167+
description: The `insecureHTTPParser` option is supported now.
21582168
- version: v13.3.0
21592169
pr-url: https://github.com/nodejs/node/pull/30570
21602170
description: The `maxHeaderSize` option is supported now.
@@ -2191,6 +2201,10 @@ changes:
21912201
request to. **Default:** `'localhost'`.
21922202
* `hostname` {string} Alias for `host`. To support [`url.parse()`][],
21932203
`hostname` will be used if both `host` and `hostname` are specified.
2204+
* `insecureHTTPParser` {boolean} Use an insecure HTTP parser that accepts
2205+
invalid HTTP headers when `true`. Using the insecure parser should be
2206+
avoided. See [`--insecure-http-parser`][] for more information.
2207+
**Default:** `false`
21942208
* `localAddress` {string} Local interface to bind for network connections.
21952209
* `lookup` {Function} Custom lookup function. **Default:** [`dns.lookup()`][].
21962210
* `maxHeaderSize` {number} Optionally overrides the value of
@@ -2364,6 +2378,7 @@ will be emitted in the following order:
23642378
Setting the `timeout` option or using the `setTimeout()` function will
23652379
not abort the request or do anything besides add a `'timeout'` event.
23662380

2381+
[`--insecure-http-parser`]: cli.html#cli_insecure_http_parser
23672382
[`--max-http-header-size`]: cli.html#cli_max_http_header_size_size
23682383
[`'checkContinue'`]: #http_event_checkcontinue
23692384
[`'request'`]: #http_event_request

lib/_http_client.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,14 @@ function ClientRequest(input, options, cb) {
187187
validateInteger(maxHeaderSize, 'maxHeaderSize', 0);
188188
this.maxHeaderSize = maxHeaderSize;
189189

190+
const insecureHTTPParser = options.insecureHTTPParser;
191+
if (insecureHTTPParser !== undefined &&
192+
typeof insecureHTTPParser !== 'boolean') {
193+
throw new ERR_INVALID_ARG_TYPE(
194+
'insecureHTTPParser', 'boolean', insecureHTTPParser);
195+
}
196+
this.insecureHTTPParser = insecureHTTPParser;
197+
190198
this.path = options.path || '/';
191199
if (cb) {
192200
this.once('response', cb);
@@ -683,7 +691,8 @@ function tickOnSocket(req, socket) {
683691
parser.initialize(HTTPParser.RESPONSE,
684692
new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req),
685693
req.maxHeaderSize || 0,
686-
isLenient());
694+
req.insecureHTTPParser === undefined ?
695+
isLenient() : req.insecureHTTPParser);
687696
parser.socket = socket;
688697
parser.outgoing = req;
689698
req.parser = parser;

lib/_http_server.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,14 @@ function Server(options, requestListener) {
335335
validateInteger(maxHeaderSize, 'maxHeaderSize', 0);
336336
this.maxHeaderSize = maxHeaderSize;
337337

338+
const insecureHTTPParser = options.insecureHTTPParser;
339+
if (insecureHTTPParser !== undefined &&
340+
typeof insecureHTTPParser !== 'boolean') {
341+
throw new ERR_INVALID_ARG_TYPE(
342+
'insecureHTTPParser', 'boolean', insecureHTTPParser);
343+
}
344+
this.insecureHTTPParser = insecureHTTPParser;
345+
338346
net.Server.call(this, { allowHalfOpen: true });
339347

340348
if (requestListener) {
@@ -416,7 +424,8 @@ function connectionListenerInternal(server, socket) {
416424
HTTPParser.REQUEST,
417425
new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket),
418426
server.maxHeaderSize || 0,
419-
isLenient(),
427+
server.insecureHTTPParser === undefined ?
428+
isLenient() : server.insecureHTTPParser,
420429
);
421430
parser.socket = socket;
422431

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
'use strict';
2+
const common = require('../common');
3+
const assert = require('assert');
4+
const http = require('http');
5+
const MakeDuplexPair = require('../common/duplexpair');
6+
7+
// Test that setting the `maxHeaderSize` option works on a per-stream-basis.
8+
9+
// Test 1: The server sends an invalid header.
10+
{
11+
const { clientSide, serverSide } = MakeDuplexPair();
12+
13+
const req = http.request({
14+
createConnection: common.mustCall(() => clientSide),
15+
insecureHTTPParser: true
16+
}, common.mustCall((res) => {
17+
assert.strictEqual(res.headers.hello, 'foo\x08foo');
18+
res.resume(); // We don’t actually care about contents.
19+
res.on('end', common.mustCall());
20+
}));
21+
req.end();
22+
23+
serverSide.resume(); // Dump the request
24+
serverSide.end('HTTP/1.1 200 OK\r\n' +
25+
'Hello: foo\x08foo\r\n' +
26+
'Content-Length: 0\r\n' +
27+
'\r\n\r\n');
28+
}
29+
30+
// Test 2: The same as Test 1 except without the option, to make sure it fails.
31+
{
32+
const { clientSide, serverSide } = MakeDuplexPair();
33+
34+
const req = http.request({
35+
createConnection: common.mustCall(() => clientSide)
36+
}, common.mustNotCall());
37+
req.end();
38+
req.on('error', common.mustCall());
39+
40+
serverSide.resume(); // Dump the request
41+
serverSide.end('HTTP/1.1 200 OK\r\n' +
42+
'Hello: foo\x08foo\r\n' +
43+
'Content-Length: 0\r\n' +
44+
'\r\n\r\n');
45+
}
46+
47+
// Test 3: The client sends an invalid header.
48+
{
49+
const testData = 'Hello, World!\n';
50+
const server = http.createServer(
51+
{ insecureHTTPParser: true },
52+
common.mustCall((req, res) => {
53+
res.statusCode = 200;
54+
res.setHeader('Content-Type', 'text/plain');
55+
res.end(testData);
56+
}));
57+
58+
server.on('clientError', common.mustNotCall());
59+
60+
const { clientSide, serverSide } = MakeDuplexPair();
61+
serverSide.server = server;
62+
server.emit('connection', serverSide);
63+
64+
clientSide.write('GET / HTTP/1.1\r\n' +
65+
'Hello: foo\x08foo\r\n' +
66+
'\r\n\r\n');
67+
}
68+
69+
// Test 4: The same as Test 3 except without the option, to make sure it fails.
70+
{
71+
const server = http.createServer(common.mustNotCall());
72+
73+
server.on('clientError', common.mustCall());
74+
75+
const { clientSide, serverSide } = MakeDuplexPair();
76+
serverSide.server = server;
77+
server.emit('connection', serverSide);
78+
79+
clientSide.write('GET / HTTP/1.1\r\n' +
80+
'Hello: foo\x08foo\r\n' +
81+
'\r\n\r\n');
82+
}

0 commit comments

Comments
 (0)