Skip to content

Commit c204d14

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 nodejs#31446 in terms of code changes. Fixes: nodejs#31440 Refs: nodejs#31446 PR-URL: nodejs#31448 Reviewed-By: Sam Roberts <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent 09e4182 commit c204d14

File tree

4 files changed

+117
-2
lines changed

4 files changed

+117
-2
lines changed

doc/api/http.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2023,6 +2023,9 @@ Found'`.
20232023
<!-- YAML
20242024
added: v0.1.13
20252025
changes:
2026+
- version: REPLACEME
2027+
pr-url: https://github.com/nodejs/node/pull/31448
2028+
description: The `insecureHTTPParser` option is supported now.
20262029
- version: v9.6.0, v8.12.0
20272030
pr-url: https://github.com/nodejs/node/pull/15752
20282031
description: The `options` argument is supported now.
@@ -2035,6 +2038,10 @@ changes:
20352038
* `ServerResponse` {http.ServerResponse} Specifies the `ServerResponse` class
20362039
to be used. Useful for extending the original `ServerResponse`. **Default:**
20372040
`ServerResponse`.
2041+
* `insecureHTTPParser` {boolean} Use an insecure HTTP parser that accepts
2042+
invalid HTTP headers when `true`. Using the insecure parser should be
2043+
avoided. See [`--insecure-http-parser`][] for more information.
2044+
**Default:** `false`
20382045
* `requestListener` {Function}
20392046

20402047
* Returns: {http.Server}
@@ -2137,6 +2144,9 @@ Defaults to 8KB. Configurable using the [`--max-http-header-size`][] CLI option.
21372144
<!-- YAML
21382145
added: v0.3.6
21392146
changes:
2147+
- version: REPLACEME
2148+
pr-url: https://github.com/nodejs/node/pull/31448
2149+
description: The `insecureHTTPParser` option is supported now.
21402150
- version: v10.9.0
21412151
pr-url: https://github.com/nodejs/node/pull/21616
21422152
description: The `url` parameter can now be passed along with a separate
@@ -2170,6 +2180,10 @@ changes:
21702180
request to. **Default:** `'localhost'`.
21712181
* `hostname` {string} Alias for `host`. To support [`url.parse()`][],
21722182
`hostname` will be used if both `host` and `hostname` are specified.
2183+
* `insecureHTTPParser` {boolean} Use an insecure HTTP parser that accepts
2184+
invalid HTTP headers when `true`. Using the insecure parser should be
2185+
avoided. See [`--insecure-http-parser`][] for more information.
2186+
**Default:** `false`
21732187
* `localAddress` {string} Local interface to bind for network connections.
21742188
* `lookup` {Function} Custom lookup function. **Default:** [`dns.lookup()`][].
21752189
* `method` {string} A string specifying the HTTP request method. **Default:**
@@ -2322,6 +2336,7 @@ Setting the `timeout` option or using the `setTimeout()` function will
23222336
not abort the request or do anything besides add a `'timeout'` event.
23232337

23242338
[`--http-server-default-timeout`]: cli.html#cli_http_server_default_timeout_milliseconds
2339+
[`--insecure-http-parser`]: cli.html#cli_insecure_http_parser
23252340
[`--max-http-header-size`]: cli.html#cli_max_http_header_size_size
23262341
[`'checkContinue'`]: #http_event_checkcontinue
23272342
[`'request'`]: #http_event_request

lib/_http_client.js

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

184+
const insecureHTTPParser = options.insecureHTTPParser;
185+
if (insecureHTTPParser !== undefined &&
186+
typeof insecureHTTPParser !== 'boolean') {
187+
throw new ERR_INVALID_ARG_TYPE(
188+
'insecureHTTPParser', 'boolean', insecureHTTPParser);
189+
}
190+
this.insecureHTTPParser = insecureHTTPParser;
191+
184192
this.path = options.path || '/';
185193
if (cb) {
186194
this.once('response', cb);
@@ -669,7 +677,8 @@ function tickOnSocket(req, socket) {
669677
req.connection = socket;
670678
parser.initialize(HTTPParser.RESPONSE,
671679
new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req),
672-
isLenient());
680+
req.insecureHTTPParser === undefined ?
681+
isLenient() : req.insecureHTTPParser);
673682
parser.socket = socket;
674683
parser.outgoing = req;
675684
req.parser = parser;

lib/_http_server.js

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

335+
const insecureHTTPParser = options.insecureHTTPParser;
336+
if (insecureHTTPParser !== undefined &&
337+
typeof insecureHTTPParser !== 'boolean') {
338+
throw new ERR_INVALID_ARG_TYPE(
339+
'insecureHTTPParser', 'boolean', insecureHTTPParser);
340+
}
341+
this.insecureHTTPParser = insecureHTTPParser;
342+
335343
net.Server.call(this, { allowHalfOpen: true });
336344

337345
if (requestListener) {
@@ -412,7 +420,8 @@ function connectionListenerInternal(server, socket) {
412420
parser.initialize(
413421
HTTPParser.REQUEST,
414422
new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket),
415-
isLenient(),
423+
server.insecureHTTPParser === undefined ?
424+
isLenient() : server.insecureHTTPParser,
416425
);
417426
parser.socket = socket;
418427

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)