@@ -68,6 +68,7 @@ static int s_record_closed_stream(
6868 struct aws_h2_connection * connection ,
6969 uint32_t stream_id ,
7070 enum aws_h2_stream_closed_when closed_when );
71+ static void s_try_write_outgoing_frames (struct aws_h2_connection * connection );
7172
7273static struct aws_h2err s_decoder_on_headers_begin (uint32_t stream_id , void * userdata );
7374static struct aws_h2err s_decoder_on_headers_i (
@@ -239,6 +240,7 @@ static struct aws_h2_connection *s_connection_new(
239240 aws_linked_list_init (& connection -> synced_data .pending_stream_list );
240241
241242 aws_linked_list_init (& connection -> thread_data .outgoing_streams_list );
243+ aws_linked_list_init (& connection -> thread_data .pending_settings_queue );
242244 aws_linked_list_init (& connection -> thread_data .stalled_window_streams_list );
243245 aws_linked_list_init (& connection -> thread_data .outgoing_frames_queue );
244246
@@ -340,6 +342,12 @@ struct aws_http_connection *aws_http_connection_new_http2_client(
340342 return & connection -> base ;
341343}
342344
345+ struct h2_pending_settings {
346+ struct aws_h2_frame_setting * settings_array ;
347+ size_t num_settings ;
348+ struct aws_linked_list_node node ;
349+ };
350+
343351static void s_handler_destroy (struct aws_channel_handler * handler ) {
344352 struct aws_h2_connection * connection = handler -> impl ;
345353 CONNECTION_LOG (TRACE , connection , "Destroying connection" );
@@ -348,6 +356,12 @@ static void s_handler_destroy(struct aws_channel_handler *handler) {
348356 AWS_ASSERT (
349357 !aws_hash_table_is_valid (& connection -> thread_data .active_streams_map ) ||
350358 aws_hash_table_get_entry_count (& connection -> thread_data .active_streams_map ) == 0 );
359+ while (!aws_linked_list_empty (& connection -> thread_data .pending_settings_queue )) {
360+ /* Some settings are sent, but peer never sends ACK back. It's not an error. We just have to clean it up */
361+ struct aws_linked_list_node * node = aws_linked_list_pop_front (& connection -> thread_data .pending_settings_queue );
362+ struct h2_pending_settings * pending_settings = AWS_CONTAINER_OF (node , struct h2_pending_settings , node );
363+ aws_mem_release (connection -> base .alloc , pending_settings );
364+ }
351365 AWS_ASSERT (aws_linked_list_empty (& connection -> thread_data .stalled_window_streams_list ));
352366 AWS_ASSERT (aws_linked_list_empty (& connection -> thread_data .outgoing_streams_list ));
353367 AWS_ASSERT (aws_linked_list_empty (& connection -> synced_data .pending_stream_list ));
@@ -368,6 +382,65 @@ static void s_handler_destroy(struct aws_channel_handler *handler) {
368382 aws_mem_release (connection -> base .alloc , connection );
369383}
370384
385+ static struct h2_pending_settings * s_new_pending_settings (
386+ struct aws_allocator * allocator ,
387+ const struct aws_h2_frame_setting * settings_array ,
388+ size_t num_settings ) {
389+
390+ size_t settings_storage_size = sizeof (struct aws_h2_frame_setting ) * num_settings ;
391+ struct h2_pending_settings * pending_settings ;
392+ void * settings_storage ;
393+ if (!aws_mem_acquire_many (
394+ allocator ,
395+ 2 ,
396+ & pending_settings ,
397+ sizeof (struct h2_pending_settings ),
398+ & settings_storage ,
399+ settings_storage_size )) {
400+ return NULL ;
401+ }
402+
403+ AWS_ZERO_STRUCT (* pending_settings );
404+ /* We buffer the settings up, incase the caller has freed them when the ACK arrives */
405+ pending_settings -> settings_array = settings_storage ;
406+ memcpy (pending_settings -> settings_array , settings_array , num_settings * sizeof (* settings_array ));
407+ pending_settings -> num_settings = num_settings ;
408+
409+ return pending_settings ;
410+ }
411+
412+ int aws_h2_connection_change_settings (
413+ struct aws_h2_connection * connection ,
414+ const struct aws_h2_frame_setting * settings_array ,
415+ size_t num_settings ) {
416+ AWS_PRECONDITION (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
417+ AWS_PRECONDITION (settings_array );
418+
419+ if (!num_settings ) {
420+ return AWS_OP_SUCCESS ;
421+ }
422+
423+ /* push the setting array into the queue, not applying the change until it is ACKed by peer */
424+ struct h2_pending_settings * pending_settings =
425+ s_new_pending_settings (connection -> base .alloc , settings_array , num_settings );
426+ if (!pending_settings ) {
427+ return AWS_OP_ERR ;
428+ }
429+ /* Send setting frame to inform our peer */
430+ struct aws_h2_frame * setting_frame =
431+ aws_h2_frame_new_settings (connection -> base .alloc , settings_array , num_settings , false /*ACK*/ );
432+ if (!setting_frame ) {
433+ CONNECTION_LOGF (ERROR , connection , "Failed to send setting_frames, error %s" , aws_error_name (aws_last_error ()));
434+ aws_mem_release (connection -> base .alloc , pending_settings );
435+ return AWS_OP_ERR ;
436+ }
437+ aws_h2_connection_enqueue_outgoing_frame (connection , setting_frame );
438+
439+ aws_linked_list_push_back (& connection -> thread_data .pending_settings_queue , & pending_settings -> node );
440+ s_try_write_outgoing_frames (connection );
441+ return AWS_OP_SUCCESS ;
442+ }
443+
371444void aws_h2_connection_enqueue_outgoing_frame (struct aws_h2_connection * connection , struct aws_h2_frame * frame ) {
372445 AWS_PRECONDITION (frame -> type != AWS_H2_FRAME_T_DATA );
373446 AWS_PRECONDITION (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
@@ -991,6 +1064,7 @@ static struct aws_h2err s_decoder_on_settings(
9911064 size_t num_settings ,
9921065 void * userdata ) {
9931066 struct aws_h2_connection * connection = userdata ;
1067+ struct aws_h2err err ;
9941068 /* Once all values have been processed, the recipient MUST immediately emit a SETTINGS frame with the ACK flag
9951069 * set.(RFC-7540 6.5.3) */
9961070 CONNECTION_LOG (TRACE , connection , "Setting frame processing ends" );
@@ -1009,12 +1083,9 @@ static struct aws_h2err s_decoder_on_settings(
10091083 continue ;
10101084 }
10111085 switch (settings_array [i ].id ) {
1012- case AWS_H2_SETTINGS_HEADER_TABLE_SIZE :
1086+ case AWS_H2_SETTINGS_HEADER_TABLE_SIZE : {
10131087 aws_h2_frame_encoder_set_setting_header_table_size (encoder , settings_array [i ].value );
1014- break ;
1015- case AWS_H2_SETTINGS_MAX_FRAME_SIZE :
1016- aws_h2_frame_encoder_set_setting_max_frame_size (encoder , settings_array [i ].value );
1017- break ;
1088+ } break ;
10181089 case AWS_H2_SETTINGS_INITIAL_WINDOW_SIZE : {
10191090 /* When the value of SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver MUST adjust the size of all stream
10201091 * flow-control windows that it maintains by the difference between the new value and the old value. */
@@ -1024,16 +1095,20 @@ static struct aws_h2err s_decoder_on_settings(
10241095 while (!aws_hash_iter_done (& stream_iter )) {
10251096 struct aws_h2_stream * stream = stream_iter .element .value ;
10261097 aws_hash_iter_next (& stream_iter );
1027- if (aws_h2_stream_window_size_change (stream , size_changed )) {
1098+ err = aws_h2_stream_window_size_change (stream , size_changed , false /*self*/ );
1099+ if (aws_h2err_failed (err )) {
10281100 CONNECTION_LOG (
10291101 ERROR ,
10301102 connection ,
10311103 "Connection error, change to SETTINGS_INITIAL_WINDOW_SIZE caused a stream's flow-control "
10321104 "window to exceed the maximum size" );
1033- return aws_h2err_from_h2_code ( AWS_H2_ERR_FLOW_CONTROL_ERROR ) ;
1105+ return err ;
10341106 }
10351107 }
10361108 } break ;
1109+ case AWS_H2_SETTINGS_MAX_FRAME_SIZE : {
1110+ aws_h2_frame_encoder_set_setting_max_frame_size (encoder , settings_array [i ].value );
1111+ } break ;
10371112 }
10381113 connection -> thread_data .settings_peer [settings_array [i ].id ] = settings_array [i ].value ;
10391114 }
@@ -1042,14 +1117,58 @@ static struct aws_h2err s_decoder_on_settings(
10421117
10431118static struct aws_h2err s_decoder_on_settings_ack (void * userdata ) {
10441119 struct aws_h2_connection * connection = userdata ;
1045- /* #TODO track which SETTINGS frames is ACKed by this */
1046-
1047- /* inform decoder about the settings */
1120+ if (aws_linked_list_empty (& connection -> thread_data .pending_settings_queue )) {
1121+ CONNECTION_LOG (ERROR , connection , "Connection error, received a malicious extra SETTINGS acknowledgement" );
1122+ return aws_h2err_from_h2_code (AWS_H2_ERR_PROTOCOL_ERROR );
1123+ }
1124+ struct aws_h2err err ;
1125+ struct aws_linked_list_node * node = aws_linked_list_front (& connection -> thread_data .pending_settings_queue );
1126+ struct h2_pending_settings * pending_settings = AWS_CONTAINER_OF (node , struct h2_pending_settings , node );
1127+ struct aws_h2_frame_setting * settings_array = pending_settings -> settings_array ;
1128+ /* Apply the settings */
10481129 struct aws_h2_decoder * decoder = connection -> thread_data .decoder ;
1049- uint32_t * settings_self = connection -> thread_data .settings_self ;
1050- aws_h2_decoder_set_setting_header_table_size (decoder , settings_self [AWS_H2_SETTINGS_HEADER_TABLE_SIZE ]);
1051- aws_h2_decoder_set_setting_enable_push (decoder , settings_self [AWS_H2_SETTINGS_ENABLE_PUSH ]);
1052- aws_h2_decoder_set_setting_max_frame_size (decoder , settings_self [AWS_H2_SETTINGS_MAX_FRAME_SIZE ]);
1130+ for (size_t i = 0 ; i < pending_settings -> num_settings ; i ++ ) {
1131+ if (connection -> thread_data .settings_self [settings_array [i ].id ] == settings_array [i ].value ) {
1132+ /* No change, don't do any work */
1133+ continue ;
1134+ }
1135+ switch (settings_array [i ].id ) {
1136+ case AWS_H2_SETTINGS_HEADER_TABLE_SIZE : {
1137+ aws_h2_decoder_set_setting_header_table_size (decoder , settings_array [i ].value );
1138+ } break ;
1139+ case AWS_H2_SETTINGS_ENABLE_PUSH : {
1140+ aws_h2_decoder_set_setting_enable_push (decoder , settings_array [i ].value );
1141+ } break ;
1142+ case AWS_H2_SETTINGS_INITIAL_WINDOW_SIZE : {
1143+ /* When the value of SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver MUST adjust the size of all stream
1144+ * flow-control windows that it maintains by the difference between the new value and the old value. */
1145+ int32_t size_changed =
1146+ settings_array [i ].value - connection -> thread_data .settings_self [settings_array [i ].id ];
1147+ struct aws_hash_iter stream_iter = aws_hash_iter_begin (& connection -> thread_data .active_streams_map );
1148+ while (!aws_hash_iter_done (& stream_iter )) {
1149+ struct aws_h2_stream * stream = stream_iter .element .value ;
1150+ aws_hash_iter_next (& stream_iter );
1151+ err = aws_h2_stream_window_size_change (stream , size_changed , true /*self*/ );
1152+ if (aws_h2err_failed (err )) {
1153+ CONNECTION_LOG (
1154+ ERROR ,
1155+ connection ,
1156+ "Connection error, change to SETTINGS_INITIAL_WINDOW_SIZE from internal caused a stream's "
1157+ "flow-control window to exceed the maximum size" );
1158+ return err ;
1159+ }
1160+ }
1161+ } break ;
1162+ case AWS_H2_SETTINGS_MAX_FRAME_SIZE : {
1163+ aws_h2_decoder_set_setting_max_frame_size (decoder , settings_array [i ].value );
1164+ } break ;
1165+ }
1166+ connection -> thread_data .settings_self [settings_array [i ].id ] = settings_array [i ].value ;
1167+ }
1168+ /* finish applying the settings, remove the front of the queue */
1169+ aws_linked_list_pop_front (& connection -> thread_data .pending_settings_queue );
1170+ /* clean up the pending_settings */
1171+ aws_mem_release (connection -> base .alloc , pending_settings );
10531172 return AWS_H2ERR_SUCCESS ;
10541173}
10551174
@@ -1142,20 +1261,6 @@ static int s_send_connection_preface_client_string(struct aws_h2_connection *con
11421261 return AWS_OP_ERR ;
11431262}
11441263
1145- /* #TODO actually fill with initial settings */
1146- /* #TODO track which SETTINGS frames have been ACK'd */
1147- static int s_enqueue_settings_frame (struct aws_h2_connection * connection ) {
1148- struct aws_allocator * alloc = connection -> base .alloc ;
1149-
1150- struct aws_h2_frame * settings_frame = aws_h2_frame_new_settings (alloc , NULL , 0 , false /*ack*/ );
1151- if (!settings_frame ) {
1152- return AWS_OP_ERR ;
1153- }
1154-
1155- aws_h2_connection_enqueue_outgoing_frame (connection , settings_frame );
1156- return AWS_OP_SUCCESS ;
1157- }
1158-
11591264static void s_handler_installed (struct aws_channel_handler * handler , struct aws_channel_slot * slot ) {
11601265 AWS_PRECONDITION (aws_channel_thread_is_callers_thread (slot -> channel ));
11611266 struct aws_h2_connection * connection = handler -> impl ;
@@ -1169,6 +1274,7 @@ static void s_handler_installed(struct aws_channel_handler *handler, struct aws_
11691274 /* Send HTTP/2 connection preface (RFC-7540 3.5)
11701275 * - clients must send magic string
11711276 * - both client and server must send SETTINGS frame */
1277+
11721278 if (connection -> base .client_data ) {
11731279 if (s_send_connection_preface_client_string (connection )) {
11741280 CONNECTION_LOGF (
@@ -1180,16 +1286,22 @@ static void s_handler_installed(struct aws_channel_handler *handler, struct aws_
11801286 }
11811287 }
11821288
1183- if (s_enqueue_settings_frame (connection )) {
1289+ /* #TODO actually fill with initial settings, now we just disable the push promise for client */
1290+ /* #TODO Probably we will move the initial settings to connection_new_http2_XXX after we got API for user to change
1291+ * settings */
1292+ struct aws_h2_frame_setting initial_settings [2 ];
1293+ size_t new_setting_iter = 0 ;
1294+ initial_settings [new_setting_iter ].id = AWS_H2_SETTINGS_ENABLE_PUSH ;
1295+ initial_settings [new_setting_iter ].value = 0 ;
1296+ new_setting_iter ++ ;
1297+ if (aws_h2_connection_change_settings (connection , initial_settings , new_setting_iter )) {
11841298 CONNECTION_LOGF (
11851299 ERROR ,
11861300 connection ,
11871301 "Failed to send SETTINGS frame for connection preface, %s" ,
11881302 aws_error_name (aws_last_error ()));
11891303 goto error ;
11901304 }
1191-
1192- s_try_write_outgoing_frames (connection );
11931305 return ;
11941306
11951307error :
0 commit comments