@@ -74,6 +74,7 @@ const uint32_t kOnHeadersComplete = 1;
7474const uint32_t kOnBody = 2 ;
7575const uint32_t kOnMessageComplete = 3 ;
7676const uint32_t kOnExecute = 4 ;
77+ const uint32_t kOnTimeout = 5 ;
7778// Any more fields than this will be flushed into JS
7879const size_t kMaxHeaderFieldsCount = 32 ;
7980
@@ -181,6 +182,7 @@ class Parser : public AsyncWrap, public StreamListener {
181182 num_fields_ = num_values_ = 0 ;
182183 url_.Reset ();
183184 status_message_.Reset ();
185+ header_parsing_start_time_ = uv_hrtime ();
184186 return 0 ;
185187 }
186188
@@ -504,6 +506,7 @@ class Parser : public AsyncWrap, public StreamListener {
504506 bool lenient = args[3 ]->IsTrue ();
505507
506508 uint64_t max_http_header_size = 0 ;
509+ uint64_t headers_timeout = 0 ;
507510
508511 CHECK (args[0 ]->IsInt32 ());
509512 CHECK (args[1 ]->IsObject ());
@@ -516,6 +519,11 @@ class Parser : public AsyncWrap, public StreamListener {
516519 max_http_header_size = env->options ()->max_http_header_size ;
517520 }
518521
522+ if (args.Length () > 4 ) {
523+ CHECK (args[4 ]->IsInt32 ());
524+ headers_timeout = args[4 ].As <Number>()->Value ();
525+ }
526+
519527 llhttp_type_t type =
520528 static_cast <llhttp_type_t >(args[0 ].As <Int32>()->Value ());
521529
@@ -532,7 +540,7 @@ class Parser : public AsyncWrap, public StreamListener {
532540
533541 parser->set_provider_type (provider);
534542 parser->AsyncReset (args[1 ].As <Object>());
535- parser->Init (type, max_http_header_size, lenient);
543+ parser->Init (type, max_http_header_size, lenient, headers_timeout );
536544 }
537545
538546 template <bool should_pause>
@@ -636,6 +644,24 @@ class Parser : public AsyncWrap, public StreamListener {
636644 if (ret.IsEmpty ())
637645 return ;
638646
647+ // check header parsing time
648+ if (header_parsing_start_time_ != 0 && headers_timeout_ != 0 ) {
649+ uint64_t now = uv_hrtime ();
650+ uint64_t parsing_time = (now - header_parsing_start_time_) / 1e6 ;
651+
652+ if (parsing_time > headers_timeout_) {
653+ Local<Value> cb =
654+ object ()->Get (env ()->context (), kOnTimeout ).ToLocalChecked ();
655+
656+ if (!cb->IsFunction ())
657+ return ;
658+
659+ MakeCallback (cb.As <Function>(), 0 , nullptr );
660+
661+ return ;
662+ }
663+ }
664+
639665 Local<Value> cb =
640666 object ()->Get (env ()->context (), kOnExecute ).ToLocalChecked ();
641667
@@ -779,7 +805,8 @@ class Parser : public AsyncWrap, public StreamListener {
779805 }
780806
781807
782- void Init (llhttp_type_t type, uint64_t max_http_header_size, bool lenient) {
808+ void Init (llhttp_type_t type, uint64_t max_http_header_size,
809+ bool lenient, uint64_t headers_timeout) {
783810 llhttp_init (&parser_, type, &settings);
784811 llhttp_set_lenient (&parser_, lenient);
785812 header_nread_ = 0 ;
@@ -790,6 +817,8 @@ class Parser : public AsyncWrap, public StreamListener {
790817 have_flushed_ = false ;
791818 got_exception_ = false ;
792819 max_http_header_size_ = max_http_header_size;
820+ header_parsing_start_time_ = 0 ;
821+ headers_timeout_ = headers_timeout;
793822 }
794823
795824
@@ -831,6 +860,8 @@ class Parser : public AsyncWrap, public StreamListener {
831860 bool pending_pause_ = false ;
832861 uint64_t header_nread_ = 0 ;
833862 uint64_t max_http_header_size_;
863+ uint64_t headers_timeout_;
864+ uint64_t header_parsing_start_time_ = 0 ;
834865
835866 // These are helper functions for filling `http_parser_settings`, which turn
836867 // a member function of Parser into a C-style HTTP parser callback.
@@ -890,6 +921,8 @@ void InitializeHttpParser(Local<Object> target,
890921 Integer::NewFromUnsigned (env->isolate (), kOnMessageComplete ));
891922 t->Set (FIXED_ONE_BYTE_STRING (env->isolate (), " kOnExecute" ),
892923 Integer::NewFromUnsigned (env->isolate (), kOnExecute ));
924+ t->Set (FIXED_ONE_BYTE_STRING (env->isolate (), " kOnTimeout" ),
925+ Integer::NewFromUnsigned (env->isolate (), kOnTimeout ));
893926
894927 Local<Array> methods = Array::New (env->isolate ());
895928#define V (num, name, string ) \
0 commit comments