@@ -441,9 +441,8 @@ class Parser : public AsyncWrap, public StreamListener {
441441 };
442442
443443 Local<Value> argv[A_MAX ];
444- Local<Object> obj = object ();
445- Local<Value> cb = obj->Get (env ()->context (),
446- kOnHeadersComplete ).ToLocalChecked ();
444+ Local<Value> cb =
445+ CachedCallback (kOnHeadersComplete , &on_headers_complete_cb_);
447446
448447 if (!cb->IsFunction ())
449448 return 0 ;
@@ -521,7 +520,7 @@ class Parser : public AsyncWrap, public StreamListener {
521520 Environment* env = this ->env ();
522521 HandleScope handle_scope (env->isolate ());
523522
524- Local<Value> cb = object ()-> Get (env-> context (), kOnBody ). ToLocalChecked ( );
523+ Local<Value> cb = CachedCallback ( kOnBody , &on_body_cb_ );
525524
526525 if (!cb->IsFunction ())
527526 return 0 ;
@@ -554,9 +553,8 @@ class Parser : public AsyncWrap, public StreamListener {
554553
555554 header_pairs_ = 0 ;
556555
557- Local<Object> obj = object ();
558- Local<Value> cb = obj->Get (env ()->context (),
559- kOnMessageComplete ).ToLocalChecked ();
556+ Local<Value> cb =
557+ CachedCallback (kOnMessageComplete , &on_message_complete_cb_);
560558
561559 if (!cb->IsFunction ())
562560 return 0 ;
@@ -940,6 +938,9 @@ class Parser : public AsyncWrap, public StreamListener {
940938 Local<Value> headers_v[kMaxHeaderFieldsCount * 2 ];
941939
942940 for (size_t i = 0 ; i < num_values_; ++i) {
941+ // Field names are not internalized: header names are attacker
942+ // controlled, so a flood of unique names would grow V8's string table
943+ // and pay the interning cost on every request with no dedup benefit.
943944 headers_v[i * 2 ] = fields_[i].ToString (env ());
944945 headers_v[i * 2 + 1 ] = values_[i].ToTrimmedString (env ());
945946 }
@@ -975,10 +976,28 @@ class Parser : public AsyncWrap, public StreamListener {
975976 }
976977
977978
979+ // Caches a set-once callback property; the cache is cleared in Init().
980+ Local<Value> CachedCallback (uint32_t index, v8::Global<v8::Value>* cache) {
981+ Isolate* isolate = env ()->isolate ();
982+ if (!cache->IsEmpty ()) {
983+ return cache->Get (isolate);
984+ }
985+ Local<Value> cb =
986+ object ()->Get (env ()->context (), index).ToLocalChecked ();
987+ if (cb->IsFunction ()) {
988+ cache->Reset (isolate, cb);
989+ }
990+ return cb;
991+ }
992+
978993 void Init (llhttp_type_t type, uint64_t max_http_header_size,
979994 uint32_t lenient_flags) {
980995 llhttp_init (&parser_, type, &settings);
981996
997+ on_headers_complete_cb_.Reset ();
998+ on_body_cb_.Reset ();
999+ on_message_complete_cb_.Reset ();
1000+
9821001 if (lenient_flags & kLenientHeaders ) {
9831002 llhttp_set_lenient_headers (&parser_, 1 );
9841003 }
@@ -1102,6 +1121,10 @@ class Parser : public AsyncWrap, public StreamListener {
11021121 size_t header_pairs_ = 0 ;
11031122 double max_header_pairs_ = -1 ;
11041123 bool pending_pause_ = false ;
1124+
1125+ v8::Global<v8::Value> on_headers_complete_cb_;
1126+ v8::Global<v8::Value> on_body_cb_;
1127+ v8::Global<v8::Value> on_message_complete_cb_;
11051128 bool received_data_ = false ;
11061129 uint64_t header_nread_ = 0 ;
11071130 uint64_t chunk_extensions_nread_ = 0 ;
0 commit comments