2019-01-09 11:22:02 +00:00
|
|
|
Ported from:
|
|
|
|
|
|
|
|
From 59f83d689641d5030743ee4f3e453e754843e188 Mon Sep 17 00:00:00 2001
|
|
|
|
From: cjihrig <cjihrig@gmail.com>
|
|
|
|
Date: Thu, 29 Nov 2018 17:29:53 -0500
|
|
|
|
Subject: [PATCH] deps: cherry-pick http_parser_set_max_header_size
|
|
|
|
MIME-Version: 1.0
|
|
|
|
Content-Type: text/plain; charset=UTF-8
|
|
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
|
|
|
|
This commit adds http_parser_set_max_header_size() to the
|
|
|
|
http-parser for overriding the compile time maximum HTTP
|
|
|
|
header size.
|
|
|
|
|
|
|
|
Backport-PR-URL: https://github.com/nodejs/node/pull/25173
|
|
|
|
PR-URL: https://github.com/nodejs/node/pull/24811
|
|
|
|
Fixes: https://github.com/nodejs/node/issues/24692
|
|
|
|
Refs: https://github.com/nodejs/http-parser/pull/453
|
|
|
|
Reviewed-By: Anna Henningsen <anna@addaleax.net>
|
|
|
|
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
|
|
|
|
Reviewed-By: Myles Borins <myles.borins@gmail.com>
|
|
|
|
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
|
|
|
|
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
|
|
|
|
Reviewed-By: James M Snell <jasnell@gmail.com>
|
|
|
|
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
From c0c4de71f0fb7b55804a9d2110dded0493fc7c3e Mon Sep 17 00:00:00 2001
|
|
|
|
From: cjihrig <cjihrig@gmail.com>
|
|
|
|
Date: Wed, 5 Dec 2018 19:59:12 -0500
|
|
|
|
Subject: [PATCH] http: add maxHeaderSize property
|
|
|
|
|
|
|
|
This commit exposes the value of --max-http-header-size
|
|
|
|
as a property of the http module.
|
|
|
|
|
|
|
|
Backport-PR-URL: https://github.com/nodejs/node/pull/25218
|
|
|
|
PR-URL: https://github.com/nodejs/node/pull/24860
|
|
|
|
Reviewed-By: Richard Lau <riclau@uk.ibm.com>
|
|
|
|
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
|
|
|
|
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
|
|
|
|
Reviewed-By: Shelley Vohr <codebytere@gmail.com>
|
|
|
|
Reviewed-By: James M Snell <jasnell@gmail.com>
|
|
|
|
|
|
|
|
|
|
|
|
From f233b160c9b8d5126b4e4845d1661c718d14d39f Mon Sep 17 00:00:00 2001
|
|
|
|
From: cjihrig <cjihrig@gmail.com>
|
|
|
|
Date: Mon, 3 Dec 2018 12:27:46 -0500
|
|
|
|
Subject: [PATCH] cli: add --max-http-header-size flag
|
|
|
|
MIME-Version: 1.0
|
|
|
|
Content-Type: text/plain; charset=UTF-8
|
|
|
|
Content-Transfer-Encoding: 8bit
|
|
|
|
|
|
|
|
Allow the maximum size of HTTP headers to be overridden from
|
|
|
|
the command line.
|
|
|
|
|
|
|
|
Backport-PR-URL: https://github.com/nodejs/node/pull/25173
|
|
|
|
co-authored-by: Matteo Collina <hello@matteocollina.com>
|
|
|
|
PR-URL: https://github.com/nodejs/node/pull/24811
|
|
|
|
Fixes: https://github.com/nodejs/node/issues/24692
|
|
|
|
Reviewed-By: Anna Henningsen <anna@addaleax.net>
|
|
|
|
Reviewed-By: Myles Borins <myles.borins@gmail.com>
|
|
|
|
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
|
|
|
|
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
|
|
|
|
Reviewed-By: James M Snell <jasnell@gmail.com>
|
|
|
|
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
From 8a3e0c069747cb6a7e1889de92304856224fee72 Mon Sep 17 00:00:00 2001
|
|
|
|
From: Matteo Collina <hello@matteocollina.com>
|
|
|
|
Date: Fri, 14 Dec 2018 12:30:01 +0100
|
|
|
|
Subject: [PATCH] http: fix regression of binary upgrade response body
|
|
|
|
|
|
|
|
See: https://github.com/nodejs/node/issues/24958
|
|
|
|
|
|
|
|
PR-URL: https://github.com/nodejs/node/pull/25036
|
|
|
|
Reviewed-By: Myles Borins <myles.borins@gmail.com>
|
|
|
|
|
|
|
|
Index: node-v4.9.1/deps/http_parser/http_parser.c
|
|
|
|
===================================================================
|
|
|
|
--- node-v4.9.1.orig/deps/http_parser/http_parser.c
|
|
|
|
+++ node-v4.9.1/deps/http_parser/http_parser.c
|
|
|
|
@@ -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
|
|
|
|
@@ -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; \
|
|
|
|
} \
|
|
|
|
@@ -1471,7 +1473,7 @@ reexecute:
|
|
|
|
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);
|
|
|
|
@@ -2438,3 +2440,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;
|
|
|
|
+}
|
|
|
|
Index: node-v4.9.1/deps/http_parser/http_parser.h
|
|
|
|
===================================================================
|
|
|
|
--- node-v4.9.1.orig/deps/http_parser/http_parser.h
|
|
|
|
+++ node-v4.9.1/deps/http_parser/http_parser.h
|
|
|
|
@@ -427,6 +427,9 @@ void http_parser_pause(http_parser *pars
|
|
|
|
/* 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
|
|
|
|
Index: node-v4.9.1/doc/api/http.md
|
|
|
|
===================================================================
|
|
|
|
--- node-v4.9.1.orig/doc/api/http.md
|
|
|
|
+++ node-v4.9.1/doc/api/http.md
|
|
|
|
@@ -1330,6 +1330,16 @@ added: v0.5.9
|
|
|
|
Global instance of Agent which is used as the default for all http client
|
|
|
|
requests.
|
|
|
|
|
|
|
|
+## http.maxHeaderSize
|
|
|
|
+<!-- YAML
|
|
|
|
+added: REPLACEME
|
|
|
|
+-->
|
|
|
|
+
|
|
|
|
+* {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
|
|
|
|
@@ -1439,6 +1449,7 @@ There are a few special headers that sho
|
|
|
|
* Sending an Authorization header will override using the `auth` option
|
|
|
|
to compute basic authentication.
|
|
|
|
|
|
|
|
+[`--max-http-header-size`]: cli.html#cli_max_http_header_size_size
|
|
|
|
[`'checkContinue'`]: #http_event_checkcontinue
|
|
|
|
[`'listening'`]: net.html#net_event_listening
|
|
|
|
[`'response'`]: #http_event_response
|
|
|
|
Index: node-v4.9.1/lib/http.js
|
|
|
|
===================================================================
|
|
|
|
--- node-v4.9.1.orig/lib/http.js
|
|
|
|
+++ node-v4.9.1/lib/http.js
|
|
|
|
@@ -19,6 +19,8 @@ const server = require('_http_server');
|
|
|
|
exports.ServerResponse = server.ServerResponse;
|
|
|
|
exports.STATUS_CODES = server.STATUS_CODES;
|
|
|
|
|
|
|
|
+let maxHeaderSize;
|
|
|
|
+
|
|
|
|
|
|
|
|
const agent = require('_http_agent');
|
|
|
|
const Agent = exports.Agent = agent.Agent;
|
|
|
|
@@ -92,8 +94,21 @@ Client.prototype.request = function(meth
|
|
|
|
return c;
|
|
|
|
};
|
|
|
|
|
|
|
|
+
|
|
|
|
exports.Client = internalUtil.deprecate(Client, 'http.Client is deprecated.');
|
|
|
|
|
|
|
|
exports.createClient = internalUtil.deprecate(function(port, host) {
|
|
|
|
return new Client(port, host);
|
|
|
|
}, 'http.createClient is deprecated. Use http.request instead.');
|
|
|
|
+
|
|
|
|
+Object.defineProperty(module.exports, 'maxHeaderSize', {
|
|
|
|
+ configurable: true,
|
|
|
|
+ enumerable: true,
|
|
|
|
+ get() {
|
|
|
|
+ if (maxHeaderSize === undefined) {
|
|
|
|
+ maxHeaderSize = process.binding('config').maxHTTPHeaderSize;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return maxHeaderSize;
|
|
|
|
+ }
|
|
|
|
+});
|
|
|
|
Index: node-v4.9.1/test/parallel/test-http-max-header-size.js
|
|
|
|
===================================================================
|
|
|
|
--- /dev/null
|
|
|
|
+++ node-v4.9.1/test/parallel/test-http-max-header-size.js
|
|
|
|
@@ -0,0 +1,11 @@
|
|
|
|
+'use strict';
|
|
|
|
+
|
|
|
|
+require('../common');
|
|
|
|
+const assert = require('assert');
|
2019-01-09 14:07:18 +00:00
|
|
|
+const spawnSync = require('child_process').spawnSync;
|
2019-01-09 11:22:02 +00:00
|
|
|
+const http = require('http');
|
|
|
|
+
|
|
|
|
+assert.strictEqual(http.maxHeaderSize, 8 * 1024);
|
|
|
|
+const child = spawnSync(process.execPath, ['--max-http-header-size=10', '-p',
|
2019-01-09 14:07:18 +00:00
|
|
|
+ 'require("http").maxHeaderSize']);
|
2019-01-09 11:22:02 +00:00
|
|
|
+assert.strictEqual(+child.stdout.toString().trim(), 10);
|
|
|
|
Index: node-v4.9.1/doc/api/cli.md
|
|
|
|
===================================================================
|
|
|
|
--- node-v4.9.1.orig/doc/api/cli.md
|
|
|
|
+++ node-v4.9.1/doc/api/cli.md
|
|
|
|
@@ -177,6 +177,12 @@ added: v0.11.15
|
|
|
|
|
|
|
|
Specify ICU data load path. (overrides `NODE_ICU_DATA`)
|
|
|
|
|
|
|
|
+### `--max-http-header-size=size`
|
|
|
|
+<!-- YAML
|
|
|
|
+added: REPLACEME
|
|
|
|
+-->
|
|
|
|
+
|
|
|
|
+Specify the maximum size, in bytes, of HTTP headers. Defaults to 8KB.
|
|
|
|
|
|
|
|
## Environment Variables
|
|
|
|
|
|
|
|
Index: node-v4.9.1/doc/node.1
|
|
|
|
===================================================================
|
|
|
|
--- node-v4.9.1.orig/doc/node.1
|
|
|
|
+++ node-v4.9.1/doc/node.1
|
|
|
|
@@ -92,6 +92,10 @@ Preload the specified module at startup.
|
|
|
|
rules. \fImodule\fR may be either a path to a file, or a node module name.
|
|
|
|
|
|
|
|
.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.
|
|
|
|
|
|
|
|
Index: node-v4.9.1/src/node_config.cc
|
|
|
|
===================================================================
|
|
|
|
--- node-v4.9.1.orig/src/node_config.cc
|
|
|
|
+++ node-v4.9.1/src/node_config.cc
|
|
|
|
@@ -9,6 +9,7 @@ namespace node {
|
|
|
|
|
|
|
|
using v8::Context;
|
|
|
|
using v8::Local;
|
|
|
|
+using v8::Number;
|
|
|
|
using v8::Object;
|
|
|
|
using v8::Value;
|
|
|
|
using v8::ReadOnly;
|
|
|
|
@@ -25,13 +26,25 @@ using v8::ReadOnly;
|
|
|
|
True(env->isolate()), ReadOnly).FromJust(); \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
+#define READONLY_PROPERTY(obj, name, value) \
|
|
|
|
+ do { \
|
|
|
|
+ obj->DefineOwnProperty(env->context(), \
|
|
|
|
+ FIXED_ONE_BYTE_STRING(env->isolate(), name), \
|
|
|
|
+ value, ReadOnly).FromJust(); \
|
|
|
|
+ } while (0)
|
|
|
|
+
|
|
|
|
void InitConfig(Local<Object> target,
|
|
|
|
Local<Value> unused,
|
|
|
|
Local<Context> context) {
|
|
|
|
-#ifdef NODE_FIPS_MODE
|
|
|
|
Environment* env = Environment::GetCurrent(context);
|
|
|
|
+#ifdef NODE_FIPS_MODE
|
|
|
|
READONLY_BOOLEAN_PROPERTY("fipsMode");
|
|
|
|
#endif
|
|
|
|
+
|
|
|
|
+ READONLY_PROPERTY(target,
|
|
|
|
+ "maxHTTPHeaderSize",
|
|
|
|
+ Number::New(env->isolate(), max_http_header_size));
|
|
|
|
+
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace node
|
|
|
|
Index: node-v4.9.1/src/node_http_parser.cc
|
|
|
|
===================================================================
|
|
|
|
--- node-v4.9.1.orig/src/node_http_parser.cc
|
|
|
|
+++ node-v4.9.1/src/node_http_parser.cc
|
|
|
|
@@ -611,8 +611,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
|
|
|
|
@@ -627,7 +625,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);
|
|
|
|
@@ -723,6 +723,9 @@ const struct http_parser_settings Parser
|
|
|
|
nullptr // on_chunk_complete
|
|
|
|
};
|
|
|
|
|
|
|
|
+void InitMaxHttpHeaderSizeOnce() {
|
|
|
|
+ http_parser_set_max_header_size(max_http_header_size);
|
|
|
|
+}
|
|
|
|
|
|
|
|
void InitHttpParser(Local<Object> target,
|
|
|
|
Local<Value> unused,
|
|
|
|
@@ -767,6 +770,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);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace node
|
|
|
|
Index: node-v4.9.1/test/sequential/test-http-max-http-headers.js
|
|
|
|
===================================================================
|
|
|
|
--- node-v4.9.1.orig/test/sequential/test-http-max-http-headers.js
|
|
|
|
+++ node-v4.9.1/test/sequential/test-http-max-http-headers.js
|
|
|
|
@@ -1,10 +1,17 @@
|
|
|
|
'use strict';
|
|
|
|
+// Flags: --expose_internals
|
|
|
|
|
|
|
|
const assert = require('assert');
|
|
|
|
const common = require('../common');
|
|
|
|
const http = require('http');
|
|
|
|
const net = require('net');
|
|
|
|
-const MAX = 8 * 1024; // 8KB
|
|
|
|
+const MAX = +(process.argv[2] || 8 * 1024); // Command line option, or 8KB.
|
|
|
|
+
|
|
|
|
+assert(process.binding('config').maxHTTPHeaderSize,
|
|
|
|
+ 'The option should exist on process.binding(\'config\')');
|
|
|
|
+
|
|
|
|
+console.log('pid is', process.pid);
|
|
|
|
+console.log('max header size is', process.binding('config').maxHTTPHeaderSize);
|
|
|
|
|
|
|
|
// Verify that we cannot receive more than 8KB of headers.
|
|
|
|
|
|
|
|
@@ -28,19 +35,15 @@ function fillHeaders(headers, currentSiz
|
|
|
|
headers += 'a'.repeat(MAX - headers.length - 3);
|
|
|
|
// Generate valid headers
|
|
|
|
if (valid) {
|
|
|
|
- // TODO(mcollina): understand why -9 is needed instead of -1
|
|
|
|
- headers = headers.slice(0, -9);
|
|
|
|
+ // TODO(mcollina): understand why -32 is needed instead of -1
|
|
|
|
+ headers = headers.slice(0, -32);
|
|
|
|
}
|
|
|
|
return headers + '\r\n\r\n';
|
|
|
|
}
|
|
|
|
|
|
|
|
-const timeout = common.platformTimeout(10);
|
|
|
|
-
|
|
|
|
function writeHeaders(socket, headers) {
|
|
|
|
const array = [];
|
|
|
|
-
|
|
|
|
- // this is off from 1024 so that \r\n does not get split
|
|
|
|
- const chunkSize = 1000;
|
|
|
|
+ const chunkSize = 100;
|
|
|
|
let last = 0;
|
|
|
|
|
|
|
|
for (let i = 0; i < headers.length / chunkSize; i++) {
|
|
|
|
@@ -55,19 +58,25 @@ function writeHeaders(socket, headers) {
|
|
|
|
next();
|
|
|
|
|
|
|
|
function next() {
|
|
|
|
- if (socket.write(array.shift())) {
|
|
|
|
- if (array.length === 0) {
|
|
|
|
- socket.end();
|
|
|
|
- } else {
|
|
|
|
- setTimeout(next, timeout);
|
|
|
|
- }
|
|
|
|
+ if (socket.destroyed) {
|
|
|
|
+ console.log('socket was destroyed early, data left to write:',
|
|
|
|
+ array.join('').length);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const chunk = array.shift();
|
|
|
|
+
|
|
|
|
+ if (chunk) {
|
|
|
|
+ console.log('writing chunk of size', chunk.length);
|
|
|
|
+ socket.write(chunk, next);
|
|
|
|
} else {
|
|
|
|
- socket.once('drain', next);
|
|
|
|
+ socket.end();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function test1() {
|
|
|
|
+ console.log('test1');
|
|
|
|
let headers =
|
|
|
|
'HTTP/1.1 200 OK\r\n' +
|
|
|
|
'Content-Length: 0\r\n' +
|
|
|
|
@@ -82,6 +91,9 @@ function test1() {
|
|
|
|
writeHeaders(sock, headers);
|
|
|
|
sock.resume();
|
|
|
|
});
|
|
|
|
+
|
|
|
|
+ // The socket might error but that's ok
|
|
|
|
+ sock.on('error', () => {});
|
|
|
|
});
|
|
|
|
|
|
|
|
server.listen(0, common.mustCall(() => {
|
|
|
|
@@ -90,17 +102,17 @@ function test1() {
|
|
|
|
|
|
|
|
client.on('error', common.mustCall((err) => {
|
|
|
|
assert.strictEqual(err.code, 'HPE_HEADER_OVERFLOW');
|
|
|
|
- server.close();
|
|
|
|
- setImmediate(test2);
|
|
|
|
+ server.close(test2);
|
|
|
|
}));
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
|
|
|
|
const test2 = common.mustCall(() => {
|
|
|
|
+ console.log('test2');
|
|
|
|
let headers =
|
|
|
|
'GET / HTTP/1.1\r\n' +
|
|
|
|
'Host: localhost\r\n' +
|
|
|
|
- 'Agent: node\r\n' +
|
|
|
|
+ 'Agent: nod2\r\n' +
|
|
|
|
'X-CRASH: ';
|
|
|
|
|
|
|
|
// /, Host, localhost, Agent, node, X-CRASH, a...
|
|
|
|
@@ -109,7 +121,7 @@ const test2 = common.mustCall(() => {
|
|
|
|
|
|
|
|
const server = http.createServer(common.mustNotCall());
|
|
|
|
|
|
|
|
- server.on('clientError', common.mustCall((err) => {
|
|
|
|
+ server.once('clientError', common.mustCall((err) => {
|
|
|
|
assert.strictEqual(err.code, 'HPE_HEADER_OVERFLOW');
|
|
|
|
}));
|
|
|
|
|
|
|
|
@@ -121,34 +133,46 @@ const test2 = common.mustCall(() => {
|
|
|
|
});
|
|
|
|
|
|
|
|
finished(client, common.mustCall((err) => {
|
|
|
|
- server.close();
|
|
|
|
- setImmediate(test3);
|
|
|
|
+ server.close(test3);
|
|
|
|
}));
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
|
|
|
const test3 = common.mustCall(() => {
|
|
|
|
+ console.log('test3');
|
|
|
|
let headers =
|
|
|
|
'GET / HTTP/1.1\r\n' +
|
|
|
|
'Host: localhost\r\n' +
|
|
|
|
- 'Agent: node\r\n' +
|
|
|
|
+ 'Agent: nod3\r\n' +
|
|
|
|
'X-CRASH: ';
|
|
|
|
|
|
|
|
// /, Host, localhost, Agent, node, X-CRASH, a...
|
|
|
|
const currentSize = 1 + 4 + 9 + 5 + 4 + 7;
|
|
|
|
headers = fillHeaders(headers, currentSize, true);
|
|
|
|
|
|
|
|
+ console.log('writing', headers.length);
|
|
|
|
+
|
|
|
|
const server = http.createServer(common.mustCall((req, res) => {
|
|
|
|
- res.end('hello world');
|
|
|
|
- setImmediate(server.close.bind(server));
|
|
|
|
+ res.end('hello from test3 server');
|
|
|
|
+ server.close();
|
|
|
|
}));
|
|
|
|
|
|
|
|
+ server.on('clientError', (err) => {
|
|
|
|
+ console.log(err.code);
|
|
|
|
+ if (err.code === 'HPE_HEADER_OVERFLOW') {
|
|
|
|
+ console.log(err.rawPacket.toString('hex'));
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+ server.on('clientError', common.mustNotCall());
|
|
|
|
+
|
|
|
|
server.listen(0, common.mustCall(() => {
|
|
|
|
const client = net.connect(server.address().port);
|
|
|
|
client.on('connect', () => {
|
|
|
|
writeHeaders(client, headers);
|
|
|
|
client.resume();
|
|
|
|
});
|
|
|
|
+
|
|
|
|
+ client.pipe(process.stdout);
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
|
|
|
|
Index: node-v4.9.1/test/sequential/test-set-http-max-http-headers.js
|
|
|
|
===================================================================
|
|
|
|
--- /dev/null
|
|
|
|
+++ node-v4.9.1/test/sequential/test-set-http-max-http-headers.js
|
|
|
|
@@ -0,0 +1,104 @@
|
|
|
|
+'use strict';
|
|
|
|
+
|
|
|
|
+const common = require('../common');
|
|
|
|
+const assert = require('assert');
|
2019-01-09 14:07:18 +00:00
|
|
|
+const spawn = require('child_process').spawn;
|
2019-01-09 11:22:02 +00:00
|
|
|
+const path = require('path');
|
|
|
|
+const testName = path.join(__dirname, 'test-http-max-http-headers.js');
|
|
|
|
+
|
|
|
|
+const timeout = common.platformTimeout(100);
|
|
|
|
+
|
|
|
|
+const tests = [];
|
|
|
|
+
|
|
|
|
+function test(fn) {
|
|
|
|
+ tests.push(fn);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+test(function(cb) {
|
|
|
|
+ console.log('running subtest expecting failure');
|
|
|
|
+
|
|
|
|
+ // Validate that the test fails if the max header size is too small.
|
|
|
|
+ const args = ['--expose-internals',
|
|
|
|
+ '--max-http-header-size=1024',
|
|
|
|
+ testName];
|
|
|
|
+ const cp = spawn(process.execPath, args, { stdio: 'inherit' });
|
|
|
|
+
|
|
|
|
+ cp.on('close', common.mustCall((code, signal) => {
|
|
|
|
+ assert.strictEqual(code, 1);
|
|
|
|
+ assert.strictEqual(signal, null);
|
|
|
|
+ cb();
|
|
|
|
+ }));
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+test(function(cb) {
|
|
|
|
+ console.log('running subtest expecting success');
|
|
|
|
+
|
|
|
|
+ const env = Object.assign({}, process.env, {
|
|
|
|
+ NODE_DEBUG: 'http'
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ // Validate that the test fails if the max header size is too small.
|
|
|
|
+ // Validate that the test now passes if the same limit becomes large enough.
|
|
|
|
+ const args = ['--expose-internals',
|
|
|
|
+ '--max-http-header-size=1024',
|
|
|
|
+ testName,
|
|
|
|
+ '1024'];
|
|
|
|
+ const cp = spawn(process.execPath, args, {
|
|
|
|
+ env,
|
|
|
|
+ stdio: 'inherit'
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ cp.on('close', common.mustCall((code, signal) => {
|
|
|
|
+ assert.strictEqual(code, 0);
|
|
|
|
+ assert.strictEqual(signal, null);
|
|
|
|
+ cb();
|
|
|
|
+ }));
|
|
|
|
+});
|
|
|
|
+
|
|
|
|
+// Next, repeat the same checks using NODE_OPTIONS if it is supported.
|
|
|
|
+if (process.config.variables.node_without_node_options) {
|
|
|
|
+ const env = Object.assign({}, process.env, {
|
|
|
|
+ NODE_OPTIONS: '--max-http-header-size=1024'
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ test(function(cb) {
|
|
|
|
+ console.log('running subtest expecting failure');
|
|
|
|
+
|
|
|
|
+ // Validate that the test fails if the max header size is too small.
|
|
|
|
+ const args = ['--expose-internals', testName];
|
|
|
|
+ const cp = spawn(process.execPath, args, { env, stdio: 'inherit' });
|
|
|
|
+
|
|
|
|
+ cp.on('close', common.mustCall((code, signal) => {
|
|
|
|
+ assert.strictEqual(code, 1);
|
|
|
|
+ assert.strictEqual(signal, null);
|
|
|
|
+ cb();
|
|
|
|
+ }));
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ test(function(cb) {
|
|
|
|
+ // Validate that the test now passes if the same limit
|
|
|
|
+ // becomes large enough.
|
|
|
|
+ const args = ['--expose-internals', testName, '1024'];
|
|
|
|
+ const cp = spawn(process.execPath, args, { env, stdio: 'inherit' });
|
|
|
|
+
|
|
|
|
+ cp.on('close', common.mustCall((code, signal) => {
|
|
|
|
+ assert.strictEqual(code, 0);
|
|
|
|
+ assert.strictEqual(signal, null);
|
|
|
|
+ cb();
|
|
|
|
+ }));
|
|
|
|
+ });
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+function runTest() {
|
|
|
|
+ const fn = tests.shift();
|
|
|
|
+
|
|
|
|
+ if (!fn) {
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ fn(() => {
|
|
|
|
+ setTimeout(runTest, timeout);
|
|
|
|
+ });
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+runTest();
|
|
|
|
Index: node-v4.9.1/src/node_internals.h
|
|
|
|
===================================================================
|
|
|
|
--- node-v4.9.1.orig/src/node_internals.h
|
|
|
|
+++ node-v4.9.1/src/node_internals.h
|
|
|
|
@@ -30,6 +30,9 @@ struct sockaddr;
|
|
|
|
|
|
|
|
namespace node {
|
|
|
|
|
|
|
|
+// Set in node.cc by ParseArgs when --max-http-header-size is used
|
|
|
|
+extern uint64_t max_http_header_size;
|
|
|
|
+
|
|
|
|
// Forward declaration
|
|
|
|
class Environment;
|
|
|
|
|
|
|
|
Index: node-v4.9.1/src/node.cc
|
|
|
|
===================================================================
|
|
|
|
--- node-v4.9.1.orig/src/node.cc
|
|
|
|
+++ node-v4.9.1/src/node.cc
|
|
|
|
@@ -161,6 +161,8 @@ unsigned int reverted = 0;
|
|
|
|
static const char* icu_data_dir = nullptr;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
+uint64_t max_http_header_size = 8 * 1024;
|
|
|
|
+
|
|
|
|
// used by C++ modules as well
|
|
|
|
bool no_deprecation = false;
|
|
|
|
|
|
|
|
@@ -3409,6 +3411,8 @@ static void PrintHelp() {
|
|
|
|
" --trace-deprecation show stack traces on deprecations\n"
|
|
|
|
" --throw-deprecation throw an exception anytime a deprecated "
|
|
|
|
"function is used\n"
|
|
|
|
+ " --max-http-header-size Specify the maximum size of HTTP\n"
|
|
|
|
+ " headers in bytes. Defaults to 8KB.\n"
|
|
|
|
" --trace-sync-io show stack trace when use of sync IO\n"
|
|
|
|
" is detected after the first tick\n"
|
|
|
|
" --track-heap-objects track heap object allocations for heap "
|
|
|
|
@@ -3558,6 +3562,8 @@ static void ParseArgs(int* argc,
|
|
|
|
short_circuit = true;
|
|
|
|
} else if (strcmp(arg, "--zero-fill-buffers") == 0) {
|
|
|
|
zero_fill_all_buffers = true;
|
|
|
|
+ } else if (strncmp(arg, "--max-http-header-size=", 23) == 0) {
|
|
|
|
+ max_http_header_size = atoi(arg + 23);
|
|
|
|
} else if (strcmp(arg, "--v8-options") == 0) {
|
|
|
|
new_v8_argv[new_v8_argc] = "--help";
|
|
|
|
new_v8_argc += 1;
|
2019-01-09 14:07:18 +00:00
|
|
|
Index: node-v4.9.1/test/common.js
|
|
|
|
===================================================================
|
|
|
|
--- node-v4.9.1.orig/test/common.js
|
|
|
|
+++ node-v4.9.1/test/common.js
|
|
|
|
@@ -410,6 +410,26 @@ exports.mustCall = function(fn, expected
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
+exports.getCallSite = function getCallSite(top) {
|
|
|
|
+ const originalStackFormatter = Error.prepareStackTrace;
|
|
|
|
+ Error.prepareStackTrace = (err, stack) =>
|
|
|
|
+ `${stack[0].getFileName()}:${stack[0].getLineNumber()}`;
|
|
|
|
+ const err = new Error();
|
|
|
|
+ Error.captureStackTrace(err, top);
|
|
|
|
+ // with the V8 Error API, the stack is not formatted until it is accessed
|
|
|
|
+ err.stack;
|
|
|
|
+ Error.prepareStackTrace = originalStackFormatter;
|
|
|
|
+ return err.stack;
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+exports.mustNotCall = function(msg) {
|
|
|
|
+ const callSite = exports.getCallSite(exports.mustNotCall);
|
|
|
|
+ return function mustNotCall() {
|
|
|
|
+ assert.fail(
|
|
|
|
+ `${msg || 'function should not have been called'} at ${callSite}`);
|
|
|
|
+ };
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
var etcServicesFileName = path.join('/etc', 'services');
|
|
|
|
if (exports.isWindows) {
|
|
|
|
etcServicesFileName = path.join(process.env.SystemRoot, 'System32', 'drivers',
|