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

Commit 5836ae5

Browse files
committed
http: make --insecure-http-parser configurable per-stream or per-server
Backport 7fc5656 Original commit message: 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 <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Rich Trott <[email protected]>
1 parent ec48296 commit 5836ae5

File tree

5 files changed

+136
-7
lines changed

5 files changed

+136
-7
lines changed

doc/api/http.md

+30-3
Original file line numberDiff line numberDiff line change
@@ -1721,11 +1721,30 @@ A collection of all the standard HTTP response status codes, and the
17211721
short description of each. For example, `http.STATUS_CODES[404] === 'Not
17221722
Found'`.
17231723

1724-
## http.createServer([requestListener])
1724+
## http.createServer([options][, requestListener])
17251725
<!-- YAML
17261726
added: v0.1.13
1727-
-->
1728-
- `requestListener` {Function}
1727+
changes:
1728+
- version: REPLACEME
1729+
pr-url: https://github.com/nodejs/node/pull/31448
1730+
description: The `insecureHTTPParser` option is supported now.
1731+
- version: v9.6.0, v8.12.0
1732+
pr-url: https://github.com/nodejs/node/pull/15752
1733+
description: The `options` argument is supported now.
1734+
-->
1735+
1736+
* `options` {Object}
1737+
* `IncomingMessage` {http.IncomingMessage} Specifies the `IncomingMessage`
1738+
class to be used. Useful for extending the original `IncomingMessage`.
1739+
**Default:** `IncomingMessage`.
1740+
* `ServerResponse` {http.ServerResponse} Specifies the `ServerResponse` class
1741+
to be used. Useful for extending the original `ServerResponse`. **Default:**
1742+
`ServerResponse`.
1743+
* `insecureHTTPParser` {boolean} Use an insecure HTTP parser that accepts
1744+
invalid HTTP headers when `true`. Using the insecure parser should be
1745+
avoided. See [`--insecure-http-parser`][] for more information.
1746+
**Default:** `false`
1747+
* `requestListener` {Function}
17291748

17301749
* Returns: {http.Server}
17311750

@@ -1820,6 +1839,9 @@ Defaults to 8KB. Configurable using the [`--max-http-header-size`][] CLI option.
18201839
<!-- YAML
18211840
added: v0.3.6
18221841
changes:
1842+
- version: REPLACEME
1843+
pr-url: https://github.com/nodejs/node/pull/31448
1844+
description: The `insecureHTTPParser` option is supported now.
18231845
- version: v7.5.0
18241846
pr-url: https://github.com/nodejs/node/pull/10638
18251847
description: The `options` parameter can be a WHATWG `URL` object.
@@ -1835,6 +1857,10 @@ changes:
18351857
`hostname`. Valid values are `4` or `6`. When unspecified, both IP v4 and
18361858
v6 will be used.
18371859
* `port` {number} Port of remote server. **Default:** `80`.
1860+
* `insecureHTTPParser` {boolean} Use an insecure HTTP parser that accepts
1861+
invalid HTTP headers when `true`. Using the insecure parser should be
1862+
avoided. See [`--insecure-http-parser`][] for more information.
1863+
**Default:** `false`
18381864
* `localAddress` {string} Local interface to bind for network connections.
18391865
* `socketPath` {string} Unix Domain Socket (use one of host:port or
18401866
socketPath).
@@ -1993,6 +2019,7 @@ will be emitted in the following order:
19932019
Note that setting the `timeout` option or using the `setTimeout` function will
19942020
not abort the request or do anything besides add a `timeout` event.
19952021

2022+
[`--insecure-http-parser`]: cli.html#cli_insecure_http_parser
19962023
[`--max-http-header-size`]: cli.html#cli_max_http_header_size_size
19972024
[`'checkContinue'`]: #http_event_checkcontinue
19982025
[`'request'`]: #http_event_request

lib/_http_client.js

+10-1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,14 @@ function ClientRequest(options, cb) {
170170
method = this.method = 'GET';
171171
}
172172

173+
const insecureHTTPParser = options.insecureHTTPParser;
174+
if (insecureHTTPParser !== undefined &&
175+
typeof insecureHTTPParser !== 'boolean') {
176+
throw new ERR_INVALID_ARG_TYPE(
177+
'insecureHTTPParser', 'boolean', insecureHTTPParser);
178+
}
179+
this.insecureHTTPParser = insecureHTTPParser;
180+
173181
this.path = options.path || '/';
174182
if (cb) {
175183
this.once('response', cb);
@@ -625,7 +633,8 @@ function tickOnSocket(req, socket) {
625633
req.connection = socket;
626634
parser.reinitialize(HTTPParser.RESPONSE,
627635
parser[is_reused_symbol],
628-
isLenient());
636+
req.insecureHTTPParser === undefined ?
637+
isLenient() : req.insecureHTTPParser);
629638
if (process.domain) {
630639
process.domain.add(parser);
631640
}

lib/_http_server.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -275,11 +275,21 @@ function Server(options, requestListener) {
275275
options = {};
276276
} else if (options == null || typeof options === 'object') {
277277
options = util._extend({}, options);
278+
} else {
279+
throw new ERR_INVALID_ARG_TYPE('options', 'object', options);
278280
}
279281

280282
this[kIncomingMessage] = options.IncomingMessage || IncomingMessage;
281283
this[kServerResponse] = options.ServerResponse || ServerResponse;
282284

285+
const insecureHTTPParser = options.insecureHTTPParser;
286+
if (insecureHTTPParser !== undefined &&
287+
typeof insecureHTTPParser !== 'boolean') {
288+
throw new ERR_INVALID_ARG_TYPE(
289+
'insecureHTTPParser', 'boolean', insecureHTTPParser);
290+
}
291+
this.insecureHTTPParser = insecureHTTPParser;
292+
283293
net.Server.call(this, { allowHalfOpen: true });
284294

285295
if (requestListener) {
@@ -337,7 +347,8 @@ function connectionListenerInternal(server, socket) {
337347
parser.reinitialize(
338348
HTTPParser.REQUEST,
339349
parser[is_reused_symbol],
340-
isLenient()
350+
server.insecureHTTPParser === undefined ?
351+
isLenient() : server.insecureHTTPParser
341352
);
342353
parser.socket = socket;
343354

lib/http.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ let maxHeaderSize;
3131

3232
const { Server } = server;
3333

34-
function createServer(requestListener) {
35-
return new Server(requestListener);
34+
function createServer(opts, requestListener) {
35+
return new Server(opts, requestListener);
3636
}
3737

3838
function request(options, cb) {
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)