Skip to content
This repository was archived by the owner on Oct 16, 2021. It is now read-only.

Commit 7fc5656

Browse files
addaleaxsam-github
authored andcommitted
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 nodejs/node#31446 in terms of code changes. Fixes: nodejs/node#31440 Refs: nodejs/node#31446 Backport-PR-URL: nodejs/node#31500 PR-URL: nodejs/node#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 ab1fcb8 commit 7fc5656

4 files changed

Lines changed: 117 additions & 2 deletions

File tree

doc/api/http.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1917,6 +1917,9 @@ Found'`.
19171917
<!-- YAML
19181918
added: v0.1.13
19191919
changes:
1920+
- version: REPLACEME
1921+
pr-url: https://github.com/nodejs/node/pull/31448
1922+
description: The `insecureHTTPParser` option is supported now.
19201923
- version: v9.6.0, v8.12.0
19211924
pr-url: https://github.com/nodejs/node/pull/15752
19221925
description: The `options` argument is supported now.
@@ -1929,6 +1932,10 @@ changes:
19291932
* `ServerResponse` {http.ServerResponse} Specifies the `ServerResponse` class
19301933
to be used. Useful for extending the original `ServerResponse`. **Default:**
19311934
`ServerResponse`.
1935+
* `insecureHTTPParser` {boolean} Use an insecure HTTP parser that accepts
1936+
invalid HTTP headers when `true`. Using the insecure parser should be
1937+
avoided. See [`--insecure-http-parser`][] for more information.
1938+
**Default:** `false`
19321939
* `requestListener` {Function}
19331940

19341941
* Returns: {http.Server}
@@ -2031,6 +2038,9 @@ Defaults to 8KB. Configurable using the [`--max-http-header-size`][] CLI option.
20312038
<!-- YAML
20322039
added: v0.3.6
20332040
changes:
2041+
- version: REPLACEME
2042+
pr-url: https://github.com/nodejs/node/pull/31448
2043+
description: The `insecureHTTPParser` option is supported now.
20342044
- version: v10.9.0
20352045
pr-url: https://github.com/nodejs/node/pull/21616
20362046
description: The `url` parameter can now be passed along with a separate
@@ -2064,6 +2074,10 @@ changes:
20642074
request to. **Default:** `'localhost'`.
20652075
* `hostname` {string} Alias for `host`. To support [`url.parse()`][],
20662076
`hostname` will be used if both `host` and `hostname` are specified.
2077+
* `insecureHTTPParser` {boolean} Use an insecure HTTP parser that accepts
2078+
invalid HTTP headers when `true`. Using the insecure parser should be
2079+
avoided. See [`--insecure-http-parser`][] for more information.
2080+
**Default:** `false`
20672081
* `localAddress` {string} Local interface to bind for network connections.
20682082
* `lookup` {Function} Custom lookup function. **Default:** [`dns.lookup()`][].
20692083
* `method` {string} A string specifying the HTTP request method. **Default:**
@@ -2216,6 +2230,7 @@ Setting the `timeout` option or using the `setTimeout()` function will
22162230
not abort the request or do anything besides add a `'timeout'` event.
22172231

22182232
[`--http-server-default-timeout`]: cli.html#cli_http_server_default_timeout_milliseconds
2233+
[`--insecure-http-parser`]: cli.html#cli_insecure_http_parser
22192234
[`--max-http-header-size`]: cli.html#cli_max_http_header_size_size
22202235
[`'checkContinue'`]: #http_event_checkcontinue
22212236
[`'request'`]: #http_event_request

lib/_http_client.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,14 @@ function ClientRequest(input, options, cb) {
173173
method = this.method = 'GET';
174174
}
175175

176+
const insecureHTTPParser = options.insecureHTTPParser;
177+
if (insecureHTTPParser !== undefined &&
178+
typeof insecureHTTPParser !== 'boolean') {
179+
throw new ERR_INVALID_ARG_TYPE(
180+
'insecureHTTPParser', 'boolean', insecureHTTPParser);
181+
}
182+
this.insecureHTTPParser = insecureHTTPParser;
183+
176184
this.path = options.path || '/';
177185
if (cb) {
178186
this.once('response', cb);
@@ -656,7 +664,8 @@ function tickOnSocket(req, socket) {
656664
req.connection = socket;
657665
parser.initialize(HTTPParser.RESPONSE,
658666
new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req),
659-
isLenient());
667+
req.insecureHTTPParser === undefined ?
668+
isLenient() : req.insecureHTTPParser);
660669
parser.socket = socket;
661670
parser.outgoing = req;
662671
req.parser = parser;

lib/_http_server.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,14 @@ function Server(options, requestListener) {
329329
this[kIncomingMessage] = options.IncomingMessage || IncomingMessage;
330330
this[kServerResponse] = options.ServerResponse || ServerResponse;
331331

332+
const insecureHTTPParser = options.insecureHTTPParser;
333+
if (insecureHTTPParser !== undefined &&
334+
typeof insecureHTTPParser !== 'boolean') {
335+
throw new ERR_INVALID_ARG_TYPE(
336+
'insecureHTTPParser', 'boolean', insecureHTTPParser);
337+
}
338+
this.insecureHTTPParser = insecureHTTPParser;
339+
332340
net.Server.call(this, { allowHalfOpen: true });
333341

334342
if (requestListener) {
@@ -387,7 +395,8 @@ function connectionListenerInternal(server, socket) {
387395
parser.initialize(
388396
HTTPParser.REQUEST,
389397
new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket),
390-
isLenient(),
398+
server.insecureHTTPParser === undefined ?
399+
isLenient() : server.insecureHTTPParser,
391400
);
392401
parser.socket = socket;
393402

Lines changed: 82 additions & 0 deletions
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)