Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 6257408

Browse filesBrowse files
addaleaxtargos
authored andcommitted
http: make maximum header size configurable per-stream or per-server
Make `maxHeaderSize` a.k.a. `--max-header-size` configurable now that the legacy parser is gone (which only supported a single global value). Refs: #30567 PR-URL: #30570 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: David Carlier <devnexen@gmail.com> Reviewed-By: Colin Ihrig <cjihrig@gmail.com> Reviewed-By: Sam Roberts <vieuxtech@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Denys Otrishko <shishugi@gmail.com>
1 parent e968e26 commit 6257408
Copy full SHA for 6257408

File tree

Expand file treeCollapse file tree

7 files changed

+135
-10
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

7 files changed

+135
-10
lines changed
Open diff view settings
Collapse file

‎doc/api/http.md‎

Copy file name to clipboardExpand all lines: doc/api/http.md
+17Lines changed: 17 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -2047,6 +2047,9 @@ Found'`.
20472047
<!-- YAML
20482048
added: v0.1.13
20492049
changes:
2050+
- version: REPLACEME
2051+
pr-url: https://github.com/nodejs/node/pull/30570
2052+
description: The `maxHeaderSize` option is supported now.
20502053
- version: v9.6.0, v8.12.0
20512054
pr-url: https://github.com/nodejs/node/pull/15752
20522055
description: The `options` argument is supported now.
@@ -2059,6 +2062,10 @@ changes:
20592062
* `ServerResponse` {http.ServerResponse} Specifies the `ServerResponse` class
20602063
to be used. Useful for extending the original `ServerResponse`. **Default:**
20612064
`ServerResponse`.
2065+
* `maxHeaderSize` {number} Optionally overrides the value of
2066+
[`--max-http-header-size`][] for requests received by this server, i.e.
2067+
the maximum length of request headers in bytes.
2068+
**Default:** 8192 (8KB).
20622069
* `requestListener` {Function}
20632070

20642071
* Returns: {http.Server}
@@ -2156,11 +2163,17 @@ added: v11.6.0
21562163
Read-only property specifying the maximum allowed size of HTTP headers in bytes.
21572164
Defaults to 8KB. Configurable using the [`--max-http-header-size`][] CLI option.
21582165

2166+
This can be overridden for servers and client requests by passing the
2167+
`maxHeaderSize` option.
2168+
21592169
## http.request(options\[, callback\])
21602170
## http.request(url\[, options\]\[, callback\])
21612171
<!-- YAML
21622172
added: v0.3.6
21632173
changes:
2174+
- version: REPLACEME
2175+
pr-url: https://github.com/nodejs/node/pull/30570
2176+
description: The `maxHeaderSize` option is supported now.
21642177
- version: v10.9.0
21652178
pr-url: https://github.com/nodejs/node/pull/21616
21662179
description: The `url` parameter can now be passed along with a separate
@@ -2196,6 +2209,10 @@ changes:
21962209
`hostname` will be used if both `host` and `hostname` are specified.
21972210
* `localAddress` {string} Local interface to bind for network connections.
21982211
* `lookup` {Function} Custom lookup function. **Default:** [`dns.lookup()`][].
2212+
* `maxHeaderSize` {number} Optionally overrides the value of
2213+
[`--max-http-header-size`][] for requests received from the server, i.e.
2214+
the maximum length of response headers in bytes.
2215+
**Default:** 8192 (8KB).
21992216
* `method` {string} A string specifying the HTTP request method. **Default:**
22002217
`'GET'`.
22012218
* `path` {string} Request path. Should include query string if any.
Collapse file

‎lib/_http_client.js‎

Copy file name to clipboardExpand all lines: lib/_http_client.js
+8-1Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ const {
5555
ERR_INVALID_PROTOCOL,
5656
ERR_UNESCAPED_CHARACTERS
5757
} = codes;
58+
const { validateInteger } = require('internal/validators');
5859
const { getTimerDuration } = require('internal/timers');
5960
const {
6061
DTRACE_HTTP_CLIENT_REQUEST,
@@ -179,6 +180,11 @@ function ClientRequest(input, options, cb) {
179180
method = this.method = 'GET';
180181
}
181182

183+
const maxHeaderSize = options.maxHeaderSize;
184+
if (maxHeaderSize !== undefined)
185+
validateInteger(maxHeaderSize, 'maxHeaderSize', 0);
186+
this.maxHeaderSize = maxHeaderSize;
187+
182188
this.path = options.path || '/';
183189
if (cb) {
184190
this.once('response', cb);
@@ -662,7 +668,8 @@ function tickOnSocket(req, socket) {
662668
const parser = parsers.alloc();
663669
req.socket = socket;
664670
parser.initialize(HTTPParser.RESPONSE,
665-
new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req));
671+
new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req),
672+
req.maxHeaderSize || 0);
666673
parser.socket = socket;
667674
parser.outgoing = req;
668675
req.parser = parser;
Collapse file

‎lib/_http_server.js‎

Copy file name to clipboardExpand all lines: lib/_http_server.js
+8-1Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ const {
5858
ERR_INVALID_ARG_TYPE,
5959
ERR_INVALID_CHAR
6060
} = require('internal/errors').codes;
61+
const { validateInteger } = require('internal/validators');
6162
const Buffer = require('buffer').Buffer;
6263
const {
6364
DTRACE_HTTP_SERVER_REQUEST,
@@ -322,6 +323,11 @@ function Server(options, requestListener) {
322323
this[kIncomingMessage] = options.IncomingMessage || IncomingMessage;
323324
this[kServerResponse] = options.ServerResponse || ServerResponse;
324325

326+
const maxHeaderSize = options.maxHeaderSize;
327+
if (maxHeaderSize !== undefined)
328+
validateInteger(maxHeaderSize, 'maxHeaderSize', 0);
329+
this.maxHeaderSize = maxHeaderSize;
330+
325331
net.Server.call(this, { allowHalfOpen: true });
326332

327333
if (requestListener) {
@@ -379,7 +385,8 @@ function connectionListenerInternal(server, socket) {
379385
// https://github.com/nodejs/node/pull/21313
380386
parser.initialize(
381387
HTTPParser.REQUEST,
382-
new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket)
388+
new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket),
389+
server.maxHeaderSize || 0
383390
);
384391
parser.socket = socket;
385392

Collapse file

‎src/node_http_parser.cc‎

Copy file name to clipboardExpand all lines: src/node_http_parser.cc
+15-3Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ using v8::Int32;
6262
using v8::Integer;
6363
using v8::Local;
6464
using v8::MaybeLocal;
65+
using v8::Number;
6566
using v8::Object;
6667
using v8::String;
6768
using v8::Uint32;
@@ -486,8 +487,17 @@ class Parser : public AsyncWrap, public StreamListener {
486487
static void Initialize(const FunctionCallbackInfo<Value>& args) {
487488
Environment* env = Environment::GetCurrent(args);
488489

490+
uint64_t max_http_header_size = 0;
491+
489492
CHECK(args[0]->IsInt32());
490493
CHECK(args[1]->IsObject());
494+
if (args.Length() > 2) {
495+
CHECK(args[2]->IsNumber());
496+
max_http_header_size = args[2].As<Number>()->Value();
497+
}
498+
if (max_http_header_size == 0) {
499+
max_http_header_size = env->options()->max_http_header_size;
500+
}
491501

492502
llhttp_type_t type =
493503
static_cast<llhttp_type_t>(args[0].As<Int32>()->Value());
@@ -505,7 +515,7 @@ class Parser : public AsyncWrap, public StreamListener {
505515

506516
parser->set_provider_type(provider);
507517
parser->AsyncReset(args[1].As<Object>());
508-
parser->Init(type);
518+
parser->Init(type, max_http_header_size);
509519
}
510520

511521
template <bool should_pause>
@@ -752,7 +762,7 @@ class Parser : public AsyncWrap, public StreamListener {
752762
}
753763

754764

755-
void Init(llhttp_type_t type) {
765+
void Init(llhttp_type_t type, uint64_t max_http_header_size) {
756766
llhttp_init(&parser_, type, &settings);
757767
header_nread_ = 0;
758768
url_.Reset();
@@ -761,12 +771,13 @@ class Parser : public AsyncWrap, public StreamListener {
761771
num_values_ = 0;
762772
have_flushed_ = false;
763773
got_exception_ = false;
774+
max_http_header_size_ = max_http_header_size;
764775
}
765776

766777

767778
int TrackHeader(size_t len) {
768779
header_nread_ += len;
769-
if (header_nread_ >= per_process::cli_options->max_http_header_size) {
780+
if (header_nread_ >= max_http_header_size_) {
770781
llhttp_set_error_reason(&parser_, "HPE_HEADER_OVERFLOW:Header overflow");
771782
return HPE_USER;
772783
}
@@ -801,6 +812,7 @@ class Parser : public AsyncWrap, public StreamListener {
801812
unsigned int execute_depth_ = 0;
802813
bool pending_pause_ = false;
803814
uint64_t header_nread_ = 0;
815+
uint64_t max_http_header_size_;
804816

805817
// These are helper functions for filling `http_parser_settings`, which turn
806818
// a member function of Parser into a C-style HTTP parser callback.
Collapse file

‎src/node_options.cc‎

Copy file name to clipboardExpand all lines: src/node_options.cc
+4-4Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
436436
"profile generated with --heap-prof. (default: 512 * 1024)",
437437
&EnvironmentOptions::heap_prof_interval);
438438
#endif // HAVE_INSPECTOR
439+
AddOption("--max-http-header-size",
440+
"set the maximum size of HTTP headers (default: 8192 (8KB))",
441+
&EnvironmentOptions::max_http_header_size,
442+
kAllowedInEnvironment);
439443
AddOption("--redirect-warnings",
440444
"write warnings to file instead of stderr",
441445
&EnvironmentOptions::redirect_warnings,
@@ -628,10 +632,6 @@ PerProcessOptionsParser::PerProcessOptionsParser(
628632
kAllowedInEnvironment);
629633
AddAlias("--trace-events-enabled", {
630634
"--trace-event-categories", "v8,node,node.async_hooks" });
631-
AddOption("--max-http-header-size",
632-
"set the maximum size of HTTP headers (default: 8KB)",
633-
&PerProcessOptions::max_http_header_size,
634-
kAllowedInEnvironment);
635635
AddOption("--v8-pool-size",
636636
"set V8's thread pool size",
637637
&PerProcessOptions::v8_thread_pool_size,
Collapse file

‎src/node_options.h‎

Copy file name to clipboardExpand all lines: src/node_options.h
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ class EnvironmentOptions : public Options {
115115
bool expose_internals = false;
116116
bool frozen_intrinsics = false;
117117
std::string heap_snapshot_signal;
118+
uint64_t max_http_header_size = 8 * 1024;
118119
bool no_deprecation = false;
119120
bool no_force_async_hooks_checks = false;
120121
bool no_warnings = false;
@@ -200,7 +201,6 @@ class PerProcessOptions : public Options {
200201
std::string title;
201202
std::string trace_event_categories;
202203
std::string trace_event_file_pattern = "node_trace.${rotation}.log";
203-
uint64_t max_http_header_size = 8 * 1024;
204204
int64_t v8_thread_pool_size = 4;
205205
bool zero_fill_all_buffers = false;
206206
bool debug_arraybuffer_allocations = false;
Collapse file
+82Lines 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 larger headers than what would otherwise be allowed.
10+
{
11+
const { clientSide, serverSide } = MakeDuplexPair();
12+
13+
const req = http.request({
14+
createConnection: common.mustCall(() => clientSide),
15+
maxHeaderSize: http.maxHeaderSize * 4
16+
}, common.mustCall((res) => {
17+
assert.strictEqual(res.headers.hello, 'A'.repeat(http.maxHeaderSize * 3));
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: ' + 'A'.repeat(http.maxHeaderSize * 3) + '\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: ' + 'A'.repeat(http.maxHeaderSize * 3) + '\r\n' +
43+
'Content-Length: 0\r\n' +
44+
'\r\n\r\n');
45+
}
46+
47+
// Test 3: The client sends larger headers than what would otherwise be allowed.
48+
{
49+
const testData = 'Hello, World!\n';
50+
const server = http.createServer(
51+
{ maxHeaderSize: http.maxHeaderSize * 4 },
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: ' + 'A'.repeat(http.maxHeaderSize * 3) + '\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: ' + 'A'.repeat(http.maxHeaderSize * 3) + '\r\n' +
81+
'\r\n\r\n');
82+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.