@@ -471,6 +471,8 @@ Http2Session::Callbacks::Callbacks(bool kHasGetPaddingCallback) {
471471 callbacks, OnSendData);
472472 nghttp2_session_callbacks_set_on_invalid_frame_recv_callback (
473473 callbacks, OnInvalidFrame);
474+ nghttp2_session_callbacks_set_on_frame_send_callback (
475+ callbacks, OnFrameSent);
474476
475477 if (kHasGetPaddingCallback ) {
476478 nghttp2_session_callbacks_set_select_padding_callback (
@@ -559,28 +561,35 @@ inline void Http2Stream::EmitStatistics() {
559561 if (!HasHttp2Observer (env ()))
560562 return ;
561563 Http2StreamPerformanceEntry* entry =
562- new Http2StreamPerformanceEntry (env (), statistics_);
564+ new Http2StreamPerformanceEntry (env (), id_, statistics_);
563565 env ()->SetImmediate ([](Environment* env, void * data) {
564- Local<Context> context = env->context ();
565566 Http2StreamPerformanceEntry* entry =
566567 static_cast <Http2StreamPerformanceEntry*>(data);
567568 if (HasHttp2Observer (env)) {
568- Local<Object> obj = entry->ToObject ();
569- v8::PropertyAttribute attr =
570- static_cast <v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
571- obj->DefineOwnProperty (
572- context,
573- FIXED_ONE_BYTE_STRING (env->isolate (), " timeToFirstByte" ),
574- Number::New (env->isolate (),
575- (entry->first_byte () - entry->startTimeNano ()) / 1e6 ),
576- attr).FromJust ();
577- obj->DefineOwnProperty (
578- context,
579- FIXED_ONE_BYTE_STRING (env->isolate (), " timeToFirstHeader" ),
580- Number::New (env->isolate (),
581- (entry->first_header () - entry->startTimeNano ()) / 1e6 ),
582- attr).FromJust ();
583- entry->Notify (obj);
569+ AliasedBuffer<double , v8::Float64Array>& buffer =
570+ env->http2_state ()->stream_stats_buffer ;
571+ buffer[IDX_STREAM_STATS_ID] = entry->id ();
572+ if (entry->first_byte () != 0 ) {
573+ buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTE] =
574+ (entry->first_byte () - entry->startTimeNano ()) / 1e6 ;
575+ } else {
576+ buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTE] = 0 ;
577+ }
578+ if (entry->first_header () != 0 ) {
579+ buffer[IDX_STREAM_STATS_TIMETOFIRSTHEADER] =
580+ (entry->first_header () - entry->startTimeNano ()) / 1e6 ;
581+ } else {
582+ buffer[IDX_STREAM_STATS_TIMETOFIRSTHEADER] = 0 ;
583+ }
584+ if (entry->first_byte_sent () != 0 ) {
585+ buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTESENT] =
586+ (entry->first_byte_sent () - entry->startTimeNano ()) / 1e6 ;
587+ } else {
588+ buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTESENT] = 0 ;
589+ }
590+ buffer[IDX_STREAM_STATS_SENTBYTES] = entry->sent_bytes ();
591+ buffer[IDX_STREAM_STATS_RECEIVEDBYTES] = entry->received_bytes ();
592+ entry->Notify (entry->ToObject ());
584593 }
585594 delete entry;
586595 }, static_cast <void *>(entry));
@@ -590,45 +599,25 @@ inline void Http2Session::EmitStatistics() {
590599 if (!HasHttp2Observer (env ()))
591600 return ;
592601 Http2SessionPerformanceEntry* entry =
593- new Http2SessionPerformanceEntry (env (), statistics_, TypeName () );
602+ new Http2SessionPerformanceEntry (env (), statistics_, session_type_ );
594603 env ()->SetImmediate ([](Environment* env, void * data) {
595- Local<Context> context = env->context ();
596604 Http2SessionPerformanceEntry* entry =
597605 static_cast <Http2SessionPerformanceEntry*>(data);
598606 if (HasHttp2Observer (env)) {
599- Local<Object> obj = entry->ToObject ();
600- v8::PropertyAttribute attr =
601- static_cast <v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
602- obj->DefineOwnProperty (
603- context,
604- FIXED_ONE_BYTE_STRING (env->isolate (), " type" ),
605- String::NewFromUtf8 (env->isolate (),
606- entry->typeName (),
607- v8::NewStringType::kInternalized )
608- .ToLocalChecked (), attr).FromJust ();
609- if (entry->ping_rtt () != 0 ) {
610- obj->DefineOwnProperty (
611- context,
612- FIXED_ONE_BYTE_STRING (env->isolate (), " pingRTT" ),
613- Number::New (env->isolate (), entry->ping_rtt () / 1e6 ),
614- attr).FromJust ();
615- }
616- obj->DefineOwnProperty (
617- context,
618- FIXED_ONE_BYTE_STRING (env->isolate (), " framesReceived" ),
619- Integer::NewFromUnsigned (env->isolate (), entry->frame_count ()),
620- attr).FromJust ();
621- obj->DefineOwnProperty (
622- context,
623- FIXED_ONE_BYTE_STRING (env->isolate (), " streamCount" ),
624- Integer::New (env->isolate (), entry->stream_count ()),
625- attr).FromJust ();
626- obj->DefineOwnProperty (
627- context,
628- FIXED_ONE_BYTE_STRING (env->isolate (), " streamAverageDuration" ),
629- Number::New (env->isolate (), entry->stream_average_duration ()),
630- attr).FromJust ();
631- entry->Notify (obj);
607+ AliasedBuffer<double , v8::Float64Array>& buffer =
608+ env->http2_state ()->session_stats_buffer ;
609+ buffer[IDX_SESSION_STATS_TYPE] = entry->type ();
610+ buffer[IDX_SESSION_STATS_PINGRTT] = entry->ping_rtt () / 1e6 ;
611+ buffer[IDX_SESSION_STATS_FRAMESRECEIVED] = entry->frame_count ();
612+ buffer[IDX_SESSION_STATS_FRAMESSENT] = entry->frame_sent ();
613+ buffer[IDX_SESSION_STATS_STREAMCOUNT] = entry->stream_count ();
614+ buffer[IDX_SESSION_STATS_STREAMAVERAGEDURATION] =
615+ entry->stream_average_duration ();
616+ buffer[IDX_SESSION_STATS_DATA_SENT] = entry->data_sent ();
617+ buffer[IDX_SESSION_STATS_DATA_RECEIVED] = entry->data_received ();
618+ buffer[IDX_SESSION_STATS_MAX_CONCURRENT_STREAMS] =
619+ entry->max_concurrent_streams ();
620+ entry->Notify (entry->ToObject ());
632621 }
633622 delete entry;
634623 }, static_cast <void *>(entry));
@@ -694,6 +683,9 @@ inline bool Http2Session::CanAddStream() {
694683inline void Http2Session::AddStream (Http2Stream* stream) {
695684 CHECK_GE (++statistics_.stream_count , 0 );
696685 streams_[stream->id ()] = stream;
686+ size_t size = streams_.size ();
687+ if (size > statistics_.max_concurrent_streams )
688+ statistics_.max_concurrent_streams = size;
697689 IncrementCurrentSessionMemory (stream->self_size ());
698690}
699691
@@ -962,6 +954,14 @@ inline int Http2Session::OnFrameNotSent(nghttp2_session* handle,
962954 return 0 ;
963955}
964956
957+ inline int Http2Session::OnFrameSent (nghttp2_session* handle,
958+ const nghttp2_frame* frame,
959+ void * user_data) {
960+ Http2Session* session = static_cast <Http2Session*>(user_data);
961+ session->statistics_ .frame_sent += 1 ;
962+ return 0 ;
963+ }
964+
965965// Called by nghttp2 when a stream closes.
966966inline int Http2Session::OnStreamClose (nghttp2_session* handle,
967967 int32_t id,
@@ -1039,6 +1039,7 @@ inline int Http2Session::OnDataChunkReceived(nghttp2_session* handle,
10391039 // If the stream has been destroyed, ignore this chunk
10401040 if (stream->IsDestroyed ())
10411041 return 0 ;
1042+ stream->statistics_ .received_bytes += len;
10421043 stream->AddChunk (data, len);
10431044 }
10441045 return 0 ;
@@ -1493,6 +1494,7 @@ void Http2Session::SendPendingData() {
14931494 size_t offset = 0 ;
14941495 size_t i = 0 ;
14951496 for (const nghttp2_stream_write& write : outgoing_buffers_) {
1497+ statistics_.data_sent += write.buf .len ;
14961498 if (write.buf .base == nullptr ) {
14971499 bufs[i++] = uv_buf_init (
14981500 reinterpret_cast <char *>(outgoing_storage_.data () + offset),
@@ -1642,6 +1644,7 @@ void Http2Session::OnStreamReadImpl(ssize_t nread,
16421644 if (bufs->len > 0 ) {
16431645 // Only pass data on if nread > 0
16441646 uv_buf_t buf[] { uv_buf_init ((*bufs).base , nread) };
1647+ session->statistics_ .data_received += nread;
16451648 ssize_t ret = session->Write (buf, 1 );
16461649
16471650 // Note: if ssize_t is not defined (e.g. on Win32), nghttp2 will typedef
@@ -2141,6 +2144,8 @@ ssize_t Http2Stream::Provider::FD::OnRead(nghttp2_session* handle,
21412144 void * user_data) {
21422145 Http2Session* session = static_cast <Http2Session*>(user_data);
21432146 Http2Stream* stream = session->FindStream (id);
2147+ if (stream->statistics_ .first_byte_sent == 0 )
2148+ stream->statistics_ .first_byte_sent = uv_hrtime ();
21442149
21452150 DEBUG_HTTP2SESSION2 (session, " reading outbound file data for stream %d" , id);
21462151 CHECK_EQ (id, stream->id ());
@@ -2191,6 +2196,7 @@ ssize_t Http2Stream::Provider::FD::OnRead(nghttp2_session* handle,
21912196 return NGHTTP2_ERR_CALLBACK_FAILURE;
21922197 }
21932198
2199+ stream->statistics_ .sent_bytes += numchars;
21942200 return numchars;
21952201}
21962202
@@ -2216,6 +2222,8 @@ ssize_t Http2Stream::Provider::Stream::OnRead(nghttp2_session* handle,
22162222 Http2Session* session = static_cast <Http2Session*>(user_data);
22172223 DEBUG_HTTP2SESSION2 (session, " reading outbound data for stream %d" , id);
22182224 Http2Stream* stream = GetStream (session, id, source);
2225+ if (stream->statistics_ .first_byte_sent == 0 )
2226+ stream->statistics_ .first_byte_sent = uv_hrtime ();
22192227 CHECK_EQ (id, stream->id ());
22202228
22212229 size_t amount = 0 ; // amount of data being sent in this data frame.
@@ -2249,6 +2257,8 @@ ssize_t Http2Stream::Provider::Stream::OnRead(nghttp2_session* handle,
22492257 if (session->IsDestroyed ())
22502258 return NGHTTP2_ERR_CALLBACK_FAILURE;
22512259 }
2260+
2261+ stream->statistics_ .sent_bytes += amount;
22522262 return amount;
22532263}
22542264
@@ -2862,6 +2872,10 @@ void Initialize(Local<Object> target,
28622872 " settingsBuffer" , state->settings_buffer .GetJSArray ());
28632873 SET_STATE_TYPEDARRAY (
28642874 " optionsBuffer" , state->options_buffer .GetJSArray ());
2875+ SET_STATE_TYPEDARRAY (
2876+ " streamStats" , state->stream_stats_buffer .GetJSArray ());
2877+ SET_STATE_TYPEDARRAY (
2878+ " sessionStats" , state->session_stats_buffer .GetJSArray ());
28652879#undef SET_STATE_TYPEDARRAY
28662880
28672881 env->set_http2_state (std::move (state));
0 commit comments