| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 59f83d6 commit f233b16
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -293,6 +293,13 @@ Indicate the end of node options. Pass the rest of the arguments to the script. | |||
| 293 | 293 | If no script filename or eval/print script is supplied prior to this, then | |
| 294 | 294 | the next argument will be used as a script filename. | |
| 295 | 295 | ||
| 296 | + ### `--max-http-header-size=size` | ||
| 297 | + <!-- YAML | ||
| 298 | + added: REPLACEME | ||
| 299 | + --> | ||
| 300 | + | ||
| 301 | + Specify the maximum size, in bytes, of HTTP headers. Defaults to 8KB. | ||
| 302 | + | ||
| 296 | 303 | ## Environment Variables | |
| 297 | 304 | ||
| 298 | 305 | ### `NODE_DEBUG=module[,…]` | |
@@ -353,6 +360,7 @@ Node options that are allowed are: | |||
| 353 | 360 | - `--debug-brk` | |
| 354 | 361 | - `--debug-port` | |
| 355 | 362 | - `--debug` | |
| 363 | + - `--max-http-header-size` | ||
| 356 | 364 | - `--no-deprecation` | |
| 357 | 365 | - `--no-warnings` | |
| 358 | 366 | - `--openssl-config` | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -92,6 +92,10 @@ Open the REPL even if stdin does not appear to be a terminal. | |||
| 92 | 92 | Preload the specified module at startup. Follows `require()`'s module resolution | |
| 93 | 93 | rules. \fImodule\fR may be either a path to a file, or a node module name. | |
| 94 | 94 | ||
| 95 | + .TP | ||
| 96 | + .BR \-\-max\-http\-header-size \fI=size\fR | ||
| 97 | + Specify the maximum size of HTTP headers in bytes. Defaults to 8KB. | ||
| 98 | + | ||
| 95 | 99 | .TP | |
| 96 | 100 | .BR \-\-no\-deprecation | |
| 97 | 101 | Silence deprecation warnings. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -170,6 +170,8 @@ unsigned int reverted = 0; | |||
| 170 | 170 | static std::string icu_data_dir; // NOLINT(runtime/string) | |
| 171 | 171 | #endif | |
| 172 | 172 | ||
| 173 | + uint64_t max_http_header_size = 8 * 1024; | ||
| 174 | + | ||
| 173 | 175 | // used by C++ modules as well | |
| 174 | 176 | bool no_deprecation = false; | |
| 175 | 177 | ||
@@ -3731,6 +3733,8 @@ static void PrintHelp() { | |||
| 3731 | 3733 | " --trace-deprecation show stack traces on deprecations\n" | |
| 3732 | 3734 | " --throw-deprecation throw an exception anytime a deprecated " | |
| 3733 | 3735 | "function is used\n" | |
| 3736 | + " --max-http-header-size Specify the maximum size of HTTP\n" | ||
| 3737 | + " headers in bytes. Defaults to 8KB.\n" | ||
| 3734 | 3738 | " --no-warnings silence all process warnings\n" | |
| 3735 | 3739 | " --napi-modules load N-API modules (no-op - option kept for " | |
| 3736 | 3740 | " compatibility)\n" | |
@@ -3852,6 +3856,7 @@ static void CheckIfAllowedInEnv(const char* exe, bool is_env, | |||
| 3852 | 3856 | "--pending-deprecation", | |
| 3853 | 3857 | "--no-warnings", | |
| 3854 | 3858 | "--napi-modules", | |
| 3859 | + "--max-http-header-size", | ||
| 3855 | 3860 | "--trace-warnings", | |
| 3856 | 3861 | "--redirect-warnings", | |
| 3857 | 3862 | "--trace-sync-io", | |
@@ -4010,6 +4015,8 @@ static void ParseArgs(int* argc, | |||
| 4010 | 4015 | new_v8_argc += 1; | |
| 4011 | 4016 | } else if (strncmp(arg, "--v8-pool-size=", 15) == 0) { | |
| 4012 | 4017 | v8_thread_pool_size = atoi(arg + 15); | |
| 4018 | + } else if (strncmp(arg, "--max-http-header-size=", 23) == 0) { | ||
| 4019 | + max_http_header_size = atoi(arg + 23); | ||
| 4013 | 4020 | #if HAVE_OPENSSL | |
| 4014 | 4021 | } else if (strncmp(arg, "--tls-cipher-list=", 18) == 0) { | |
| 4015 | 4022 | default_cipher_list = arg + 18; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -7,6 +7,7 @@ namespace node { | |||
| 7 | 7 | ||
| 8 | 8 | using v8::Context; | |
| 9 | 9 | using v8::Local; | |
| 10 | + using v8::Number; | ||
| 10 | 11 | using v8::Object; | |
| 11 | 12 | using v8::ReadOnly; | |
| 12 | 13 | using v8::String; | |
@@ -24,6 +25,13 @@ using v8::Value; | |||
| 24 | 25 | True(env->isolate()), ReadOnly).FromJust(); \ | |
| 25 | 26 | } while (0) | |
| 26 | 27 | ||
| 28 | + #define READONLY_PROPERTY(obj, name, value) \ | ||
| 29 | + do { \ | ||
| 30 | + obj->DefineOwnProperty(env->context(), \ | ||
| 31 | + FIXED_ONE_BYTE_STRING(env->isolate(), name), \ | ||
| 32 | + value, ReadOnly).FromJust(); \ | ||
| 33 | + } while (0) | ||
| 34 | + | ||
| 27 | 35 | void InitConfig(Local<Object> target, | |
| 28 | 36 | Local<Value> unused, | |
| 29 | 37 | Local<Context> context) { | |
@@ -46,6 +54,11 @@ void InitConfig(Local<Object> target, | |||
| 46 | 54 | if (config_expose_internals) | |
| 47 | 55 | READONLY_BOOLEAN_PROPERTY("exposeInternals"); | |
| 48 | 56 | ||
| 57 | + | ||
| 58 | + READONLY_PROPERTY(target, | ||
| 59 | + "maxHTTPHeaderSize", | ||
| 60 | + Number::New(env->isolate(), max_http_header_size)); | ||
| 61 | + | ||
| 49 | 62 | if (!config_warning_file.empty()) { | |
| 50 | 63 | Local<String> name = OneByteString(env->isolate(), "warningFile"); | |
| 51 | 64 | Local<String> value = String::NewFromUtf8(env->isolate(), | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -731,6 +731,9 @@ const struct http_parser_settings Parser::settings = { | |||
| 731 | 731 | nullptr // on_chunk_complete | |
| 732 | 732 | }; | |
| 733 | 733 | ||
| 734 | + void InitMaxHttpHeaderSizeOnce() { | ||
| 735 | + http_parser_set_max_header_size(max_http_header_size); | ||
| 736 | + } | ||
| 734 | 737 | ||
| 735 | 738 | void InitHttpParser(Local<Object> target, | |
| 736 | 739 | Local<Value> unused, | |
@@ -775,6 +778,8 @@ void InitHttpParser(Local<Object> target, | |||
| 775 | 778 | ||
| 776 | 779 | target->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "HTTPParser"), | |
| 777 | 780 | t->GetFunction()); | |
| 781 | + static uv_once_t init_once = UV_ONCE_INIT; | ||
| 782 | + uv_once(&init_once, InitMaxHttpHeaderSizeOnce); | ||
| 778 | 783 | } | |
| 779 | 784 | ||
| 780 | 785 | } // namespace node | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -56,6 +56,9 @@ extern bool config_expose_internals; | |||
| 56 | 56 | // it to stderr. | |
| 57 | 57 | extern std::string config_warning_file; | |
| 58 | 58 | ||
| 59 | + // Set in node.cc by ParseArgs when --max-http-header-size is used | ||
| 60 | + extern uint64_t max_http_header_size; | ||
| 61 | + | ||
| 59 | 62 | // Forward declaration | |
| 60 | 63 | class Environment; | |
| 61 | 64 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1,10 +1,17 @@ | |||
| 1 | 1 | 'use strict'; | |
| 2 | + // Flags: --expose_internals | ||
| 2 | 3 | ||
| 3 | 4 | const assert = require('assert'); | |
| 4 | 5 | const common = require('../common'); | |
| 5 | 6 | const http = require('http'); | |
| 6 | 7 | const net = require('net'); | |
| 7 | - const MAX = 8 * 1024; // 8KB | ||
| 8 | + const MAX = +(process.argv[2] || 8 * 1024); // Command line option, or 8KB. | ||
| 9 | + | ||
| 10 | + assert(process.binding('config').maxHTTPHeaderSize, | ||
| 11 | + 'The option should exist on process.binding(\'config\')'); | ||
| 12 | + | ||
| 13 | + console.log('pid is', process.pid); | ||
| 14 | + console.log('max header size is', process.binding('config').maxHTTPHeaderSize); | ||
| 8 | 15 | ||
| 9 | 16 | // Verify that we cannot receive more than 8KB of headers. | |
| 10 | 17 | ||
@@ -28,19 +35,15 @@ function fillHeaders(headers, currentSize, valid = false) { | |||
| 28 | 35 | headers += 'a'.repeat(MAX - headers.length - 3); | |
| 29 | 36 | // Generate valid headers | |
| 30 | 37 | if (valid) { | |
| 31 | - // TODO(mcollina): understand why -9 is needed instead of -1 | ||
| 32 | - headers = headers.slice(0, -9); | ||
| 38 | + // TODO(mcollina): understand why -32 is needed instead of -1 | ||
| 39 | + headers = headers.slice(0, -32); | ||
| 33 | 40 | } | |
| 34 | 41 | return headers + '\r\n\r\n'; | |
| 35 | 42 | } | |
| 36 | 43 | ||
| 37 | - const timeout = common.platformTimeout(10); | ||
| 38 | - | ||
| 39 | 44 | function writeHeaders(socket, headers) { | |
| 40 | 45 | const array = []; | |
| 41 | - | ||
| 42 | - // this is off from 1024 so that \r\n does not get split | ||
| 43 | - const chunkSize = 1000; | ||
| 46 | + const chunkSize = 100; | ||
| 44 | 47 | let last = 0; | |
| 45 | 48 | ||
| 46 | 49 | for (let i = 0; i < headers.length / chunkSize; i++) { | |
@@ -55,19 +58,25 @@ function writeHeaders(socket, headers) { | |||
| 55 | 58 | next(); | |
| 56 | 59 | ||
| 57 | 60 | function next() { | |
| 58 | - if (socket.write(array.shift())) { | ||
| 59 | - if (array.length === 0) { | ||
| 60 | - socket.end(); | ||
| 61 | - } else { | ||
| 62 | - setTimeout(next, timeout); | ||
| 63 | - } | ||
| 61 | + if (socket.destroyed) { | ||
| 62 | + console.log('socket was destroyed early, data left to write:', | ||
| 63 | + array.join('').length); | ||
| 64 | + return; | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + const chunk = array.shift(); | ||
| 68 | + | ||
| 69 | + if (chunk) { | ||
| 70 | + console.log('writing chunk of size', chunk.length); | ||
| 71 | + socket.write(chunk, next); | ||
| 64 | 72 | } else { | |
| 65 | - socket.once('drain', next); | ||
| 73 | + socket.end(); | ||
| 66 | 74 | } | |
| 67 | 75 | } | |
| 68 | 76 | } | |
| 69 | 77 | ||
| 70 | 78 | function test1() { | |
| 79 | + console.log('test1'); | ||
| 71 | 80 | let headers = | |
| 72 | 81 | 'HTTP/1.1 200 OK\r\n' + | |
| 73 | 82 | 'Content-Length: 0\r\n' + | |
@@ -82,6 +91,9 @@ function test1() { | |||
| 82 | 91 | writeHeaders(sock, headers); | |
| 83 | 92 | sock.resume(); | |
| 84 | 93 | }); | |
| 94 | + | ||
| 95 | + // The socket might error but that's ok | ||
| 96 | + sock.on('error', () => {}); | ||
| 85 | 97 | }); | |
| 86 | 98 | ||
| 87 | 99 | server.listen(0, common.mustCall(() => { | |
@@ -90,17 +102,17 @@ function test1() { | |||
| 90 | 102 | ||
| 91 | 103 | client.on('error', common.mustCall((err) => { | |
| 92 | 104 | assert.strictEqual(err.code, 'HPE_HEADER_OVERFLOW'); | |
| 93 | - server.close(); | ||
| 94 | - setImmediate(test2); | ||
| 105 | + server.close(test2); | ||
| 95 | 106 | })); | |
| 96 | 107 | })); | |
| 97 | 108 | } | |
| 98 | 109 | ||
| 99 | 110 | const test2 = common.mustCall(() => { | |
| 111 | + console.log('test2'); | ||
| 100 | 112 | let headers = | |
| 101 | 113 | 'GET / HTTP/1.1\r\n' + | |
| 102 | 114 | 'Host: localhost\r\n' + | |
| 103 | - 'Agent: node\r\n' + | ||
| 115 | + 'Agent: nod2\r\n' + | ||
| 104 | 116 | 'X-CRASH: '; | |
| 105 | 117 | ||
| 106 | 118 | // /, Host, localhost, Agent, node, X-CRASH, a... | |
@@ -109,7 +121,7 @@ const test2 = common.mustCall(() => { | |||
| 109 | 121 | ||
| 110 | 122 | const server = http.createServer(common.mustNotCall()); | |
| 111 | 123 | ||
| 112 | - server.on('clientError', common.mustCall((err) => { | ||
| 124 | + server.once('clientError', common.mustCall((err) => { | ||
| 113 | 125 | assert.strictEqual(err.code, 'HPE_HEADER_OVERFLOW'); | |
| 114 | 126 | })); | |
| 115 | 127 | ||
@@ -121,34 +133,46 @@ const test2 = common.mustCall(() => { | |||
| 121 | 133 | }); | |
| 122 | 134 | ||
| 123 | 135 | finished(client, common.mustCall((err) => { | |
| 124 | - server.close(); | ||
| 125 | - setImmediate(test3); | ||
| 136 | + server.close(test3); | ||
| 126 | 137 | })); | |
| 127 | 138 | })); | |
| 128 | 139 | }); | |
| 129 | 140 | ||
| 130 | 141 | const test3 = common.mustCall(() => { | |
| 142 | + console.log('test3'); | ||
| 131 | 143 | let headers = | |
| 132 | 144 | 'GET / HTTP/1.1\r\n' + | |
| 133 | 145 | 'Host: localhost\r\n' + | |
| 134 | - 'Agent: node\r\n' + | ||
| 146 | + 'Agent: nod3\r\n' + | ||
| 135 | 147 | 'X-CRASH: '; | |
| 136 | 148 | ||
| 137 | 149 | // /, Host, localhost, Agent, node, X-CRASH, a... | |
| 138 | 150 | const currentSize = 1 + 4 + 9 + 5 + 4 + 7; | |
| 139 | 151 | headers = fillHeaders(headers, currentSize, true); | |
| 140 | 152 | ||
| 153 | + console.log('writing', headers.length); | ||
| 154 | + | ||
| 141 | 155 | const server = http.createServer(common.mustCall((req, res) => { | |
| 142 | - res.end('hello world'); | ||
| 143 | - setImmediate(server.close.bind(server)); | ||
| 156 | + res.end('hello from test3 server'); | ||
| 157 | + server.close(); | ||
| 144 | 158 | })); | |
| 145 | 159 | ||
| 160 | + server.on('clientError', (err) => { | ||
| 161 | + console.log(err.code); | ||
| 162 | + if (err.code === 'HPE_HEADER_OVERFLOW') { | ||
| 163 | + console.log(err.rawPacket.toString('hex')); | ||
| 164 | + } | ||
| 165 | + }); | ||
| 166 | + server.on('clientError', common.mustNotCall()); | ||
| 167 | + | ||
| 146 | 168 | server.listen(0, common.mustCall(() => { | |
| 147 | 169 | const client = net.connect(server.address().port); | |
| 148 | 170 | client.on('connect', () => { | |
| 149 | 171 | writeHeaders(client, headers); | |
| 150 | 172 | client.resume(); | |
| 151 | 173 | }); | |
| 174 | + | ||
| 175 | + client.pipe(process.stdout); | ||
| 152 | 176 | })); | |
| 153 | 177 | }); | |
| 154 | 178 | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments