@@ -451,6 +451,92 @@ Http2Session::Callbacks::~Callbacks() {
451
451
nghttp2_session_callbacks_del (callbacks);
452
452
}
453
453
454
+ // Track memory allocated by nghttp2 using a custom allocator.
455
+ class Http2Session ::MemoryAllocatorInfo {
456
+ public:
457
+ explicit MemoryAllocatorInfo (Http2Session* session)
458
+ : info({ session, H2Malloc, H2Free, H2Calloc, H2Realloc }) {}
459
+
460
+ static void * H2Malloc (size_t size, void * user_data) {
461
+ return H2Realloc (nullptr , size, user_data);
462
+ }
463
+
464
+ static void * H2Calloc (size_t nmemb, size_t size, void * user_data) {
465
+ size_t real_size = MultiplyWithOverflowCheck (nmemb, size);
466
+ void * mem = H2Malloc (real_size, user_data);
467
+ if (mem != nullptr )
468
+ memset (mem, 0 , real_size);
469
+ return mem;
470
+ }
471
+
472
+ static void H2Free (void * ptr, void * user_data) {
473
+ if (ptr == nullptr ) return ; // free(null); happens quite often.
474
+ void * result = H2Realloc (ptr, 0 , user_data);
475
+ CHECK_EQ (result, nullptr );
476
+ }
477
+
478
+ static void * H2Realloc (void * ptr, size_t size, void * user_data) {
479
+ Http2Session* session = static_cast <Http2Session*>(user_data);
480
+ size_t previous_size = 0 ;
481
+ char * original_ptr = nullptr ;
482
+
483
+ // We prepend each allocated buffer with a size_t containing the full
484
+ // size of the allocation.
485
+ if (size > 0 ) size += sizeof (size_t );
486
+
487
+ if (ptr != nullptr ) {
488
+ // We are free()ing or re-allocating.
489
+ original_ptr = static_cast <char *>(ptr) - sizeof (size_t );
490
+ previous_size = *reinterpret_cast <size_t *>(original_ptr);
491
+ // This means we called StopTracking() on this pointer before.
492
+ if (previous_size == 0 ) {
493
+ // Fall back to the standard Realloc() function.
494
+ char * ret = UncheckedRealloc (original_ptr, size);
495
+ if (ret != nullptr )
496
+ ret += sizeof (size_t );
497
+ return ret;
498
+ }
499
+ }
500
+ CHECK_GE (session->current_nghttp2_memory_ , previous_size);
501
+
502
+ // TODO(addaleax): Add the following, and handle NGHTTP2_ERR_NOMEM properly
503
+ // everywhere:
504
+ //
505
+ // if (size > previous_size &&
506
+ // !session->IsAvailableSessionMemory(size - previous_size)) {
507
+ // return nullptr;
508
+ // }
509
+
510
+ char * mem = UncheckedRealloc (original_ptr, size);
511
+
512
+ if (mem != nullptr ) {
513
+ // Adjust the memory info counter.
514
+ session->current_nghttp2_memory_ += size - previous_size;
515
+ *reinterpret_cast <size_t *>(mem) = size;
516
+ mem += sizeof (size_t );
517
+ } else if (size == 0 ) {
518
+ session->current_nghttp2_memory_ -= previous_size;
519
+ }
520
+
521
+ return mem;
522
+ }
523
+
524
+ static void StopTracking (Http2Session* session, void * ptr) {
525
+ size_t * original_ptr = reinterpret_cast <size_t *>(
526
+ static_cast <char *>(ptr) - sizeof (size_t ));
527
+ session->current_nghttp2_memory_ -= *original_ptr;
528
+ *original_ptr = 0 ;
529
+ }
530
+
531
+ inline nghttp2_mem* operator *() { return &info; }
532
+
533
+ nghttp2_mem info;
534
+ };
535
+
536
+ void Http2Session::StopTrackingRcbuf (nghttp2_rcbuf* buf) {
537
+ MemoryAllocatorInfo::StopTracking (this , buf);
538
+ }
539
+
454
540
Http2Session::Http2Session (Environment* env,
455
541
Local<Object> wrap,
456
542
nghttp2_session_type type)
@@ -482,15 +568,17 @@ Http2Session::Http2Session(Environment* env,
482
568
= callback_struct_saved[hasGetPaddingCallback ? 1 : 0 ].callbacks ;
483
569
484
570
auto fn = type == NGHTTP2_SESSION_SERVER ?
485
- nghttp2_session_server_new2 :
486
- nghttp2_session_client_new2;
571
+ nghttp2_session_server_new3 :
572
+ nghttp2_session_client_new3;
573
+
574
+ MemoryAllocatorInfo allocator_info (this );
487
575
488
576
// This should fail only if the system is out of memory, which
489
577
// is going to cause lots of other problems anyway, or if any
490
578
// of the options are out of acceptable range, which we should
491
579
// be catching before it gets this far. Either way, crash if this
492
580
// fails.
493
- CHECK_EQ (fn (&session_, callbacks, this , *opts), 0 );
581
+ CHECK_EQ (fn (&session_, callbacks, this , *opts, *allocator_info ), 0 );
494
582
495
583
outgoing_storage_.reserve (4096 );
496
584
outgoing_buffers_.reserve (32 );
@@ -502,6 +590,7 @@ Http2Session::~Http2Session() {
502
590
for (const auto & stream : streams_)
503
591
stream.second ->session_ = nullptr ;
504
592
nghttp2_session_del (session_);
593
+ CHECK_EQ (current_nghttp2_memory_, 0 );
505
594
}
506
595
507
596
std::string Http2Session::diagnostic_name () const {
@@ -1150,9 +1239,9 @@ void Http2Session::HandleHeadersFrame(const nghttp2_frame* frame) {
1150
1239
nghttp2_header item = headers[n++];
1151
1240
// The header name and value are passed as external one-byte strings
1152
1241
name_str =
1153
- ExternalHeader::New<true >(env () , item.name ).ToLocalChecked ();
1242
+ ExternalHeader::New<true >(this , item.name ).ToLocalChecked ();
1154
1243
value_str =
1155
- ExternalHeader::New<false >(env () , item.value ).ToLocalChecked ();
1244
+ ExternalHeader::New<false >(this , item.value ).ToLocalChecked ();
1156
1245
argv[j * 2 ] = name_str;
1157
1246
argv[j * 2 + 1 ] = value_str;
1158
1247
j++;
0 commit comments