@@ -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 (
@@ -560,28 +562,35 @@ inline void Http2Stream::EmitStatistics() {
560562 if (!HasHttp2Observer (env ()))
561563 return ;
562564 Http2StreamPerformanceEntry* entry =
563- new Http2StreamPerformanceEntry (env (), statistics_);
565+ new Http2StreamPerformanceEntry (env (), id_, statistics_);
564566 env ()->SetImmediate ([](Environment* env, void * data) {
565- Local<Context> context = env->context ();
566567 Http2StreamPerformanceEntry* entry =
567568 static_cast <Http2StreamPerformanceEntry*>(data);
568569 if (HasHttp2Observer (env)) {
569- Local<Object> obj = entry->ToObject ();
570- v8::PropertyAttribute attr =
571- static_cast <v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
572- obj->DefineOwnProperty (
573- context,
574- FIXED_ONE_BYTE_STRING (env->isolate (), " timeToFirstByte" ),
575- Number::New (env->isolate (),
576- (entry->first_byte () - entry->startTimeNano ()) / 1e6 ),
577- attr).FromJust ();
578- obj->DefineOwnProperty (
579- context,
580- FIXED_ONE_BYTE_STRING (env->isolate (), " timeToFirstHeader" ),
581- Number::New (env->isolate (),
582- (entry->first_header () - entry->startTimeNano ()) / 1e6 ),
583- attr).FromJust ();
584- entry->Notify (obj);
570+ AliasedBuffer<double , v8::Float64Array>& buffer =
571+ env->http2_state ()->stream_stats_buffer ;
572+ buffer[IDX_STREAM_STATS_ID] = entry->id ();
573+ if (entry->first_byte () != 0 ) {
574+ buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTE] =
575+ (entry->first_byte () - entry->startTimeNano ()) / 1e6 ;
576+ } else {
577+ buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTE] = 0 ;
578+ }
579+ if (entry->first_header () != 0 ) {
580+ buffer[IDX_STREAM_STATS_TIMETOFIRSTHEADER] =
581+ (entry->first_header () - entry->startTimeNano ()) / 1e6 ;
582+ } else {
583+ buffer[IDX_STREAM_STATS_TIMETOFIRSTHEADER] = 0 ;
584+ }
585+ if (entry->first_byte_sent () != 0 ) {
586+ buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTESENT] =
587+ (entry->first_byte_sent () - entry->startTimeNano ()) / 1e6 ;
588+ } else {
589+ buffer[IDX_STREAM_STATS_TIMETOFIRSTBYTESENT] = 0 ;
590+ }
591+ buffer[IDX_STREAM_STATS_SENTBYTES] = entry->sent_bytes ();
592+ buffer[IDX_STREAM_STATS_RECEIVEDBYTES] = entry->received_bytes ();
593+ entry->Notify (entry->ToObject ());
585594 }
586595 delete entry;
587596 }, static_cast <void *>(entry));
@@ -591,45 +600,25 @@ inline void Http2Session::EmitStatistics() {
591600 if (!HasHttp2Observer (env ()))
592601 return ;
593602 Http2SessionPerformanceEntry* entry =
594- new Http2SessionPerformanceEntry (env (), statistics_, TypeName () );
603+ new Http2SessionPerformanceEntry (env (), statistics_, session_type_ );
595604 env ()->SetImmediate ([](Environment* env, void * data) {
596- Local<Context> context = env->context ();
597605 Http2SessionPerformanceEntry* entry =
598606 static_cast <Http2SessionPerformanceEntry*>(data);
599607 if (HasHttp2Observer (env)) {
600- Local<Object> obj = entry->ToObject ();
601- v8::PropertyAttribute attr =
602- static_cast <v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
603- obj->DefineOwnProperty (
604- context,
605- FIXED_ONE_BYTE_STRING (env->isolate (), " type" ),
606- String::NewFromUtf8 (env->isolate (),
607- entry->typeName (),
608- v8::NewStringType::kInternalized )
609- .ToLocalChecked (), attr).FromJust ();
610- if (entry->ping_rtt () != 0 ) {
611- obj->DefineOwnProperty (
612- context,
613- FIXED_ONE_BYTE_STRING (env->isolate (), " pingRTT" ),
614- Number::New (env->isolate (), entry->ping_rtt () / 1e6 ),
615- attr).FromJust ();
616- }
617- obj->DefineOwnProperty (
618- context,
619- FIXED_ONE_BYTE_STRING (env->isolate (), " framesReceived" ),
620- Integer::NewFromUnsigned (env->isolate (), entry->frame_count ()),
621- attr).FromJust ();
622- obj->DefineOwnProperty (
623- context,
624- FIXED_ONE_BYTE_STRING (env->isolate (), " streamCount" ),
625- Integer::New (env->isolate (), entry->stream_count ()),
626- attr).FromJust ();
627- obj->DefineOwnProperty (
628- context,
629- FIXED_ONE_BYTE_STRING (env->isolate (), " streamAverageDuration" ),
630- Number::New (env->isolate (), entry->stream_average_duration ()),
631- attr).FromJust ();
632- entry->Notify (obj);
608+ AliasedBuffer<double , v8::Float64Array>& buffer =
609+ env->http2_state ()->session_stats_buffer ;
610+ buffer[IDX_SESSION_STATS_TYPE] = entry->type ();
611+ buffer[IDX_SESSION_STATS_PINGRTT] = entry->ping_rtt () / 1e6 ;
612+ buffer[IDX_SESSION_STATS_FRAMESRECEIVED] = entry->frame_count ();
613+ buffer[IDX_SESSION_STATS_FRAMESSENT] = entry->frame_sent ();
614+ buffer[IDX_SESSION_STATS_STREAMCOUNT] = entry->stream_count ();
615+ buffer[IDX_SESSION_STATS_STREAMAVERAGEDURATION] =
616+ entry->stream_average_duration ();
617+ buffer[IDX_SESSION_STATS_DATA_SENT] = entry->data_sent ();
618+ buffer[IDX_SESSION_STATS_DATA_RECEIVED] = entry->data_received ();
619+ buffer[IDX_SESSION_STATS_MAX_CONCURRENT_STREAMS] =
620+ entry->max_concurrent_streams ();
621+ entry->Notify (entry->ToObject ());
633622 }
634623 delete entry;
635624 }, static_cast <void *>(entry));
@@ -695,6 +684,9 @@ inline bool Http2Session::CanAddStream() {
695684inline void Http2Session::AddStream (Http2Stream* stream) {
696685 CHECK_GE (++statistics_.stream_count , 0 );
697686 streams_[stream->id ()] = stream;
687+ size_t size = streams_.size ();
688+ if (size > statistics_.max_concurrent_streams )
689+ statistics_.max_concurrent_streams = size;
698690 IncrementCurrentSessionMemory (stream->self_size ());
699691}
700692
@@ -963,6 +955,14 @@ inline int Http2Session::OnFrameNotSent(nghttp2_session* handle,
963955 return 0 ;
964956}
965957
958+ inline int Http2Session::OnFrameSent (nghttp2_session* handle,
959+ const nghttp2_frame* frame,
960+ void * user_data) {
961+ Http2Session* session = static_cast <Http2Session*>(user_data);
962+ session->statistics_ .frame_sent += 1 ;
963+ return 0 ;
964+ }
965+
966966// Called by nghttp2 when a stream closes.
967967inline int Http2Session::OnStreamClose (nghttp2_session* handle,
968968 int32_t id,
@@ -1040,6 +1040,7 @@ inline int Http2Session::OnDataChunkReceived(nghttp2_session* handle,
10401040 // If the stream has been destroyed, ignore this chunk
10411041 if (stream->IsDestroyed ())
10421042 return 0 ;
1043+ stream->statistics_ .received_bytes += len;
10431044 stream->AddChunk (data, len);
10441045 }
10451046 return 0 ;
@@ -1494,6 +1495,7 @@ void Http2Session::SendPendingData() {
14941495 size_t offset = 0 ;
14951496 size_t i = 0 ;
14961497 for (const nghttp2_stream_write& write : outgoing_buffers_) {
1498+ statistics_.data_sent += write.buf .len ;
14971499 if (write.buf .base == nullptr ) {
14981500 bufs[i++] = uv_buf_init (
14991501 reinterpret_cast <char *>(outgoing_storage_.data () + offset),
@@ -1643,6 +1645,7 @@ void Http2Session::OnStreamReadImpl(ssize_t nread,
16431645 if (bufs->len > 0 ) {
16441646 // Only pass data on if nread > 0
16451647 uv_buf_t buf[] { uv_buf_init ((*bufs).base , nread) };
1648+ session->statistics_ .data_received += nread;
16461649 ssize_t ret = session->Write (buf, 1 );
16471650
16481651 // Note: if ssize_t is not defined (e.g. on Win32), nghttp2 will typedef
@@ -2142,6 +2145,8 @@ ssize_t Http2Stream::Provider::FD::OnRead(nghttp2_session* handle,
21422145 void * user_data) {
21432146 Http2Session* session = static_cast <Http2Session*>(user_data);
21442147 Http2Stream* stream = session->FindStream (id);
2148+ if (stream->statistics_ .first_byte_sent == 0 )
2149+ stream->statistics_ .first_byte_sent = uv_hrtime ();
21452150
21462151 DEBUG_HTTP2SESSION2 (session, " reading outbound file data for stream %d" , id);
21472152 CHECK_EQ (id, stream->id ());
@@ -2192,6 +2197,7 @@ ssize_t Http2Stream::Provider::FD::OnRead(nghttp2_session* handle,
21922197 return NGHTTP2_ERR_CALLBACK_FAILURE;
21932198 }
21942199
2200+ stream->statistics_ .sent_bytes += numchars;
21952201 return numchars;
21962202}
21972203
@@ -2217,6 +2223,8 @@ ssize_t Http2Stream::Provider::Stream::OnRead(nghttp2_session* handle,
22172223 Http2Session* session = static_cast <Http2Session*>(user_data);
22182224 DEBUG_HTTP2SESSION2 (session, " reading outbound data for stream %d" , id);
22192225 Http2Stream* stream = GetStream (session, id, source);
2226+ if (stream->statistics_ .first_byte_sent == 0 )
2227+ stream->statistics_ .first_byte_sent = uv_hrtime ();
22202228 CHECK_EQ (id, stream->id ());
22212229
22222230 size_t amount = 0 ; // amount of data being sent in this data frame.
@@ -2250,6 +2258,8 @@ ssize_t Http2Stream::Provider::Stream::OnRead(nghttp2_session* handle,
22502258 if (session->IsDestroyed ())
22512259 return NGHTTP2_ERR_CALLBACK_FAILURE;
22522260 }
2261+
2262+ stream->statistics_ .sent_bytes += amount;
22532263 return amount;
22542264}
22552265
@@ -2863,6 +2873,10 @@ void Initialize(Local<Object> target,
28632873 " settingsBuffer" , state->settings_buffer .GetJSArray ());
28642874 SET_STATE_TYPEDARRAY (
28652875 " optionsBuffer" , state->options_buffer .GetJSArray ());
2876+ SET_STATE_TYPEDARRAY (
2877+ " streamStats" , state->stream_stats_buffer .GetJSArray ());
2878+ SET_STATE_TYPEDARRAY (
2879+ " sessionStats" , state->session_stats_buffer .GetJSArray ());
28662880#undef SET_STATE_TYPEDARRAY
28672881
28682882 env->set_http2_state (std::move (state));
0 commit comments