Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions include/aws/http/private/h2_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ struct aws_h2_connection {

bool is_outgoing_frames_task_active;

/* Settings received from peer, which restricts the message to send */
uint32_t settings_peer[AWS_H2_SETTINGS_END_RANGE];
/* My settings to send/sent to peer, which affects the decoding */
uint32_t settings_self[AWS_H2_SETTINGS_END_RANGE];

/* Maps stream-id to aws_h2_stream*.
* Contains all streams in the open, reserved, and half-closed states (terms from RFC-7540 5.1).
* Once a stream enters closed state, it is removed from this map. */
Expand Down
11 changes: 6 additions & 5 deletions include/aws/http/private/h2_decoder.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,8 @@ struct aws_h2_decoder_vtable {
/* Called once for SETTINGS frame with ACK flag */
int (*on_settings_ack)(void *userdata);

/* For SETTINGS frame (no ACK flag set): _begin() is called, then 0+ _i() calls, then _end().
* No other decoder callbacks will occur in this time. */
int (*on_settings_begin)(void *userdata);
int (*on_settings_i)(uint16_t setting_id, uint32_t value, void *userdata);
int (*on_settings_end)(void *userdata);
/* Called once for SETTINGS frame, without ACK flag */
int (*on_settings)(const struct aws_h2_frame_setting *settings_array, size_t num_settings, void *userdata);

/* For GOAWAY frame: _begin() is called, then 0+ _i() calls, then _end().
* No other decoder callbacks will occur in this time. */
Expand Down Expand Up @@ -114,6 +111,10 @@ AWS_HTTP_API struct aws_h2_decoder *aws_h2_decoder_new(struct aws_h2_decoder_par
AWS_HTTP_API void aws_h2_decoder_destroy(struct aws_h2_decoder *decoder);
AWS_HTTP_API int aws_h2_decode(struct aws_h2_decoder *decoder, struct aws_byte_cursor *data);

AWS_HTTP_API void aws_h2_decoder_set_setting_header_table_size(struct aws_h2_decoder *decoder, uint32_t data);
AWS_HTTP_API void aws_h2_decoder_set_setting_enable_push(struct aws_h2_decoder *decoder, uint32_t data);
AWS_HTTP_API void aws_h2_decoder_set_setting_max_frame_size(struct aws_h2_decoder *decoder, uint32_t data);

AWS_EXTERN_C_END

#endif /* AWS_HTTP_H2_DECODER_H */
14 changes: 14 additions & 0 deletions include/aws/http/private/h2_frames.h
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,15 @@ struct aws_h2_frame_encoder {
const void *logging_id;
struct aws_hpack_context *hpack;
struct aws_h2_frame *current_frame;

/* Settings for frame encoder, which is based on the settings received from peer */
struct {
/* the maximum size of the header compression table used to decode header blocks */
uint32_t header_table_size;
/* the size of the largest frame payload */
uint32_t max_frame_size;
} settings;

bool has_errored;
};

Expand Down Expand Up @@ -272,6 +281,11 @@ struct aws_h2_frame *aws_h2_frame_new_window_update(
uint32_t stream_id,
uint32_t window_size_increment);

AWS_HTTP_API void aws_h2_frame_encoder_set_setting_header_table_size(
struct aws_h2_frame_encoder *encoder,
uint32_t data);
AWS_HTTP_API void aws_h2_frame_encoder_set_setting_max_frame_size(struct aws_h2_frame_encoder *encoder, uint32_t data);

AWS_EXTERN_C_END

#endif /* AWS_HTTP_H2_FRAMES_H */
80 changes: 77 additions & 3 deletions source/h2_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ static void s_cross_thread_work_task(struct aws_channel_task *task, void *arg, e
static void s_outgoing_frames_task(struct aws_channel_task *task, void *arg, enum aws_task_status status);

static int s_decoder_on_ping(uint8_t opaque_data[AWS_H2_PING_DATA_SIZE], void *userdata);
static int s_decoder_on_settings(
const struct aws_h2_frame_setting *settings_array,
size_t num_settings,
void *userdata);
static int s_decoder_on_settings_ack(void *userdata);

static struct aws_http_connection_vtable s_h2_connection_vtable = {
.channel_handler_vtable =
Expand All @@ -87,6 +92,8 @@ static struct aws_http_connection_vtable s_h2_connection_vtable = {
static const struct aws_h2_decoder_vtable s_h2_decoder_vtable = {
.on_data = NULL,
.on_ping = s_decoder_on_ping,
.on_settings = s_decoder_on_settings,
.on_settings_ack = s_decoder_on_settings_ack,
};

static void s_lock_synced_data(struct aws_h2_connection *connection) {
Expand Down Expand Up @@ -209,6 +216,10 @@ static struct aws_h2_connection *s_connection_new(
goto error;
}

/* Initialize the value of settings */
memcpy(connection->thread_data.settings_peer, aws_h2_settings_initial, sizeof(aws_h2_settings_initial));
memcpy(connection->thread_data.settings_self, aws_h2_settings_initial, sizeof(aws_h2_settings_initial));

/* Create a new decoder */
struct aws_h2_decoder_params params = {
.alloc = alloc,
Expand Down Expand Up @@ -527,13 +538,70 @@ static int s_decoder_on_ping(uint8_t opaque_data[AWS_H2_PING_DATA_SIZE], void *u
}

aws_h2_connection_enqueue_outgoing_frame(connection, ping_ack_frame);
s_try_write_outgoing_frames(connection);
return AWS_OP_SUCCESS;
error:
CONNECTION_LOGF(ERROR, connection, "Ping ACK frame failed to be sent, error %s", aws_error_name(aws_last_error()));
return AWS_OP_ERR;
}

static void s_aws_h2_decoder_change_settings(struct aws_h2_connection *connection) {
struct aws_h2_decoder *decoder = connection->thread_data.decoder;
uint32_t *settings_self = connection->thread_data.settings_self;
aws_h2_decoder_set_setting_header_table_size(decoder, settings_self[AWS_H2_SETTINGS_HEADER_TABLE_SIZE]);
aws_h2_decoder_set_setting_enable_push(decoder, settings_self[AWS_H2_SETTINGS_ENABLE_PUSH]);
aws_h2_decoder_set_setting_max_frame_size(decoder, settings_self[AWS_H2_SETTINGS_MAX_FRAME_SIZE]);
}

static int s_decoder_on_settings(
const struct aws_h2_frame_setting *settings_array,
size_t num_settings,
void *userdata) {
struct aws_h2_connection *connection = userdata;
/* 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()));
goto error;
}
aws_h2_connection_enqueue_outgoing_frame(connection, settings_ack_frame);
/* Store the change to encoder and connection after enqueue the setting ACK frame */
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_H2_SETTINGS_HEADER_TABLE_SIZE:
aws_h2_frame_encoder_set_setting_header_table_size(encoder, settings_array[i].value);
break;
case AWS_H2_SETTINGS_MAX_FRAME_SIZE:
aws_h2_frame_encoder_set_setting_max_frame_size(encoder, settings_array[i].value);
break;
}
connection->thread_data.settings_peer[settings_array[i].id] = settings_array[i].value;
}
return AWS_OP_SUCCESS;

error:

return AWS_OP_ERR;
}

static int s_decoder_on_settings_ack(void *userdata) {
struct aws_h2_connection *connection = userdata;
/* #TODO track which SETTINGS frames is ACKed by this */

/* inform decoder about the settings */
s_aws_h2_decoder_change_settings(connection);
return AWS_OP_SUCCESS;
}

/* End decoder callbacks */

static int s_send_connection_preface_client_string(struct aws_h2_connection *connection) {

/* Just send the magic string on its own aws_io_message. */
Expand Down Expand Up @@ -563,7 +631,7 @@ static int s_send_connection_preface_client_string(struct aws_h2_connection *con
return AWS_OP_ERR;
}

/* #TODO actually fill with settings */
/* #TODO actually fill with initial settings */
/* #TODO track which SETTINGS frames have been ACK'd */
static int s_enqueue_settings_frame(struct aws_h2_connection *connection) {
struct aws_allocator *alloc = connection->base.alloc;
Expand Down Expand Up @@ -650,7 +718,11 @@ static void s_stream_complete(struct aws_h2_connection *connection, struct aws_h
static void s_activate_stream(struct aws_h2_connection *connection, struct aws_h2_stream *stream) {
AWS_PRECONDITION(aws_channel_thread_is_callers_thread(connection->base.channel_slot->channel));

/* #TODO: don't exceed peer's max-concurrent-streams setting */
uint32_t max_concurrent_streams = connection->thread_data.settings_peer[AWS_H2_SETTINGS_MAX_CONCURRENT_STREAMS];
if (aws_hash_table_get_entry_count(&connection->thread_data.active_streams_map) >= max_concurrent_streams) {
AWS_H2_STREAM_LOG(ERROR, stream, "Failed activating stream, max concurrent streams are reached");
goto error;
}

if (aws_hash_table_put(
&connection->thread_data.active_streams_map, (void *)(size_t)stream->base.id, stream, NULL)) {
Expand Down Expand Up @@ -842,6 +914,8 @@ static int s_handler_process_read_message(
aws_mem_release(message->allocator, message);
message = NULL;
}
/* Flush any outgoing frames that might have been queued as a result of decoder callbacks. */
s_try_write_outgoing_frames(connection);
return AWS_OP_SUCCESS;
shutdown:
if (message) {
Expand Down
78 changes: 69 additions & 9 deletions source/h2_decoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,18 @@ struct aws_h2_decoder {
bool malformed;
} header_block_in_progress;

/* Settings for decoder, which is based on the settings sent to the peer and ACKed by peer */
struct {
/* the maximum size of the header compression table used to decode header blocks */
uint32_t header_table_size;
/* enable/disable server push */
uint32_t enable_push;
/* the size of the largest frame payload */
uint32_t max_frame_size;
} settings;

struct aws_array_list settings_buffer_list;

/* User callbacks and settings. */
const struct aws_h2_decoder_vtable *vtable;
void *userdata;
Expand Down Expand Up @@ -290,9 +302,19 @@ struct aws_h2_decoder *aws_h2_decoder_new(struct aws_h2_decoder_params *params)
decoder->state = &s_state_prefix;
}

decoder->settings.header_table_size = aws_h2_settings_initial[AWS_H2_SETTINGS_HEADER_TABLE_SIZE];
decoder->settings.enable_push = aws_h2_settings_initial[AWS_H2_SETTINGS_ENABLE_PUSH];
decoder->settings.max_frame_size = aws_h2_settings_initial[AWS_H2_SETTINGS_MAX_FRAME_SIZE];

if (aws_array_list_init_dynamic(
&decoder->settings_buffer_list, decoder->alloc, 0, sizeof(struct aws_h2_frame_setting))) {
goto array_list_failed;
}

return decoder;

failed_new_hpack:
array_list_failed:
aws_mem_release(params->alloc, allocation);
failed_alloc:
return NULL;
Expand All @@ -309,6 +331,7 @@ void aws_h2_decoder_destroy(struct aws_h2_decoder *decoder) {
if (!decoder) {
return;
}
aws_array_list_clean_up(&decoder->settings_buffer_list);
aws_hpack_context_destroy(decoder->hpack);
s_reset_header_block_in_progress(decoder);
aws_mem_release(decoder->alloc, decoder);
Expand Down Expand Up @@ -587,13 +610,13 @@ static int s_state_fn_prefix(struct aws_h2_decoder *decoder, struct aws_byte_cur
}

/* Validate payload length. */
static const uint32_t MAX_FRAME_SIZE = 16384; /* #TODO handle the SETTINGS_MAX_FRAME_SIZE setting */
if (frame->payload_len > MAX_FRAME_SIZE) {
uint32_t max_frame_size = decoder->settings.max_frame_size;
if (frame->payload_len > max_frame_size) {
DECODER_LOGF(
ERROR,
decoder,
"Decoder's max frame size is %" PRIu32 ", but frame of size %" PRIu32 " was received.",
MAX_FRAME_SIZE,
max_frame_size,
frame->payload_len);
return aws_raise_error(AWS_ERROR_HTTP_INVALID_FRAME_SIZE);
}
Expand Down Expand Up @@ -785,9 +808,6 @@ static int s_state_fn_frame_settings_begin(struct aws_h2_decoder *decoder, struc
return aws_raise_error(AWS_ERROR_HTTP_INVALID_FRAME_SIZE);
}

/* Report start of non-ACK settings frame */
DECODER_CALL_VTABLE(decoder, on_settings_begin);

/* Enter looping states until all entries are consumed. */
return s_decoder_switch_state(decoder, &s_state_frame_settings_loop);
}
Expand All @@ -797,8 +817,12 @@ static int s_state_fn_frame_settings_loop(struct aws_h2_decoder *decoder, struct
(void)input;

if (decoder->frame_in_progress.payload_len == 0) {
/* Huzzah, done with the frame */
DECODER_CALL_VTABLE(decoder, on_settings_end);
/* Huzzah, done with the frame, fire the callback */
struct aws_array_list *buffer = &decoder->settings_buffer_list;
DECODER_CALL_VTABLE_ARGS(
decoder, on_settings, buffer->data, aws_array_list_length(&decoder->settings_buffer_list));
/* clean up the buffer */
aws_array_list_clear(&decoder->settings_buffer_list);
return s_decoder_reset_state(decoder);
}

Expand Down Expand Up @@ -831,7 +855,25 @@ static int s_state_fn_frame_settings_i(struct aws_h2_decoder *decoder, struct aw
/* An endpoint that receives a SETTINGS frame with any unknown or unsupported identifier MUST ignore that setting.
* RFC-7540 6.5.2 */
if (id >= AWS_H2_SETTINGS_BEGIN_RANGE && id < AWS_H2_SETTINGS_END_RANGE) {
DECODER_CALL_VTABLE_ARGS(decoder, on_settings_i, id, value);
/* check the value meets the settings bounds */
if (value < aws_h2_settings_bounds[id][0] || value > aws_h2_settings_bounds[id][1]) {
DECODER_LOGF(
ERROR, decoder, "A value of SETTING frame is invalid, id: %" PRIu16 ", value: %" PRIu32, id, value);
if (id == AWS_H2_SETTINGS_INITIAL_WINDOW_SIZE) {
return aws_raise_error(AWS_H2_ERR_FLOW_CONTROL_ERROR);
} else {
/* TODO: translates errors from AWS_ERROR_HTTP_XYZ into AWS_H2_ERR_XYZ */
return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
}
}
struct aws_h2_frame_setting setting;
setting.id = id;
setting.value = value;
/* array_list will keep a copy of setting, it is fine to be a local variable */
if (aws_array_list_push_back(&decoder->settings_buffer_list, &setting)) {
DECODER_LOGF(ERROR, decoder, "Writing setting to buffer failed, %s", aws_error_name(aws_last_error()));
return AWS_OP_ERR;
}
}

/* Update payload len */
Expand All @@ -848,6 +890,12 @@ static int s_state_fn_frame_settings_i(struct aws_h2_decoder *decoder, struct aw
*/
static int s_state_fn_frame_push_promise(struct aws_h2_decoder *decoder, struct aws_byte_cursor *input) {

if (decoder->settings.enable_push == 0) {
/* treat the receipt of a PUSH_PROMISE frame as a connection error of type PROTOCOL_ERROR.(RFC-7540 6.5.2) */
DECODER_LOG(ERROR, decoder, "PUSH_PROMISE is invalid, the seting for enable push is 0");
return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
}

AWS_ASSERT(input->len >= s_state_frame_push_promise_requires_4_bytes);

uint32_t promised_stream_id = 0;
Expand Down Expand Up @@ -1342,3 +1390,15 @@ static int s_state_fn_connection_preface_string(struct aws_h2_decoder *decoder,
/* Remain in state until more data arrives */
return AWS_OP_SUCCESS;
}

void aws_h2_decoder_set_setting_header_table_size(struct aws_h2_decoder *decoder, uint32_t data) {
decoder->settings.header_table_size = data;
}

void aws_h2_decoder_set_setting_enable_push(struct aws_h2_decoder *decoder, uint32_t data) {
decoder->settings.enable_push = data;
}

void aws_h2_decoder_set_setting_max_frame_size(struct aws_h2_decoder *decoder, uint32_t data) {
decoder->settings.max_frame_size = data;
}
16 changes: 13 additions & 3 deletions source/h2_frames.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,9 +136,7 @@ static int s_get_max_contiguous_payload_length(
return aws_raise_error(AWS_ERROR_SHORT_BUFFER);
}

/* #TODO actually check against encoder's current MAX_FRAME_SIZE */
(void)encoder;
const size_t max_payload_given_settings = AWS_H2_PAYLOAD_MAX;
size_t max_payload_given_settings = encoder->settings.max_frame_size;

*max_payload_length = aws_min_size(max_payload_given_space_available, max_payload_given_settings);
return AWS_OP_SUCCESS;
Expand Down Expand Up @@ -248,6 +246,9 @@ int aws_h2_frame_encoder_init(
return AWS_OP_ERR;
}

encoder->settings.header_table_size = aws_h2_settings_initial[AWS_H2_SETTINGS_HEADER_TABLE_SIZE];
encoder->settings.max_frame_size = aws_h2_settings_initial[AWS_H2_SETTINGS_MAX_FRAME_SIZE];

return AWS_OP_SUCCESS;
}
void aws_h2_frame_encoder_clean_up(struct aws_h2_frame_encoder *encoder) {
Expand Down Expand Up @@ -1138,3 +1139,12 @@ int aws_h2_encode_frame(
encoder->current_frame = *frame_complete ? NULL : frame;
return AWS_OP_SUCCESS;
}

void aws_h2_frame_encoder_set_setting_header_table_size(struct aws_h2_frame_encoder *encoder, uint32_t data) {
/* TODO:: pass down to the HPACK context aws_hpack_resize_dynamic_table() */
encoder->settings.header_table_size = data;
}

void aws_h2_frame_encoder_set_setting_max_frame_size(struct aws_h2_frame_encoder *encoder, uint32_t data) {
encoder->settings.max_frame_size = data;
}
Loading