diff options
author | Alex R <alexei.rudenko@gmail.com> | 2019-10-31 01:33:33 +0300 |
---|---|---|
committer | Michaƫl Zasso <targos@protonmail.com> | 2020-04-11 11:01:16 +0300 |
commit | 8597df48f79408b0a72049197982c67e424552c0 (patch) | |
tree | 4e1af04636bddca37f6942047ae775e533bbe363 /src/node_http_parser.cc | |
parent | 4acd7f4390f4c73c14532fef1cb3e56911604935 (diff) |
http: fix incorrect headersTimeout measurement
For keep-alive connections, the headersTimeout may fire during
subsequent request because the measurement was reset after
a request and not before a request.
PR-URL: https://github.com/nodejs/node/pull/32329
Fixes: https://github.com/nodejs/node/issues/27363
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Diffstat (limited to 'src/node_http_parser.cc')
-rw-r--r-- | src/node_http_parser.cc | 37 |
1 files changed, 35 insertions, 2 deletions
diff --git a/src/node_http_parser.cc b/src/node_http_parser.cc index 75d7e89a91c..2c75e6a532f 100644 --- a/src/node_http_parser.cc +++ b/src/node_http_parser.cc @@ -74,6 +74,7 @@ const uint32_t kOnHeadersComplete = 1; const uint32_t kOnBody = 2; const uint32_t kOnMessageComplete = 3; const uint32_t kOnExecute = 4; +const uint32_t kOnTimeout = 5; // Any more fields than this will be flushed into JS const size_t kMaxHeaderFieldsCount = 32; @@ -181,6 +182,7 @@ class Parser : public AsyncWrap, public StreamListener { num_fields_ = num_values_ = 0; url_.Reset(); status_message_.Reset(); + header_parsing_start_time_ = uv_hrtime(); return 0; } @@ -504,6 +506,7 @@ class Parser : public AsyncWrap, public StreamListener { bool lenient = args[3]->IsTrue(); uint64_t max_http_header_size = 0; + uint64_t headers_timeout = 0; CHECK(args[0]->IsInt32()); CHECK(args[1]->IsObject()); @@ -516,6 +519,11 @@ class Parser : public AsyncWrap, public StreamListener { max_http_header_size = env->options()->max_http_header_size; } + if (args.Length() > 4) { + CHECK(args[4]->IsInt32()); + headers_timeout = args[4].As<Number>()->Value(); + } + llhttp_type_t type = static_cast<llhttp_type_t>(args[0].As<Int32>()->Value()); @@ -532,7 +540,7 @@ class Parser : public AsyncWrap, public StreamListener { parser->set_provider_type(provider); parser->AsyncReset(args[1].As<Object>()); - parser->Init(type, max_http_header_size, lenient); + parser->Init(type, max_http_header_size, lenient, headers_timeout); } template <bool should_pause> @@ -636,6 +644,24 @@ class Parser : public AsyncWrap, public StreamListener { if (ret.IsEmpty()) return; + // check header parsing time + if (header_parsing_start_time_ != 0 && headers_timeout_ != 0) { + uint64_t now = uv_hrtime(); + uint64_t parsing_time = (now - header_parsing_start_time_) / 1e6; + + if (parsing_time > headers_timeout_) { + Local<Value> cb = + object()->Get(env()->context(), kOnTimeout).ToLocalChecked(); + + if (!cb->IsFunction()) + return; + + MakeCallback(cb.As<Function>(), 0, nullptr); + + return; + } + } + Local<Value> cb = object()->Get(env()->context(), kOnExecute).ToLocalChecked(); @@ -779,7 +805,8 @@ class Parser : public AsyncWrap, public StreamListener { } - void Init(llhttp_type_t type, uint64_t max_http_header_size, bool lenient) { + void Init(llhttp_type_t type, uint64_t max_http_header_size, + bool lenient, uint64_t headers_timeout) { llhttp_init(&parser_, type, &settings); llhttp_set_lenient(&parser_, lenient); header_nread_ = 0; @@ -790,6 +817,8 @@ class Parser : public AsyncWrap, public StreamListener { have_flushed_ = false; got_exception_ = false; max_http_header_size_ = max_http_header_size; + header_parsing_start_time_ = 0; + headers_timeout_ = headers_timeout; } @@ -831,6 +860,8 @@ class Parser : public AsyncWrap, public StreamListener { bool pending_pause_ = false; uint64_t header_nread_ = 0; uint64_t max_http_header_size_; + uint64_t headers_timeout_; + uint64_t header_parsing_start_time_ = 0; // These are helper functions for filling `http_parser_settings`, which turn // a member function of Parser into a C-style HTTP parser callback. @@ -890,6 +921,8 @@ void InitializeHttpParser(Local<Object> target, Integer::NewFromUnsigned(env->isolate(), kOnMessageComplete)); t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnExecute"), Integer::NewFromUnsigned(env->isolate(), kOnExecute)); + t->Set(FIXED_ONE_BYTE_STRING(env->isolate(), "kOnTimeout"), + Integer::NewFromUnsigned(env->isolate(), kOnTimeout)); Local<Array> methods = Array::New(env->isolate()); #define V(num, name, string) \ |