@@ -146,6 +146,8 @@ struct aws_h2err s_decoder_on_goaway(
146
146
uint32_t error_code ,
147
147
struct aws_byte_cursor debug_data ,
148
148
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 );
149
151
150
152
static struct aws_http_connection_vtable s_h2_connection_vtable = {
151
153
.channel_handler_vtable =
@@ -157,6 +159,8 @@ static struct aws_http_connection_vtable s_h2_connection_vtable = {
157
159
.initial_window_size = s_handler_initial_window_size ,
158
160
.message_overhead = s_handler_message_overhead ,
159
161
.destroy = s_handler_destroy ,
162
+ .reset_statistics = s_reset_statistics ,
163
+ .gather_statistics = s_gather_statistics ,
160
164
},
161
165
162
166
.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
219
223
(void )err ;
220
224
}
221
225
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
+
222
234
/**
223
235
* Internal function for bringing connection to a stop.
224
236
* Invoked multiple times, including when:
@@ -373,6 +385,9 @@ static struct aws_h2_connection *s_connection_new(
373
385
connection -> thread_data .goaway_received_last_stream_id = AWS_H2_STREAM_ID_MAX ;
374
386
connection -> thread_data .goaway_sent_last_stream_id = AWS_H2_STREAM_ID_MAX ;
375
387
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
+
376
391
connection -> synced_data .is_open = true;
377
392
connection -> synced_data .new_stream_error_code = AWS_ERROR_SUCCESS ;
378
393
@@ -795,6 +810,9 @@ static int s_encode_data_from_outgoing_streams(struct aws_h2_connection *connect
795
810
796
811
AWS_PRECONDITION (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
797
812
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
+ }
798
816
struct aws_linked_list * stalled_window_streams_list = & connection -> thread_data .stalled_window_streams_list ;
799
817
struct aws_linked_list * waiting_streams_list = & connection -> thread_data .waiting_streams_list ;
800
818
@@ -816,7 +834,7 @@ static int s_encode_data_from_outgoing_streams(struct aws_h2_connection *connect
816
834
"Peer connection's flow-control window is too small now %zu. Connection will stop sending DATA until "
817
835
"WINDOW_UPDATE is received." ,
818
836
connection -> thread_data .window_size_peer );
819
- break ;
837
+ goto done ;
820
838
}
821
839
822
840
/* 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
885
903
return aws_raise_error (aws_error_code );
886
904
}
887
905
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
+
888
916
return AWS_OP_SUCCESS ;
889
917
}
890
918
@@ -1768,6 +1796,19 @@ static void s_stream_complete(struct aws_h2_connection *connection, struct aws_h
1768
1796
aws_linked_list_remove (& stream -> node );
1769
1797
}
1770
1798
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
+
1771
1812
aws_h2_stream_complete (stream , error_code );
1772
1813
1773
1814
/* release connection's hold on stream */
@@ -1867,6 +1908,14 @@ static void s_move_stream_to_thread(
1867
1908
if (aws_h2_stream_on_activated (stream , & body_state )) {
1868
1909
goto error ;
1869
1910
}
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
+
1870
1919
switch (body_state ) {
1871
1920
case AWS_H2_STREAM_BODY_STATE_WAITING_WRITES :
1872
1921
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) {
2753
2802
/* "All frames begin with a fixed 9-octet header followed by a variable-length payload" (RFC-7540 4.1) */
2754
2803
return 9 ;
2755
2804
}
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