Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v8.15.0 proposal #25177

Merged
merged 7 commits into from Dec 26, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Expand Up @@ -27,7 +27,8 @@ release.
</tr>
<tr>
<td valign="top">
<b><a href="doc/changelogs/CHANGELOG_V8.md#8.14.1">8.14.1</a></b><br/>
<b><a href="doc/changelogs/CHANGELOG_V8.md#8.15.0">8.15.0</a></b><br/>
<a href="doc/changelogs/CHANGELOG_V8.md#8.14.1">8.14.1</a><br/>
<a href="doc/changelogs/CHANGELOG_V8.md#8.14.0">8.14.0</a><br/>
<a href="doc/changelogs/CHANGELOG_V8.md#8.13.0">8.13.0</a><br/>
<a href="doc/changelogs/CHANGELOG_V8.md#8.12.0">8.12.0</a><br/>
Expand Down
15 changes: 11 additions & 4 deletions deps/http_parser/http_parser.c
Expand Up @@ -25,6 +25,8 @@
#include <string.h>
#include <limits.h>

static uint32_t max_header_size = HTTP_MAX_HEADER_SIZE;

#ifndef ULLONG_MAX
# define ULLONG_MAX ((uint64_t) -1) /* 2^64-1 */
#endif
Expand Down Expand Up @@ -137,20 +139,20 @@ do { \
} while (0)

/* Don't allow the total size of the HTTP headers (including the status
* line) to exceed HTTP_MAX_HEADER_SIZE. This check is here to protect
* line) to exceed max_header_size. This check is here to protect
* embedders against denial-of-service attacks where the attacker feeds
* us a never-ending header that the embedder keeps buffering.
*
* This check is arguably the responsibility of embedders but we're doing
* it on the embedder's behalf because most won't bother and this way we
* make the web a little safer. HTTP_MAX_HEADER_SIZE is still far bigger
* make the web a little safer. max_header_size is still far bigger
* than any reasonable request or response so this should never affect
* day-to-day operation.
*/
#define COUNT_HEADER_SIZE(V) \
do { \
parser->nread += (V); \
if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \
if (UNLIKELY(parser->nread > max_header_size)) { \
SET_ERRNO(HPE_HEADER_OVERFLOW); \
goto error; \
} \
Expand Down Expand Up @@ -1471,7 +1473,7 @@ size_t http_parser_execute (http_parser *parser,
const char* p_lf;
size_t limit = data + len - p;

limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
limit = MIN(limit, max_header_size);

p_cr = (const char*) memchr(p, CR, limit);
p_lf = (const char*) memchr(p, LF, limit);
Expand Down Expand Up @@ -2437,3 +2439,8 @@ http_parser_version(void) {
HTTP_PARSER_VERSION_MINOR * 0x00100 |
HTTP_PARSER_VERSION_PATCH * 0x00001;
}

void
http_parser_set_max_header_size(uint32_t size) {
max_header_size = size;
}
3 changes: 3 additions & 0 deletions deps/http_parser/http_parser.h
Expand Up @@ -427,6 +427,9 @@ void http_parser_pause(http_parser *parser, int paused);
/* Checks if this is the final chunk of the body. */
int http_body_is_final(const http_parser *parser);

/* Change the maximum header size provided at compile time. */
void http_parser_set_max_header_size(uint32_t size);

#ifdef __cplusplus
}
#endif
Expand Down
8 changes: 8 additions & 0 deletions doc/api/cli.md
Expand Up @@ -405,6 +405,13 @@ Indicate the end of node options. Pass the rest of the arguments to the script.
If no script filename or eval/print script is supplied prior to this, then
the next argument will be used as a script filename.

### `--max-http-header-size=size`
<!-- YAML
added: v8.15.0
-->

Specify the maximum size, in bytes, of HTTP headers. Defaults to 8KB.

## Environment Variables

### `NODE_DEBUG=module[,…]`
Expand Down Expand Up @@ -472,6 +479,7 @@ Node.js options that are allowed are:
- `--inspect-brk`
- `--inspect-port`
- `--inspect`
- `--max-http-header-size`
- `--no-deprecation`
- `--no-warnings`
- `--openssl-config`
Expand Down
11 changes: 11 additions & 0 deletions doc/api/http.md
Expand Up @@ -1805,6 +1805,16 @@ added: v0.5.9
Global instance of `Agent` which is used as the default for all HTTP client
requests.

## http.maxHeaderSize
<!-- YAML
added: v8.15.0
-->

* {number}

Read-only property specifying the maximum allowed size of HTTP headers in bytes.
Defaults to 8KB. Configurable using the [`--max-http-header-size`][] CLI option.

## http.request(options[, callback])
<!-- YAML
added: v0.3.6
Expand Down Expand Up @@ -1982,6 +1992,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.

[`--max-http-header-size`]: cli.html#cli_max_http_header_size_size
[`'checkContinue'`]: #http_event_checkcontinue
[`'request'`]: #http_event_request
[`'response'`]: #http_event_response
Expand Down
25 changes: 25 additions & 0 deletions doc/changelogs/CHANGELOG_V8.md
Expand Up @@ -10,6 +10,7 @@
</tr>
<tr>
<td valign="top">
<a href="#8.15.0">8.15.0</a><br/>
<a href="#8.14.1">8.14.1</a><br/>
<a href="#8.14.0">8.14.0</a><br/>
<a href="#8.13.0">8.13.0</a><br/>
Expand Down Expand Up @@ -60,6 +61,30 @@
[Node.js Long Term Support Plan](https://github.com/nodejs/LTS) and
will be supported actively until April 2019 and maintained until December 2019.

<a id="8.15.0"></a>
## 2018-12-26, Version 8.15.0 'Carbon' (LTS), @MylesBorins

The 8.14.0 security release introduced some unexpected breakages on the 8.x release line.
This is a special release to fix a regression in the HTTP binary upgrade response body and add
a missing CLI flag to adjust the max header size of the http parser.

### Notable Changes

* **cli**:
- add --max-http-header-size flag (cjihrig) [#24811](https://github.com/nodejs/node/pull/24811)
* **http**:
- add maxHeaderSize property (cjihrig) [#24860](https://github.com/nodejs/node/pull/24860)

### Commits

* [[`693e362175`](https://github.com/nodejs/node/commit/693e362175)] - **(SEMVER-MINOR)** **cli**: add --max-http-header-size flag (cjihrig) [#24811](https://github.com/nodejs/node/pull/24811)
* [[`4fb5a1be2f`](https://github.com/nodejs/node/commit/4fb5a1be2f)] - **(SEMVER-MINOR)** **deps**: cherry-pick http\_parser\_set\_max\_header\_size (cjihrig) [#24811](https://github.com/nodejs/node/pull/24811)
* [[`446f8b54e5`](https://github.com/nodejs/node/commit/446f8b54e5)] - **(SEMVER-MINOR)** **http**: add maxHeaderSize property (cjihrig) [#24860](https://github.com/nodejs/node/pull/24860)
* [[`215ecfe4de`](https://github.com/nodejs/node/commit/215ecfe4de)] - **http**: fix regression of binary upgrade response body (Matteo Collina) [#25037](https://github.com/nodejs/node/pull/25037)
* [[`e1fbc26c6a`](https://github.com/nodejs/node/commit/e1fbc26c6a)] - **test**: move test-benchmark-path to sequential (Rich Trott) [#21393](https://github.com/nodejs/node/pull/21393)
* [[`aef71c05a2`](https://github.com/nodejs/node/commit/aef71c05a2)] - **test**: mark test-http2-settings-flood as flaky on Windows (Rich Trott) [#25048](https://github.com/nodejs/node/pull/25048)


<a id="8.14.1"></a>
## 2018-12-18, Version 8.14.1 'Carbon' (LTS), @MylesBorins prepared by @BethGriggs

Expand Down
4 changes: 4 additions & 0 deletions doc/node.1
Expand Up @@ -110,6 +110,10 @@ Activate inspector on host:port and break at start of user script.
.BR \-\-inspect-port \fI=[host:]port\fR
Set the host:port to be used when the inspector is activated.

.TP
.BR \-\-max\-http\-header-size \fI=size\fR
Specify the maximum size of HTTP headers in bytes. Defaults to 8KB.

.TP
.BR \-\-no\-deprecation
Silence deprecation warnings.
Expand Down
13 changes: 13 additions & 0 deletions lib/http.js
Expand Up @@ -27,6 +27,7 @@ const common = require('_http_common');
const incoming = require('_http_incoming');
const outgoing = require('_http_outgoing');
const server = require('_http_server');
let maxHeaderSize;

const { Server } = server;

Expand Down Expand Up @@ -59,3 +60,15 @@ module.exports = {
get,
request
};

Object.defineProperty(module.exports, 'maxHeaderSize', {
configurable: true,
enumerable: true,
get() {
if (maxHeaderSize === undefined) {
maxHeaderSize = process.binding('config').maxHTTPHeaderSize;
}

return maxHeaderSize;
}
});
7 changes: 7 additions & 0 deletions src/node.cc
Expand Up @@ -201,6 +201,8 @@ unsigned int reverted = 0;
std::string icu_data_dir; // NOLINT(runtime/string)
#endif

uint64_t max_http_header_size = 8 * 1024;

// used by C++ modules as well
bool no_deprecation = false;

Expand Down Expand Up @@ -3120,6 +3122,8 @@ static void PrintHelp() {
" set host:port for inspector\n"
#endif
" --no-deprecation silence deprecation warnings\n"
" --max-http-header-size Specify the maximum size of HTTP\n"
" headers in bytes. Defaults to 8KB.\n"
" --trace-deprecation show stack traces on deprecations\n"
" --throw-deprecation throw an exception on deprecations\n"
" --pending-deprecation emit pending deprecation warnings\n"
Expand Down Expand Up @@ -3264,6 +3268,7 @@ static void CheckIfAllowedInEnv(const char* exe, bool is_env,
"--expose-http2",
"--experimental-modules",
"--loader",
"--max-http-header-size",
"--trace-warnings",
"--redirect-warnings",
"--trace-sync-io",
Expand Down Expand Up @@ -3468,6 +3473,8 @@ static void ParseArgs(int* argc,
new_v8_argc += 1;
} else if (strncmp(arg, "--v8-pool-size=", 15) == 0) {
v8_thread_pool_size = atoi(arg + 15);
} else if (strncmp(arg, "--max-http-header-size=", 23) == 0) {
max_http_header_size = atoi(arg + 23);
#if HAVE_OPENSSL
} else if (strncmp(arg, "--tls-cipher-list=", 18) == 0) {
default_cipher_list = arg + 18;
Expand Down
4 changes: 4 additions & 0 deletions src/node_config.cc
Expand Up @@ -88,6 +88,10 @@ static void InitConfig(Local<Object> target,
if (env->abort_on_uncaught_exception())
READONLY_BOOLEAN_PROPERTY("shouldAbortOnUncaughtException");

READONLY_PROPERTY(target,
"maxHTTPHeaderSize",
Number::New(env->isolate(), max_http_header_size));

READONLY_PROPERTY(target,
"bits",
Number::New(env->isolate(), 8 * sizeof(intptr_t)));
Expand Down
11 changes: 8 additions & 3 deletions src/node_http_parser.cc
Expand Up @@ -621,8 +621,6 @@ class Parser : public AsyncWrap {
size_t nparsed =
http_parser_execute(&parser_, &settings, data, len);

enum http_errno err = HTTP_PARSER_ERRNO(&parser_);

Save();

// Unassign the 'buffer_' variable
Expand All @@ -637,7 +635,9 @@ class Parser : public AsyncWrap {
Local<Integer> nparsed_obj = Integer::New(env()->isolate(), nparsed);
// If there was a parse error in one of the callbacks
// TODO(bnoordhuis) What if there is an error on EOF?
if ((!parser_.upgrade && nparsed != len) || err != HPE_OK) {
if (!parser_.upgrade && nparsed != len) {
enum http_errno err = HTTP_PARSER_ERRNO(&parser_);

Local<Value> e = Exception::Error(env()->parse_error_string());
Local<Object> obj = e->ToObject(env()->isolate());
obj->Set(env()->bytes_parsed_string(), nparsed_obj);
Expand Down Expand Up @@ -755,6 +755,9 @@ const struct http_parser_settings Parser::settings = {
nullptr // on_chunk_complete
};

void InitMaxHttpHeaderSizeOnce() {
http_parser_set_max_header_size(max_http_header_size);
}

void InitHttpParser(Local<Object> target,
Local<Value> unused,
Expand Down Expand Up @@ -801,6 +804,8 @@ void InitHttpParser(Local<Object> target,

target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "HTTPParser"),
t->GetFunction());
static uv_once_t init_once = UV_ONCE_INIT;
uv_once(&init_once, InitMaxHttpHeaderSizeOnce);
}

} // anonymous namespace
Expand Down
3 changes: 3 additions & 0 deletions src/node_internals.h
Expand Up @@ -176,6 +176,9 @@ extern std::string config_warning_file; // NOLINT(runtime/string)
// NODE_PENDING_DEPRECATION is used
extern bool config_pending_deprecation;

// Set in node.cc by ParseArgs when --max-http-header-size is used
extern uint64_t max_http_header_size;

// Tells whether it is safe to call v8::Isolate::GetCurrent().
extern bool v8_initialized;

Expand Down
6 changes: 3 additions & 3 deletions src/node_version.h
Expand Up @@ -23,13 +23,13 @@
#define SRC_NODE_VERSION_H_

#define NODE_MAJOR_VERSION 8
#define NODE_MINOR_VERSION 14
#define NODE_PATCH_VERSION 2
#define NODE_MINOR_VERSION 15
#define NODE_PATCH_VERSION 0

#define NODE_VERSION_IS_LTS 1
#define NODE_VERSION_LTS_CODENAME "Carbon"

#define NODE_VERSION_IS_RELEASE 0
#define NODE_VERSION_IS_RELEASE 1

#ifndef NODE_STRINGIFY
#define NODE_STRINGIFY(n) NODE_STRINGIFY_HELPER(n)
Expand Down
11 changes: 11 additions & 0 deletions test/parallel/test-http-max-header-size.js
@@ -0,0 +1,11 @@
'use strict';

require('../common');
const assert = require('assert');
const { spawnSync } = require('child_process');
const http = require('http');

assert.strictEqual(http.maxHeaderSize, 8 * 1024);
const child = spawnSync(process.execPath, ['--max-http-header-size=10', '-p',
'http.maxHeaderSize']);
assert.strictEqual(+child.stdout.toString().trim(), 10);
2 changes: 2 additions & 0 deletions test/sequential/sequential.status
Expand Up @@ -13,6 +13,8 @@ test-inspector-bindings : PASS, FLAKY
test-inspector-debug-end : PASS, FLAKY
test-inspector-async-hook-setup-at-signal: PASS, FLAKY
test-inspector-stop-profile-after-done: PASS, FLAKY
# https://github.com/nodejs/node/issues/25043
test-http2-settings-flood: PASS,FLAKY

[$system==linux]

Expand Down