static struct aws_h2err s_decoder_on_settings()

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;
}