in source/h2_connection.c [1361:1448]
static struct aws_h2err s_decoder_on_settings(
const struct aws_http2_setting *settings_array,
size_t num_settings,
void *userdata) {
struct aws_h2_connection *connection = userdata;
struct aws_h2err err;
/* Once all values have been processed, the recipient MUST immediately emit a SETTINGS frame with the ACK flag
* set.(RFC-7540 6.5.3) */
CONNECTION_LOG(TRACE, connection, "Setting frame processing ends");
struct aws_h2_frame *settings_ack_frame = aws_h2_frame_new_settings(connection->base.alloc, NULL, 0, true);
if (!settings_ack_frame) {
CONNECTION_LOGF(
ERROR, connection, "Settings ACK frame failed to be sent, error %s", aws_error_name(aws_last_error()));
return aws_h2err_from_last_error();
}
aws_h2_connection_enqueue_outgoing_frame(connection, settings_ack_frame);
/* Allocate a block of memory for settings_array in callback, which will only includes the settings we changed,
* freed once the callback finished */
struct aws_http2_setting *callback_array = NULL;
if (num_settings) {
callback_array = aws_mem_acquire(connection->base.alloc, num_settings * sizeof(struct aws_http2_setting));
if (!callback_array) {
return aws_h2err_from_last_error();
}
}
size_t callback_array_num = 0;
/* Apply the change to encoder and connection */
struct aws_h2_frame_encoder *encoder = &connection->thread_data.encoder;
for (size_t i = 0; i < num_settings; i++) {
if (connection->thread_data.settings_peer[settings_array[i].id] == settings_array[i].value) {
/* No change, don't do any work */
continue;
}
switch (settings_array[i].id) {
case AWS_HTTP2_SETTINGS_HEADER_TABLE_SIZE: {
aws_h2_frame_encoder_set_setting_header_table_size(encoder, settings_array[i].value);
} break;
case AWS_HTTP2_SETTINGS_INITIAL_WINDOW_SIZE: {
/* When the value of SETTINGS_INITIAL_WINDOW_SIZE changes, a receiver MUST adjust the size of all stream
* flow-control windows that it maintains by the difference between the new value and the old value. */
int32_t size_changed =
settings_array[i].value - connection->thread_data.settings_peer[settings_array[i].id];
struct aws_hash_iter stream_iter = aws_hash_iter_begin(&connection->thread_data.active_streams_map);
while (!aws_hash_iter_done(&stream_iter)) {
struct aws_h2_stream *stream = stream_iter.element.value;
aws_hash_iter_next(&stream_iter);
err = aws_h2_stream_window_size_change(stream, size_changed, false /*self*/);
if (aws_h2err_failed(err)) {
CONNECTION_LOG(
ERROR,
connection,
"Connection error, change to SETTINGS_INITIAL_WINDOW_SIZE caused a stream's flow-control "
"window to exceed the maximum size");
goto error;
}
}
} break;
case AWS_HTTP2_SETTINGS_MAX_FRAME_SIZE: {
aws_h2_frame_encoder_set_setting_max_frame_size(encoder, settings_array[i].value);
} break;
default:
break;
}
connection->thread_data.settings_peer[settings_array[i].id] = settings_array[i].value;
callback_array[callback_array_num++] = settings_array[i];
}
if (connection->on_remote_settings_change) {
connection->on_remote_settings_change(
&connection->base, callback_array, callback_array_num, connection->base.user_data);
}
{ /* BEGIN CRITICAL SECTION */
s_lock_synced_data(connection);
memcpy(
connection->synced_data.settings_peer,
connection->thread_data.settings_peer,
sizeof(connection->thread_data.settings_peer));
s_unlock_synced_data(connection);
} /* END CRITICAL SECTION */
aws_mem_release(connection->base.alloc, callback_array);
return AWS_H2ERR_SUCCESS;
error:
aws_mem_release(connection->base.alloc, callback_array);
return err;
}