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

Commit

Permalink
http: make --insecure-http-parser configurable per-stream or per-server
Browse files Browse the repository at this point in the history
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 <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>
  • Loading branch information
zsw007 committed Feb 12, 2020
1 parent b9eeafe commit 5d54f9a
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 7 deletions.
33 changes: 30 additions & 3 deletions doc/api/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -1721,11 +1721,30 @@ A collection of all the standard HTTP response status codes, and the
short description of each. For example, `http.STATUS_CODES[404] === 'Not
Found'`.

## http.createServer([requestListener])
## http.createServer([options][, requestListener])
<!-- YAML
added: v0.1.13
-->
- `requestListener` {Function}
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/31448
description: The `insecureHTTPParser` option is supported now.
- version: v9.6.0, v8.12.0
pr-url: https://github.com/nodejs/node/pull/15752
description: The `options` argument is supported now.
-->

* `options` {Object}
* `IncomingMessage` {http.IncomingMessage} Specifies the `IncomingMessage`
class to be used. Useful for extending the original `IncomingMessage`.
**Default:** `IncomingMessage`.
* `ServerResponse` {http.ServerResponse} Specifies the `ServerResponse` class
to be used. Useful for extending the original `ServerResponse`. **Default:**
`ServerResponse`.
* `insecureHTTPParser` {boolean} Use an insecure HTTP parser that accepts
invalid HTTP headers when `true`. Using the insecure parser should be
avoided. See [`--insecure-http-parser`][] for more information.
**Default:** `false`
* `requestListener` {Function}

* Returns: {http.Server}

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

[`--insecure-http-parser`]: cli.html#cli_insecure_http_parser
[`--max-http-header-size`]: cli.html#cli_max_http_header_size_size
[`'checkContinue'`]: #http_event_checkcontinue
[`'request'`]: #http_event_request
Expand Down
11 changes: 10 additions & 1 deletion lib/_http_client.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,14 @@ function ClientRequest(options, cb) {
method = this.method = 'GET';
}

const insecureHTTPParser = options.insecureHTTPParser;
if (insecureHTTPParser !== undefined &&
typeof insecureHTTPParser !== 'boolean') {
throw new ERR_INVALID_ARG_TYPE(
'insecureHTTPParser', 'boolean', insecureHTTPParser);
}
this.insecureHTTPParser = insecureHTTPParser;

this.path = options.path || '/';
if (cb) {
this.once('response', cb);
Expand Down Expand Up @@ -625,7 +633,8 @@ function tickOnSocket(req, socket) {
req.connection = socket;
parser.reinitialize(HTTPParser.RESPONSE,
parser[is_reused_symbol],
isLenient());
req.insecureHTTPParser === undefined ?
isLenient() : req.insecureHTTPParser);
if (process.domain) {
process.domain.add(parser);
}
Expand Down
13 changes: 12 additions & 1 deletion lib/_http_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -275,11 +275,21 @@ function Server(options, requestListener) {
options = {};
} else if (options == null || typeof options === 'object') {
options = util._extend({}, options);
} else {
throw new ERR_INVALID_ARG_TYPE('options', 'object', options);
}

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

const insecureHTTPParser = options.insecureHTTPParser;
if (insecureHTTPParser !== undefined &&
typeof insecureHTTPParser !== 'boolean') {
throw new ERR_INVALID_ARG_TYPE(
'insecureHTTPParser', 'boolean', insecureHTTPParser);
}
this.insecureHTTPParser = insecureHTTPParser;

net.Server.call(this, { allowHalfOpen: true });

if (requestListener) {
Expand Down Expand Up @@ -337,7 +347,8 @@ function connectionListenerInternal(server, socket) {
parser.reinitialize(
HTTPParser.REQUEST,
parser[is_reused_symbol],
isLenient()
server.insecureHTTPParser === undefined ?
isLenient() : server.insecureHTTPParser
);
parser.socket = socket;

Expand Down
4 changes: 2 additions & 2 deletions lib/http.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ let maxHeaderSize;

const { Server } = server;

function createServer(requestListener) {
return new Server(requestListener);
function createServer(opts, requestListener) {
return new Server(opts, requestListener);
}

function request(options, cb) {
Expand Down
82 changes: 82 additions & 0 deletions test/parallel/test-http-insecure-parser-per-stream.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
'use strict';
const common = require('../common');
const assert = require('assert');
const http = require('http');
const MakeDuplexPair = require('../common/duplexpair');

// Test that setting the `maxHeaderSize` option works on a per-stream-basis.

// Test 1: The server sends an invalid header.
{
const { clientSide, serverSide } = MakeDuplexPair();

const req = http.request({
createConnection: common.mustCall(() => clientSide),
insecureHTTPParser: true
}, common.mustCall((res) => {
assert.strictEqual(res.headers.hello, 'foo\x08foo');
res.resume(); // We don’t actually care about contents.
res.on('end', common.mustCall());
}));
req.end();

serverSide.resume(); // Dump the request
serverSide.end('HTTP/1.1 200 OK\r\n' +
'Hello: foo\x08foo\r\n' +
'Content-Length: 0\r\n' +
'\r\n\r\n');
}

// Test 2: The same as Test 1 except without the option, to make sure it fails.
{
const { clientSide, serverSide } = MakeDuplexPair();

const req = http.request({
createConnection: common.mustCall(() => clientSide)
}, common.mustNotCall());
req.end();
req.on('error', common.mustCall());

serverSide.resume(); // Dump the request
serverSide.end('HTTP/1.1 200 OK\r\n' +
'Hello: foo\x08foo\r\n' +
'Content-Length: 0\r\n' +
'\r\n\r\n');
}

// Test 3: The client sends an invalid header.
{
const testData = 'Hello, World!\n';
const server = http.createServer(
{ insecureHTTPParser: true },
common.mustCall((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end(testData);
}));

server.on('clientError', common.mustNotCall());

const { clientSide, serverSide } = MakeDuplexPair();
serverSide.server = server;
server.emit('connection', serverSide);

clientSide.write('GET / HTTP/1.1\r\n' +
'Hello: foo\x08foo\r\n' +
'\r\n\r\n');
}

// Test 4: The same as Test 3 except without the option, to make sure it fails.
{
const server = http.createServer(common.mustNotCall());

server.on('clientError', common.mustCall());

const { clientSide, serverSide } = MakeDuplexPair();
serverSide.server = server;
server.emit('connection', serverSide);

clientSide.write('GET / HTTP/1.1\r\n' +
'Hello: foo\x08foo\r\n' +
'\r\n\r\n');
}

0 comments on commit 5d54f9a

Please sign in to comment.