33#include " node_buffer.h"
44#include " node_http2.h"
55#include " node_http2_state.h"
6+ #include " node_perf.h"
67
78#include < algorithm>
89
@@ -20,6 +21,7 @@ using v8::Uint32;
2021using v8::Uint32Array;
2122using v8::Undefined;
2223
24+ using node::performance::PerformanceEntry;
2325namespace http2 {
2426
2527namespace {
@@ -468,6 +470,7 @@ Http2Session::Http2Session(Environment* env,
468470 : AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTP2SESSION),
469471 session_type_(type) {
470472 MakeWeak<Http2Session>(this );
473+ statistics_.start_time = uv_hrtime ();
471474
472475 // Capture the configuration options for this session
473476 Http2Options opts (env);
@@ -527,6 +530,86 @@ Http2Session::~Http2Session() {
527530 nghttp2_session_del (session_);
528531}
529532
533+ inline bool HasHttp2Observer (Environment* env) {
534+ uint32_t * observers = env->performance_state ()->observers ;
535+ return observers[performance::NODE_PERFORMANCE_ENTRY_TYPE_HTTP2] != 0 ;
536+ }
537+
538+ inline void Http2Stream::EmitStatistics () {
539+ if (!HasHttp2Observer (env ()))
540+ return ;
541+ Http2StreamPerformanceEntry* entry =
542+ new Http2StreamPerformanceEntry (env (), statistics_);
543+ env ()->SetImmediate ([](Environment* env, void * data) {
544+ Local<Context> context = env->context ();
545+ Http2StreamPerformanceEntry* entry =
546+ static_cast <Http2StreamPerformanceEntry*>(data);
547+ if (HasHttp2Observer (env)) {
548+ Local<Object> obj = entry->ToObject ();
549+ v8::PropertyAttribute attr =
550+ static_cast <v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
551+ obj->DefineOwnProperty (
552+ context,
553+ FIXED_ONE_BYTE_STRING (env->isolate (), " timeToFirstByte" ),
554+ Number::New (env->isolate (),
555+ (entry->first_byte () - entry->startTimeNano ()) / 1e6 ),
556+ attr);
557+ obj->DefineOwnProperty (
558+ context,
559+ FIXED_ONE_BYTE_STRING (env->isolate (), " timeToFirstHeader" ),
560+ Number::New (env->isolate (),
561+ (entry->first_header () - entry->startTimeNano ()) / 1e6 ),
562+ attr);
563+ entry->Notify (obj);
564+ }
565+ delete entry;
566+ }, static_cast <void *>(entry));
567+ }
568+
569+ inline void Http2Session::EmitStatistics () {
570+ if (!HasHttp2Observer (env ()))
571+ return ;
572+ Http2SessionPerformanceEntry* entry =
573+ new Http2SessionPerformanceEntry (env (), statistics_, TypeName ());
574+ env ()->SetImmediate ([](Environment* env, void * data) {
575+ Local<Context> context = env->context ();
576+ Http2SessionPerformanceEntry* entry =
577+ static_cast <Http2SessionPerformanceEntry*>(data);
578+ if (HasHttp2Observer (env)) {
579+ Local<Object> obj = entry->ToObject ();
580+ v8::PropertyAttribute attr =
581+ static_cast <v8::PropertyAttribute>(v8::ReadOnly | v8::DontDelete);
582+ obj->DefineOwnProperty (
583+ context,
584+ FIXED_ONE_BYTE_STRING (env->isolate (), " type" ),
585+ String::NewFromUtf8 (env->isolate (),
586+ entry->typeName (),
587+ v8::NewStringType::kInternalized )
588+ .ToLocalChecked (), attr);
589+ if (entry->ping_rtt () != 0 ) {
590+ obj->DefineOwnProperty (
591+ context,
592+ FIXED_ONE_BYTE_STRING (env->isolate (), " pingRTT" ),
593+ Number::New (env->isolate (), entry->ping_rtt () / 1e6 ), attr);
594+ }
595+ obj->DefineOwnProperty (
596+ context,
597+ FIXED_ONE_BYTE_STRING (env->isolate (), " framesReceived" ),
598+ Integer::NewFromUnsigned (env->isolate (), entry->frame_count ()), attr);
599+ obj->DefineOwnProperty (
600+ context,
601+ FIXED_ONE_BYTE_STRING (env->isolate (), " streamCount" ),
602+ Integer::New (env->isolate (), entry->stream_count ()), attr);
603+ obj->DefineOwnProperty (
604+ context,
605+ FIXED_ONE_BYTE_STRING (env->isolate (), " streamAverageDuration" ),
606+ Number::New (env->isolate (), entry->stream_average_duration ()), attr);
607+ entry->Notify (obj);
608+ }
609+ delete entry;
610+ }, static_cast <void *>(entry));
611+ }
612+
530613// Closes the session and frees the associated resources
531614void Http2Session::Close (uint32_t code, bool socket_closed) {
532615 DEBUG_HTTP2SESSION (this , " closing session" );
@@ -560,6 +643,9 @@ void Http2Session::Close(uint32_t code, bool socket_closed) {
560643 static_cast <Http2Session::Http2Ping*>(data)->Done (false );
561644 }, static_cast <void *>(ping));
562645 }
646+
647+ statistics_.end_time = uv_hrtime ();
648+ EmitStatistics ();
563649}
564650
565651// Locates an existing known stream by ID. nghttp2 has a similar method
@@ -571,6 +657,7 @@ inline Http2Stream* Http2Session::FindStream(int32_t id) {
571657
572658
573659inline void Http2Session::AddStream (Http2Stream* stream) {
660+ CHECK_GE (++statistics_.stream_count , 0 );
574661 streams_[stream->id ()] = stream;
575662}
576663
@@ -720,6 +807,7 @@ inline int Http2Session::OnFrameReceive(nghttp2_session* handle,
720807 const nghttp2_frame* frame,
721808 void * user_data) {
722809 Http2Session* session = static_cast <Http2Session*>(user_data);
810+ session->statistics_ .frame_count ++;
723811 DEBUG_HTTP2SESSION2 (session, " complete frame received: type: %d" ,
724812 frame->hd .type );
725813 switch (frame->hd .type ) {
@@ -1447,6 +1535,7 @@ Http2Stream::Http2Stream(
14471535 id_(id),
14481536 current_headers_category_(category) {
14491537 MakeWeak<Http2Stream>(this );
1538+ statistics_.start_time = uv_hrtime ();
14501539
14511540 // Limit the number of header pairs
14521541 max_header_pairs_ = session->GetMaxHeaderPairs ();
@@ -1530,6 +1619,8 @@ inline bool Http2Stream::HasDataChunks(bool ignore_eos) {
15301619// handles it's internal memory`.
15311620inline void Http2Stream::AddChunk (const uint8_t * data, size_t len) {
15321621 CHECK (!this ->IsDestroyed ());
1622+ if (this ->statistics_ .first_byte == 0 )
1623+ this ->statistics_ .first_byte = uv_hrtime ();
15331624 if (flags_ & NGHTTP2_STREAM_FLAG_EOS)
15341625 return ;
15351626 char * buf = nullptr ;
@@ -1590,7 +1681,6 @@ inline void Http2Stream::Destroy() {
15901681 // may still be some pending operations queued for this stream.
15911682 env ()->SetImmediate ([](Environment* env, void * data) {
15921683 Http2Stream* stream = static_cast <Http2Stream*>(data);
1593-
15941684 // Free any remaining outgoing data chunks here. This should be done
15951685 // here because it's possible for destroy to have been called while
15961686 // we still have qeueued outbound writes.
@@ -1603,6 +1693,12 @@ inline void Http2Stream::Destroy() {
16031693
16041694 delete stream;
16051695 }, this , this ->object ());
1696+
1697+ statistics_.end_time = uv_hrtime ();
1698+ session_->statistics_ .stream_average_duration =
1699+ ((statistics_.end_time - statistics_.start_time ) /
1700+ session_->statistics_ .stream_count ) / 1e6 ;
1701+ EmitStatistics ();
16061702}
16071703
16081704
@@ -1815,6 +1911,8 @@ inline bool Http2Stream::AddHeader(nghttp2_rcbuf* name,
18151911 nghttp2_rcbuf* value,
18161912 uint8_t flags) {
18171913 CHECK (!this ->IsDestroyed ());
1914+ if (this ->statistics_ .first_header == 0 )
1915+ this ->statistics_ .first_header = uv_hrtime ();
18181916 size_t length = GetBufferLength (name) + GetBufferLength (value) + 32 ;
18191917 if (current_headers_.size () == max_header_pairs_ ||
18201918 current_headers_length_ + length > max_header_length_) {
@@ -2493,8 +2591,8 @@ void Http2Session::Http2Ping::Send(uint8_t* payload) {
24932591}
24942592
24952593void Http2Session::Http2Ping::Done (bool ack, const uint8_t * payload) {
2496- uint64_t end = uv_hrtime ();
2497- double duration = (end - startTime_) / 1e6 ;
2594+ session_-> statistics_ . ping_rtt = ( uv_hrtime () - startTime_ );
2595+ double duration = (session_-> statistics_ . ping_rtt - startTime_) / 1e6 ;
24982596
24992597 Local<Value> buf = Undefined (env ()->isolate ());
25002598 if (payload != nullptr ) {
0 commit comments