| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent e968e26 commit 6257408
7 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -2047,6 +2047,9 @@ Found'`. | |||
| 2047 | 2047 | <!-- YAML | |
| 2048 | 2048 | added: v0.1.13 | |
| 2049 | 2049 | changes: | |
| 2050 | + - version: REPLACEME | ||
| 2051 | + pr-url: https://github.com/nodejs/node/pull/30570 | ||
| 2052 | + description: The `maxHeaderSize` option is supported now. | ||
| 2050 | 2053 | - version: v9.6.0, v8.12.0 | |
| 2051 | 2054 | pr-url: https://github.com/nodejs/node/pull/15752 | |
| 2052 | 2055 | description: The `options` argument is supported now. | |
@@ -2059,6 +2062,10 @@ changes: | |||
| 2059 | 2062 | * `ServerResponse` {http.ServerResponse} Specifies the `ServerResponse` class | |
| 2060 | 2063 | to be used. Useful for extending the original `ServerResponse`. **Default:** | |
| 2061 | 2064 | `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). | ||
| 2062 | 2069 | * `requestListener` {Function} | |
| 2063 | 2070 | ||
| 2064 | 2071 | * Returns: {http.Server} | |
@@ -2156,11 +2163,17 @@ added: v11.6.0 | |||
| 2156 | 2163 | Read-only property specifying the maximum allowed size of HTTP headers in bytes. | |
| 2157 | 2164 | Defaults to 8KB. Configurable using the [`--max-http-header-size`][] CLI option. | |
| 2158 | 2165 | ||
| 2166 | + This can be overridden for servers and client requests by passing the | ||
| 2167 | + `maxHeaderSize` option. | ||
| 2168 | + | ||
| 2159 | 2169 | ## http.request(options\[, callback\]) | |
| 2160 | 2170 | ## http.request(url\[, options\]\[, callback\]) | |
| 2161 | 2171 | <!-- YAML | |
| 2162 | 2172 | added: v0.3.6 | |
| 2163 | 2173 | changes: | |
| 2174 | + - version: REPLACEME | ||
| 2175 | + pr-url: https://github.com/nodejs/node/pull/30570 | ||
| 2176 | + description: The `maxHeaderSize` option is supported now. | ||
| 2164 | 2177 | - version: v10.9.0 | |
| 2165 | 2178 | pr-url: https://github.com/nodejs/node/pull/21616 | |
| 2166 | 2179 | description: The `url` parameter can now be passed along with a separate | |
@@ -2196,6 +2209,10 @@ changes: | |||
| 2196 | 2209 | `hostname` will be used if both `host` and `hostname` are specified. | |
| 2197 | 2210 | * `localAddress` {string} Local interface to bind for network connections. | |
| 2198 | 2211 | * `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). | ||
| 2199 | 2216 | * `method` {string} A string specifying the HTTP request method. **Default:** | |
| 2200 | 2217 | `'GET'`. | |
| 2201 | 2218 | * `path` {string} Request path. Should include query string if any. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -55,6 +55,7 @@ const { | |||
| 55 | 55 | ERR_INVALID_PROTOCOL, | |
| 56 | 56 | ERR_UNESCAPED_CHARACTERS | |
| 57 | 57 | } = codes; | |
| 58 | + const { validateInteger } = require('internal/validators'); | ||
| 58 | 59 | const { getTimerDuration } = require('internal/timers'); | |
| 59 | 60 | const { | |
| 60 | 61 | DTRACE_HTTP_CLIENT_REQUEST, | |
@@ -179,6 +180,11 @@ function ClientRequest(input, options, cb) { | |||
| 179 | 180 | method = this.method = 'GET'; | |
| 180 | 181 | } | |
| 181 | 182 | ||
| 183 | + const maxHeaderSize = options.maxHeaderSize; | ||
| 184 | + if (maxHeaderSize !== undefined) | ||
| 185 | + validateInteger(maxHeaderSize, 'maxHeaderSize', 0); | ||
| 186 | + this.maxHeaderSize = maxHeaderSize; | ||
| 187 | + | ||
| 182 | 188 | this.path = options.path || '/'; | |
| 183 | 189 | if (cb) { | |
| 184 | 190 | this.once('response', cb); | |
@@ -662,7 +668,8 @@ function tickOnSocket(req, socket) { | |||
| 662 | 668 | const parser = parsers.alloc(); | |
| 663 | 669 | req.socket = socket; | |
| 664 | 670 | parser.initialize(HTTPParser.RESPONSE, | |
| 665 | - new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req)); | ||
| 671 | + new HTTPClientAsyncResource('HTTPINCOMINGMESSAGE', req), | ||
| 672 | + req.maxHeaderSize || 0); | ||
| 666 | 673 | parser.socket = socket; | |
| 667 | 674 | parser.outgoing = req; | |
| 668 | 675 | req.parser = parser; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -58,6 +58,7 @@ const { | |||
| 58 | 58 | ERR_INVALID_ARG_TYPE, | |
| 59 | 59 | ERR_INVALID_CHAR | |
| 60 | 60 | } = require('internal/errors').codes; | |
| 61 | + const { validateInteger } = require('internal/validators'); | ||
| 61 | 62 | const Buffer = require('buffer').Buffer; | |
| 62 | 63 | const { | |
| 63 | 64 | DTRACE_HTTP_SERVER_REQUEST, | |
@@ -322,6 +323,11 @@ function Server(options, requestListener) { | |||
| 322 | 323 | this[kIncomingMessage] = options.IncomingMessage || IncomingMessage; | |
| 323 | 324 | this[kServerResponse] = options.ServerResponse || ServerResponse; | |
| 324 | 325 | ||
| 326 | + const maxHeaderSize = options.maxHeaderSize; | ||
| 327 | + if (maxHeaderSize !== undefined) | ||
| 328 | + validateInteger(maxHeaderSize, 'maxHeaderSize', 0); | ||
| 329 | + this.maxHeaderSize = maxHeaderSize; | ||
| 330 | + | ||
| 325 | 331 | net.Server.call(this, { allowHalfOpen: true }); | |
| 326 | 332 | ||
| 327 | 333 | if (requestListener) { | |
@@ -379,7 +385,8 @@ function connectionListenerInternal(server, socket) { | |||
| 379 | 385 | // https://github.com/nodejs/node/pull/21313 | |
| 380 | 386 | parser.initialize( | |
| 381 | 387 | HTTPParser.REQUEST, | |
| 382 | - new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket) | ||
| 388 | + new HTTPServerAsyncResource('HTTPINCOMINGMESSAGE', socket), | ||
| 389 | + server.maxHeaderSize || 0 | ||
| 383 | 390 | ); | |
| 384 | 391 | parser.socket = socket; | |
| 385 | 392 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -62,6 +62,7 @@ using v8::Int32; | |||
| 62 | 62 | using v8::Integer; | |
| 63 | 63 | using v8::Local; | |
| 64 | 64 | using v8::MaybeLocal; | |
| 65 | + using v8::Number; | ||
| 65 | 66 | using v8::Object; | |
| 66 | 67 | using v8::String; | |
| 67 | 68 | using v8::Uint32; | |
@@ -486,8 +487,17 @@ class Parser : public AsyncWrap, public StreamListener { | |||
| 486 | 487 | static void Initialize(const FunctionCallbackInfo<Value>& args) { | |
| 487 | 488 | Environment* env = Environment::GetCurrent(args); | |
| 488 | 489 | ||
| 490 | + uint64_t max_http_header_size = 0; | ||
| 491 | + | ||
| 489 | 492 | CHECK(args[0]->IsInt32()); | |
| 490 | 493 | 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 | + } | ||
| 491 | 501 | ||
| 492 | 502 | llhttp_type_t type = | |
| 493 | 503 | static_cast<llhttp_type_t>(args[0].As<Int32>()->Value()); | |
@@ -505,7 +515,7 @@ class Parser : public AsyncWrap, public StreamListener { | |||
| 505 | 515 | ||
| 506 | 516 | parser->set_provider_type(provider); | |
| 507 | 517 | parser->AsyncReset(args[1].As<Object>()); | |
| 508 | - parser->Init(type); | ||
| 518 | + parser->Init(type, max_http_header_size); | ||
| 509 | 519 | } | |
| 510 | 520 | ||
| 511 | 521 | template <bool should_pause> | |
@@ -752,7 +762,7 @@ class Parser : public AsyncWrap, public StreamListener { | |||
| 752 | 762 | } | |
| 753 | 763 | ||
| 754 | 764 | ||
| 755 | - void Init(llhttp_type_t type) { | ||
| 765 | + void Init(llhttp_type_t type, uint64_t max_http_header_size) { | ||
| 756 | 766 | llhttp_init(&parser_, type, &settings); | |
| 757 | 767 | header_nread_ = 0; | |
| 758 | 768 | url_.Reset(); | |
@@ -761,12 +771,13 @@ class Parser : public AsyncWrap, public StreamListener { | |||
| 761 | 771 | num_values_ = 0; | |
| 762 | 772 | have_flushed_ = false; | |
| 763 | 773 | got_exception_ = false; | |
| 774 | + max_http_header_size_ = max_http_header_size; | ||
| 764 | 775 | } | |
| 765 | 776 | ||
| 766 | 777 | ||
| 767 | 778 | int TrackHeader(size_t len) { | |
| 768 | 779 | header_nread_ += len; | |
| 769 | - if (header_nread_ >= per_process::cli_options->max_http_header_size) { | ||
| 780 | + if (header_nread_ >= max_http_header_size_) { | ||
| 770 | 781 | llhttp_set_error_reason(&parser_, "HPE_HEADER_OVERFLOW:Header overflow"); | |
| 771 | 782 | return HPE_USER; | |
| 772 | 783 | } | |
@@ -801,6 +812,7 @@ class Parser : public AsyncWrap, public StreamListener { | |||
| 801 | 812 | unsigned int execute_depth_ = 0; | |
| 802 | 813 | bool pending_pause_ = false; | |
| 803 | 814 | uint64_t header_nread_ = 0; | |
| 815 | + uint64_t max_http_header_size_; | ||
| 804 | 816 | ||
| 805 | 817 | // These are helper functions for filling `http_parser_settings`, which turn | |
| 806 | 818 | // a member function of Parser into a C-style HTTP parser callback. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -436,6 +436,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { | |||
| 436 | 436 | "profile generated with --heap-prof. (default: 512 * 1024)", | |
| 437 | 437 | &EnvironmentOptions::heap_prof_interval); | |
| 438 | 438 | #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); | ||
| 439 | 443 | AddOption("--redirect-warnings", | |
| 440 | 444 | "write warnings to file instead of stderr", | |
| 441 | 445 | &EnvironmentOptions::redirect_warnings, | |
@@ -628,10 +632,6 @@ PerProcessOptionsParser::PerProcessOptionsParser( | |||
| 628 | 632 | kAllowedInEnvironment); | |
| 629 | 633 | AddAlias("--trace-events-enabled", { | |
| 630 | 634 | "--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); | ||
| 635 | 635 | AddOption("--v8-pool-size", | |
| 636 | 636 | "set V8's thread pool size", | |
| 637 | 637 | &PerProcessOptions::v8_thread_pool_size, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -115,6 +115,7 @@ class EnvironmentOptions : public Options { | |||
| 115 | 115 | bool expose_internals = false; | |
| 116 | 116 | bool frozen_intrinsics = false; | |
| 117 | 117 | std::string heap_snapshot_signal; | |
| 118 | + uint64_t max_http_header_size = 8 * 1024; | ||
| 118 | 119 | bool no_deprecation = false; | |
| 119 | 120 | bool no_force_async_hooks_checks = false; | |
| 120 | 121 | bool no_warnings = false; | |
@@ -200,7 +201,6 @@ class PerProcessOptions : public Options { | |||
| 200 | 201 | std::string title; | |
| 201 | 202 | std::string trace_event_categories; | |
| 202 | 203 | std::string trace_event_file_pattern = "node_trace.${rotation}.log"; | |
| 203 | - uint64_t max_http_header_size = 8 * 1024; | ||
| 204 | 204 | int64_t v8_thread_pool_size = 4; | |
| 205 | 205 | bool zero_fill_all_buffers = false; | |
| 206 | 206 | bool debug_arraybuffer_allocations = false; | |
| Original file line number | Diff line number | Diff 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 | + } | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments