@@ -146,6 +146,8 @@ struct aws_h2err s_decoder_on_goaway(
146146 uint32_t error_code ,
147147 struct aws_byte_cursor debug_data ,
148148 void * userdata );
149+ static void s_reset_statistics (struct aws_channel_handler * handler );
150+ static void s_gather_statistics (struct aws_channel_handler * handler , struct aws_array_list * stats );
149151
150152static struct aws_http_connection_vtable s_h2_connection_vtable = {
151153 .channel_handler_vtable =
@@ -157,6 +159,8 @@ static struct aws_http_connection_vtable s_h2_connection_vtable = {
157159 .initial_window_size = s_handler_initial_window_size ,
158160 .message_overhead = s_handler_message_overhead ,
159161 .destroy = s_handler_destroy ,
162+ .reset_statistics = s_reset_statistics ,
163+ .gather_statistics = s_gather_statistics ,
160164 },
161165
162166 .on_channel_handler_installed = s_handler_installed ,
@@ -219,6 +223,14 @@ static void s_release_stream_and_connection_lock(struct aws_h2_stream *stream, s
219223 (void )err ;
220224}
221225
226+ static void s_add_time_measurement_to_stats (uint64_t start_ns , uint64_t end_ns , uint64_t * output_ms ) {
227+ if (end_ns > start_ns ) {
228+ * output_ms += aws_timestamp_convert (end_ns - start_ns , AWS_TIMESTAMP_NANOS , AWS_TIMESTAMP_MILLIS , NULL );
229+ } else {
230+ * output_ms = 0 ;
231+ }
232+ }
233+
222234/**
223235 * Internal function for bringing connection to a stop.
224236 * Invoked multiple times, including when:
@@ -373,6 +385,9 @@ static struct aws_h2_connection *s_connection_new(
373385 connection -> thread_data .goaway_received_last_stream_id = AWS_H2_STREAM_ID_MAX ;
374386 connection -> thread_data .goaway_sent_last_stream_id = AWS_H2_STREAM_ID_MAX ;
375387
388+ aws_crt_statistics_http2_channel_init (& connection -> thread_data .stats );
389+ connection -> thread_data .stats .was_inactive = true; /* Start with non active streams */
390+
376391 connection -> synced_data .is_open = true;
377392 connection -> synced_data .new_stream_error_code = AWS_ERROR_SUCCESS ;
378393
@@ -795,6 +810,9 @@ static int s_encode_data_from_outgoing_streams(struct aws_h2_connection *connect
795810
796811 AWS_PRECONDITION (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
797812 struct aws_linked_list * outgoing_streams_list = & connection -> thread_data .outgoing_streams_list ;
813+ if (aws_linked_list_empty (outgoing_streams_list )) {
814+ return AWS_OP_SUCCESS ;
815+ }
798816 struct aws_linked_list * stalled_window_streams_list = & connection -> thread_data .stalled_window_streams_list ;
799817 struct aws_linked_list * waiting_streams_list = & connection -> thread_data .waiting_streams_list ;
800818
@@ -816,7 +834,7 @@ static int s_encode_data_from_outgoing_streams(struct aws_h2_connection *connect
816834 "Peer connection's flow-control window is too small now %zu. Connection will stop sending DATA until "
817835 "WINDOW_UPDATE is received." ,
818836 connection -> thread_data .window_size_peer );
819- break ;
837+ goto done ;
820838 }
821839
822840 /* Stop looping if message is so full it's not worth the bother */
@@ -885,6 +903,16 @@ static int s_encode_data_from_outgoing_streams(struct aws_h2_connection *connect
885903 return aws_raise_error (aws_error_code );
886904 }
887905
906+ if (aws_linked_list_empty (outgoing_streams_list )) {
907+ /* transition from something to write -> nothing to write */
908+ uint64_t now_ns = 0 ;
909+ aws_channel_current_clock_time (connection -> base .channel_slot -> channel , & now_ns );
910+ s_add_time_measurement_to_stats (
911+ connection -> thread_data .outgoing_timestamp_ns ,
912+ now_ns ,
913+ & connection -> thread_data .stats .pending_outgoing_stream_ms );
914+ }
915+
888916 return AWS_OP_SUCCESS ;
889917}
890918
@@ -1768,6 +1796,19 @@ static void s_stream_complete(struct aws_h2_connection *connection, struct aws_h
17681796 aws_linked_list_remove (& stream -> node );
17691797 }
17701798
1799+ if (aws_hash_table_get_entry_count (& connection -> thread_data .active_streams_map ) == 0 &&
1800+ connection -> thread_data .incoming_timestamp_ns != 0 ) {
1801+ uint64_t now_ns = 0 ;
1802+ aws_channel_current_clock_time (connection -> base .channel_slot -> channel , & now_ns );
1803+ /* transition from something to read -> nothing to read and nothing to write */
1804+ s_add_time_measurement_to_stats (
1805+ connection -> thread_data .incoming_timestamp_ns ,
1806+ now_ns ,
1807+ & connection -> thread_data .stats .pending_incoming_stream_ms );
1808+ connection -> thread_data .stats .was_inactive = true;
1809+ connection -> thread_data .incoming_timestamp_ns = 0 ;
1810+ }
1811+
17711812 aws_h2_stream_complete (stream , error_code );
17721813
17731814 /* release connection's hold on stream */
@@ -1867,6 +1908,14 @@ static void s_move_stream_to_thread(
18671908 if (aws_h2_stream_on_activated (stream , & body_state )) {
18681909 goto error ;
18691910 }
1911+
1912+ if (aws_hash_table_get_entry_count (& connection -> thread_data .active_streams_map ) == 1 ) {
1913+ /* transition from nothing to read -> something to read */
1914+ uint64_t now_ns = 0 ;
1915+ aws_channel_current_clock_time (connection -> base .channel_slot -> channel , & now_ns );
1916+ connection -> thread_data .incoming_timestamp_ns = now_ns ;
1917+ }
1918+
18701919 switch (body_state ) {
18711920 case AWS_H2_STREAM_BODY_STATE_WAITING_WRITES :
18721921 aws_linked_list_push_back (& connection -> thread_data .waiting_streams_list , & stream -> node );
@@ -2753,3 +2802,49 @@ static size_t s_handler_message_overhead(struct aws_channel_handler *handler) {
27532802 /* "All frames begin with a fixed 9-octet header followed by a variable-length payload" (RFC-7540 4.1) */
27542803 return 9 ;
27552804}
2805+
2806+ static void s_reset_statistics (struct aws_channel_handler * handler ) {
2807+ struct aws_h2_connection * connection = handler -> impl ;
2808+ aws_crt_statistics_http2_channel_reset (& connection -> thread_data .stats );
2809+ if (aws_hash_table_get_entry_count (& connection -> thread_data .active_streams_map ) == 0 ) {
2810+ /* Check the current state */
2811+ connection -> thread_data .stats .was_inactive = true;
2812+ }
2813+ return ;
2814+ }
2815+
2816+ static void s_gather_statistics (struct aws_channel_handler * handler , struct aws_array_list * stats ) {
2817+
2818+ struct aws_h2_connection * connection = handler -> impl ;
2819+ AWS_PRECONDITION (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
2820+
2821+ /* TODO: Need update the way we calculate statistics, to account for user-controlled pauses.
2822+ * If user is adding chunks 1 by 1, there can naturally be a gap in the upload.
2823+ * If the user lets the stream-window go to zero, there can naturally be a gap in the download. */
2824+ uint64_t now_ns = 0 ;
2825+ if (aws_channel_current_clock_time (connection -> base .channel_slot -> channel , & now_ns )) {
2826+ return ;
2827+ }
2828+
2829+ if (!aws_linked_list_empty (& connection -> thread_data .outgoing_streams_list )) {
2830+ s_add_time_measurement_to_stats (
2831+ connection -> thread_data .outgoing_timestamp_ns ,
2832+ now_ns ,
2833+ & connection -> thread_data .stats .pending_outgoing_stream_ms );
2834+
2835+ connection -> thread_data .outgoing_timestamp_ns = now_ns ;
2836+ }
2837+ if (aws_hash_table_get_entry_count (& connection -> thread_data .active_streams_map ) != 0 ) {
2838+ s_add_time_measurement_to_stats (
2839+ connection -> thread_data .incoming_timestamp_ns ,
2840+ now_ns ,
2841+ & connection -> thread_data .stats .pending_incoming_stream_ms );
2842+
2843+ connection -> thread_data .incoming_timestamp_ns = now_ns ;
2844+ } else {
2845+ connection -> thread_data .stats .was_inactive = true;
2846+ }
2847+
2848+ void * stats_base = & connection -> thread_data .stats ;
2849+ aws_array_list_push_back (stats , & stats_base );
2850+ }
0 commit comments