3
3
#include " node_buffer.h"
4
4
#include " node_http2.h"
5
5
#include " node_http2_state.h"
6
+ #include " node_perf.h"
6
7
7
8
#include < algorithm>
8
9
@@ -20,6 +21,7 @@ using v8::Uint32;
20
21
using v8::Uint32Array;
21
22
using v8::Undefined;
22
23
24
+ using node::performance::PerformanceEntry;
23
25
namespace http2 {
24
26
25
27
namespace {
@@ -468,6 +470,7 @@ Http2Session::Http2Session(Environment* env,
468
470
: AsyncWrap(env, wrap, AsyncWrap::PROVIDER_HTTP2SESSION),
469
471
session_type_(type) {
470
472
MakeWeak<Http2Session>(this );
473
+ statistics_.start_time = uv_hrtime ();
471
474
472
475
// Capture the configuration options for this session
473
476
Http2Options opts (env);
@@ -527,6 +530,86 @@ Http2Session::~Http2Session() {
527
530
nghttp2_session_del (session_);
528
531
}
529
532
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
+
530
613
// Closes the session and frees the associated resources
531
614
void Http2Session::Close (uint32_t code, bool socket_closed) {
532
615
DEBUG_HTTP2SESSION (this , " closing session" );
@@ -560,6 +643,9 @@ void Http2Session::Close(uint32_t code, bool socket_closed) {
560
643
static_cast <Http2Session::Http2Ping*>(data)->Done (false );
561
644
}, static_cast <void *>(ping));
562
645
}
646
+
647
+ statistics_.end_time = uv_hrtime ();
648
+ EmitStatistics ();
563
649
}
564
650
565
651
// Locates an existing known stream by ID. nghttp2 has a similar method
@@ -571,6 +657,7 @@ inline Http2Stream* Http2Session::FindStream(int32_t id) {
571
657
572
658
573
659
inline void Http2Session::AddStream (Http2Stream* stream) {
660
+ CHECK_GE (++statistics_.stream_count , 0 );
574
661
streams_[stream->id ()] = stream;
575
662
}
576
663
@@ -720,6 +807,7 @@ inline int Http2Session::OnFrameReceive(nghttp2_session* handle,
720
807
const nghttp2_frame* frame,
721
808
void * user_data) {
722
809
Http2Session* session = static_cast <Http2Session*>(user_data);
810
+ session->statistics_ .frame_count ++;
723
811
DEBUG_HTTP2SESSION2 (session, " complete frame received: type: %d" ,
724
812
frame->hd .type );
725
813
switch (frame->hd .type ) {
@@ -1447,6 +1535,7 @@ Http2Stream::Http2Stream(
1447
1535
id_(id),
1448
1536
current_headers_category_(category) {
1449
1537
MakeWeak<Http2Stream>(this );
1538
+ statistics_.start_time = uv_hrtime ();
1450
1539
1451
1540
// Limit the number of header pairs
1452
1541
max_header_pairs_ = session->GetMaxHeaderPairs ();
@@ -1530,6 +1619,8 @@ inline bool Http2Stream::HasDataChunks(bool ignore_eos) {
1530
1619
// handles it's internal memory`.
1531
1620
inline void Http2Stream::AddChunk (const uint8_t * data, size_t len) {
1532
1621
CHECK (!this ->IsDestroyed ());
1622
+ if (this ->statistics_ .first_byte == 0 )
1623
+ this ->statistics_ .first_byte = uv_hrtime ();
1533
1624
if (flags_ & NGHTTP2_STREAM_FLAG_EOS)
1534
1625
return ;
1535
1626
char * buf = nullptr ;
@@ -1590,7 +1681,6 @@ inline void Http2Stream::Destroy() {
1590
1681
// may still be some pending operations queued for this stream.
1591
1682
env ()->SetImmediate ([](Environment* env, void * data) {
1592
1683
Http2Stream* stream = static_cast <Http2Stream*>(data);
1593
-
1594
1684
// Free any remaining outgoing data chunks here. This should be done
1595
1685
// here because it's possible for destroy to have been called while
1596
1686
// we still have qeueued outbound writes.
@@ -1603,6 +1693,12 @@ inline void Http2Stream::Destroy() {
1603
1693
1604
1694
delete stream;
1605
1695
}, 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 ();
1606
1702
}
1607
1703
1608
1704
@@ -1815,6 +1911,8 @@ inline bool Http2Stream::AddHeader(nghttp2_rcbuf* name,
1815
1911
nghttp2_rcbuf* value,
1816
1912
uint8_t flags) {
1817
1913
CHECK (!this ->IsDestroyed ());
1914
+ if (this ->statistics_ .first_header == 0 )
1915
+ this ->statistics_ .first_header = uv_hrtime ();
1818
1916
size_t length = GetBufferLength (name) + GetBufferLength (value) + 32 ;
1819
1917
if (current_headers_.size () == max_header_pairs_ ||
1820
1918
current_headers_length_ + length > max_header_length_) {
@@ -2493,8 +2591,8 @@ void Http2Session::Http2Ping::Send(uint8_t* payload) {
2493
2591
}
2494
2592
2495
2593
void 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 ;
2498
2596
2499
2597
Local<Value> buf = Undefined (env ()->isolate ());
2500
2598
if (payload != nullptr ) {
0 commit comments