@@ -486,6 +486,92 @@ Http2Session::Callbacks::~Callbacks() {
486
486
nghttp2_session_callbacks_del (callbacks);
487
487
}
488
488
489
+ // Track memory allocated by nghttp2 using a custom allocator.
490
+ class Http2Session ::MemoryAllocatorInfo {
491
+ public:
492
+ explicit MemoryAllocatorInfo (Http2Session* session)
493
+ : info({ session, H2Malloc, H2Free, H2Calloc, H2Realloc }) {}
494
+
495
+ static void * H2Malloc (size_t size, void * user_data) {
496
+ return H2Realloc (nullptr , size, user_data);
497
+ }
498
+
499
+ static void * H2Calloc (size_t nmemb, size_t size, void * user_data) {
500
+ size_t real_size = MultiplyWithOverflowCheck (nmemb, size);
501
+ void * mem = H2Malloc (real_size, user_data);
502
+ if (mem != nullptr )
503
+ memset (mem, 0 , real_size);
504
+ return mem;
505
+ }
506
+
507
+ static void H2Free (void * ptr, void * user_data) {
508
+ if (ptr == nullptr ) return ; // free(null); happens quite often.
509
+ void * result = H2Realloc (ptr, 0 , user_data);
510
+ CHECK_EQ (result, nullptr );
511
+ }
512
+
513
+ static void * H2Realloc (void * ptr, size_t size, void * user_data) {
514
+ Http2Session* session = static_cast <Http2Session*>(user_data);
515
+ size_t previous_size = 0 ;
516
+ char * original_ptr = nullptr ;
517
+
518
+ // We prepend each allocated buffer with a size_t containing the full
519
+ // size of the allocation.
520
+ if (size > 0 ) size += sizeof (size_t );
521
+
522
+ if (ptr != nullptr ) {
523
+ // We are free()ing or re-allocating.
524
+ original_ptr = static_cast <char *>(ptr) - sizeof (size_t );
525
+ previous_size = *reinterpret_cast <size_t *>(original_ptr);
526
+ // This means we called StopTracking() on this pointer before.
527
+ if (previous_size == 0 ) {
528
+ // Fall back to the standard Realloc() function.
529
+ char * ret = UncheckedRealloc (original_ptr, size);
530
+ if (ret != nullptr )
531
+ ret += sizeof (size_t );
532
+ return ret;
533
+ }
534
+ }
535
+ CHECK_GE (session->current_nghttp2_memory_ , previous_size);
536
+
537
+ // TODO(addaleax): Add the following, and handle NGHTTP2_ERR_NOMEM properly
538
+ // everywhere:
539
+ //
540
+ // if (size > previous_size &&
541
+ // !session->IsAvailableSessionMemory(size - previous_size)) {
542
+ // return nullptr;
543
+ // }
544
+
545
+ char * mem = UncheckedRealloc (original_ptr, size);
546
+
547
+ if (mem != nullptr ) {
548
+ // Adjust the memory info counter.
549
+ session->current_nghttp2_memory_ += size - previous_size;
550
+ *reinterpret_cast <size_t *>(mem) = size;
551
+ mem += sizeof (size_t );
552
+ } else if (size == 0 ) {
553
+ session->current_nghttp2_memory_ -= previous_size;
554
+ }
555
+
556
+ return mem;
557
+ }
558
+
559
+ static void StopTracking (Http2Session* session, void * ptr) {
560
+ size_t * original_ptr = reinterpret_cast <size_t *>(
561
+ static_cast <char *>(ptr) - sizeof (size_t ));
562
+ session->current_nghttp2_memory_ -= *original_ptr;
563
+ *original_ptr = 0 ;
564
+ }
565
+
566
+ inline nghttp2_mem* operator *() { return &info; }
567
+
568
+ nghttp2_mem info;
569
+ };
570
+
571
+ void Http2Session::StopTrackingRcbuf (nghttp2_rcbuf* buf) {
572
+ MemoryAllocatorInfo::StopTracking (this , buf);
573
+ }
574
+
489
575
Http2Session::Http2Session (Environment* env,
490
576
Local<Object> wrap,
491
577
nghttp2_session_type type)
@@ -517,15 +603,17 @@ Http2Session::Http2Session(Environment* env,
517
603
= callback_struct_saved[hasGetPaddingCallback ? 1 : 0 ].callbacks ;
518
604
519
605
auto fn = type == NGHTTP2_SESSION_SERVER ?
520
- nghttp2_session_server_new2 :
521
- nghttp2_session_client_new2;
606
+ nghttp2_session_server_new3 :
607
+ nghttp2_session_client_new3;
608
+
609
+ MemoryAllocatorInfo allocator_info (this );
522
610
523
611
// This should fail only if the system is out of memory, which
524
612
// is going to cause lots of other problems anyway, or if any
525
613
// of the options are out of acceptable range, which we should
526
614
// be catching before it gets this far. Either way, crash if this
527
615
// fails.
528
- CHECK_EQ (fn (&session_, callbacks, this , *opts), 0 );
616
+ CHECK_EQ (fn (&session_, callbacks, this , *opts, *allocator_info ), 0 );
529
617
530
618
outgoing_storage_.reserve (4096 );
531
619
outgoing_buffers_.reserve (32 );
@@ -553,6 +641,7 @@ Http2Session::~Http2Session() {
553
641
Unconsume ();
554
642
DEBUG_HTTP2SESSION (this , " freeing nghttp2 session" );
555
643
nghttp2_session_del (session_);
644
+ CHECK_EQ (current_nghttp2_memory_, 0 );
556
645
}
557
646
558
647
inline bool HasHttp2Observer (Environment* env) {
@@ -1160,9 +1249,9 @@ inline void Http2Session::HandleHeadersFrame(const nghttp2_frame* frame) {
1160
1249
nghttp2_header item = headers[n++];
1161
1250
// The header name and value are passed as external one-byte strings
1162
1251
name_str =
1163
- ExternalHeader::New<true >(env () , item.name ).ToLocalChecked ();
1252
+ ExternalHeader::New<true >(this , item.name ).ToLocalChecked ();
1164
1253
value_str =
1165
- ExternalHeader::New<false >(env () , item.value ).ToLocalChecked ();
1254
+ ExternalHeader::New<false >(this , item.value ).ToLocalChecked ();
1166
1255
argv[j * 2 ] = name_str;
1167
1256
argv[j * 2 + 1 ] = value_str;
1168
1257
j++;
0 commit comments