Skip to content

Commit d0c3a07

Browse files
authored
H2 setting frame (#206)
* Setting frames in connection * bug_fix * Debug * decoder_settings * encoder_settings * Unit_test_built * clang format * consistance between code * delete unused value * add buffer_list for settings * windows cannot build fuzz test.. * clang format * update based on mike's comments * clang format Co-authored-by: Dengke Tang <dengket@amazon.com>
1 parent 1808e7d commit d0c3a07

File tree

11 files changed

+352
-72
lines changed

11 files changed

+352
-72
lines changed

include/aws/http/private/h2_connection.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@ struct aws_h2_connection {
4242

4343
bool is_outgoing_frames_task_active;
4444

45+
/* Settings received from peer, which restricts the message to send */
46+
uint32_t settings_peer[AWS_H2_SETTINGS_END_RANGE];
47+
/* My settings to send/sent to peer, which affects the decoding */
48+
uint32_t settings_self[AWS_H2_SETTINGS_END_RANGE];
49+
4550
/* Maps stream-id to aws_h2_stream*.
4651
* Contains all streams in the open, reserved, and half-closed states (terms from RFC-7540 5.1).
4752
* Once a stream enters closed state, it is removed from this map. */

include/aws/http/private/h2_decoder.h

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,8 @@ struct aws_h2_decoder_vtable {
7575
/* Called once for SETTINGS frame with ACK flag */
7676
int (*on_settings_ack)(void *userdata);
7777

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

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

114+
AWS_HTTP_API void aws_h2_decoder_set_setting_header_table_size(struct aws_h2_decoder *decoder, uint32_t data);
115+
AWS_HTTP_API void aws_h2_decoder_set_setting_enable_push(struct aws_h2_decoder *decoder, uint32_t data);
116+
AWS_HTTP_API void aws_h2_decoder_set_setting_max_frame_size(struct aws_h2_decoder *decoder, uint32_t data);
117+
117118
AWS_EXTERN_C_END
118119

119120
#endif /* AWS_HTTP_H2_DECODER_H */

include/aws/http/private/h2_frames.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,15 @@ struct aws_h2_frame_encoder {
133133
const void *logging_id;
134134
struct aws_hpack_context *hpack;
135135
struct aws_h2_frame *current_frame;
136+
137+
/* Settings for frame encoder, which is based on the settings received from peer */
138+
struct {
139+
/* the maximum size of the header compression table used to decode header blocks */
140+
uint32_t header_table_size;
141+
/* the size of the largest frame payload */
142+
uint32_t max_frame_size;
143+
} settings;
144+
136145
bool has_errored;
137146
};
138147

@@ -272,6 +281,11 @@ struct aws_h2_frame *aws_h2_frame_new_window_update(
272281
uint32_t stream_id,
273282
uint32_t window_size_increment);
274283

284+
AWS_HTTP_API void aws_h2_frame_encoder_set_setting_header_table_size(
285+
struct aws_h2_frame_encoder *encoder,
286+
uint32_t data);
287+
AWS_HTTP_API void aws_h2_frame_encoder_set_setting_max_frame_size(struct aws_h2_frame_encoder *encoder, uint32_t data);
288+
275289
AWS_EXTERN_C_END
276290

277291
#endif /* AWS_HTTP_H2_FRAMES_H */

source/h2_connection.c

Lines changed: 77 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,11 @@ static void s_cross_thread_work_task(struct aws_channel_task *task, void *arg, e
6262
static void s_outgoing_frames_task(struct aws_channel_task *task, void *arg, enum aws_task_status status);
6363

6464
static int s_decoder_on_ping(uint8_t opaque_data[AWS_H2_PING_DATA_SIZE], void *userdata);
65+
static int s_decoder_on_settings(
66+
const struct aws_h2_frame_setting *settings_array,
67+
size_t num_settings,
68+
void *userdata);
69+
static int s_decoder_on_settings_ack(void *userdata);
6570

6671
static struct aws_http_connection_vtable s_h2_connection_vtable = {
6772
.channel_handler_vtable =
@@ -87,6 +92,8 @@ static struct aws_http_connection_vtable s_h2_connection_vtable = {
8792
static const struct aws_h2_decoder_vtable s_h2_decoder_vtable = {
8893
.on_data = NULL,
8994
.on_ping = s_decoder_on_ping,
95+
.on_settings = s_decoder_on_settings,
96+
.on_settings_ack = s_decoder_on_settings_ack,
9097
};
9198

9299
static void s_lock_synced_data(struct aws_h2_connection *connection) {
@@ -209,6 +216,10 @@ static struct aws_h2_connection *s_connection_new(
209216
goto error;
210217
}
211218

219+
/* Initialize the value of settings */
220+
memcpy(connection->thread_data.settings_peer, aws_h2_settings_initial, sizeof(aws_h2_settings_initial));
221+
memcpy(connection->thread_data.settings_self, aws_h2_settings_initial, sizeof(aws_h2_settings_initial));
222+
212223
/* Create a new decoder */
213224
struct aws_h2_decoder_params params = {
214225
.alloc = alloc,
@@ -527,13 +538,70 @@ static int s_decoder_on_ping(uint8_t opaque_data[AWS_H2_PING_DATA_SIZE], void *u
527538
}
528539

529540
aws_h2_connection_enqueue_outgoing_frame(connection, ping_ack_frame);
530-
s_try_write_outgoing_frames(connection);
531541
return AWS_OP_SUCCESS;
532542
error:
533543
CONNECTION_LOGF(ERROR, connection, "Ping ACK frame failed to be sent, error %s", aws_error_name(aws_last_error()));
534544
return AWS_OP_ERR;
535545
}
536546

547+
static void s_aws_h2_decoder_change_settings(struct aws_h2_connection *connection) {
548+
struct aws_h2_decoder *decoder = connection->thread_data.decoder;
549+
uint32_t *settings_self = connection->thread_data.settings_self;
550+
aws_h2_decoder_set_setting_header_table_size(decoder, settings_self[AWS_H2_SETTINGS_HEADER_TABLE_SIZE]);
551+
aws_h2_decoder_set_setting_enable_push(decoder, settings_self[AWS_H2_SETTINGS_ENABLE_PUSH]);
552+
aws_h2_decoder_set_setting_max_frame_size(decoder, settings_self[AWS_H2_SETTINGS_MAX_FRAME_SIZE]);
553+
}
554+
555+
static int s_decoder_on_settings(
556+
const struct aws_h2_frame_setting *settings_array,
557+
size_t num_settings,
558+
void *userdata) {
559+
struct aws_h2_connection *connection = userdata;
560+
/* Once all values have been processed, the recipient MUST immediately emit a SETTINGS frame with the ACK flag
561+
* set.(RFC-7540 6.5.3) */
562+
CONNECTION_LOG(TRACE, connection, "Setting frame processing ends");
563+
struct aws_h2_frame *settings_ack_frame = aws_h2_frame_new_settings(connection->base.alloc, NULL, 0, true);
564+
if (!settings_ack_frame) {
565+
CONNECTION_LOGF(
566+
ERROR, connection, "Settings ACK frame failed to be sent, error %s", aws_error_name(aws_last_error()));
567+
goto error;
568+
}
569+
aws_h2_connection_enqueue_outgoing_frame(connection, settings_ack_frame);
570+
/* Store the change to encoder and connection after enqueue the setting ACK frame */
571+
struct aws_h2_frame_encoder *encoder = &connection->thread_data.encoder;
572+
for (size_t i = 0; i < num_settings; i++) {
573+
if (connection->thread_data.settings_peer[settings_array[i].id] == settings_array[i].value) {
574+
/* No change, don't do any work */
575+
continue;
576+
}
577+
switch (settings_array[i].id) {
578+
case AWS_H2_SETTINGS_HEADER_TABLE_SIZE:
579+
aws_h2_frame_encoder_set_setting_header_table_size(encoder, settings_array[i].value);
580+
break;
581+
case AWS_H2_SETTINGS_MAX_FRAME_SIZE:
582+
aws_h2_frame_encoder_set_setting_max_frame_size(encoder, settings_array[i].value);
583+
break;
584+
}
585+
connection->thread_data.settings_peer[settings_array[i].id] = settings_array[i].value;
586+
}
587+
return AWS_OP_SUCCESS;
588+
589+
error:
590+
591+
return AWS_OP_ERR;
592+
}
593+
594+
static int s_decoder_on_settings_ack(void *userdata) {
595+
struct aws_h2_connection *connection = userdata;
596+
/* #TODO track which SETTINGS frames is ACKed by this */
597+
598+
/* inform decoder about the settings */
599+
s_aws_h2_decoder_change_settings(connection);
600+
return AWS_OP_SUCCESS;
601+
}
602+
603+
/* End decoder callbacks */
604+
537605
static int s_send_connection_preface_client_string(struct aws_h2_connection *connection) {
538606

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

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

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

655727
if (aws_hash_table_put(
656728
&connection->thread_data.active_streams_map, (void *)(size_t)stream->base.id, stream, NULL)) {
@@ -842,6 +914,8 @@ static int s_handler_process_read_message(
842914
aws_mem_release(message->allocator, message);
843915
message = NULL;
844916
}
917+
/* Flush any outgoing frames that might have been queued as a result of decoder callbacks. */
918+
s_try_write_outgoing_frames(connection);
845919
return AWS_OP_SUCCESS;
846920
shutdown:
847921
if (message) {

source/h2_decoder.c

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,18 @@ struct aws_h2_decoder {
244244
bool malformed;
245245
} header_block_in_progress;
246246

247+
/* Settings for decoder, which is based on the settings sent to the peer and ACKed by peer */
248+
struct {
249+
/* the maximum size of the header compression table used to decode header blocks */
250+
uint32_t header_table_size;
251+
/* enable/disable server push */
252+
uint32_t enable_push;
253+
/* the size of the largest frame payload */
254+
uint32_t max_frame_size;
255+
} settings;
256+
257+
struct aws_array_list settings_buffer_list;
258+
247259
/* User callbacks and settings. */
248260
const struct aws_h2_decoder_vtable *vtable;
249261
void *userdata;
@@ -290,9 +302,19 @@ struct aws_h2_decoder *aws_h2_decoder_new(struct aws_h2_decoder_params *params)
290302
decoder->state = &s_state_prefix;
291303
}
292304

305+
decoder->settings.header_table_size = aws_h2_settings_initial[AWS_H2_SETTINGS_HEADER_TABLE_SIZE];
306+
decoder->settings.enable_push = aws_h2_settings_initial[AWS_H2_SETTINGS_ENABLE_PUSH];
307+
decoder->settings.max_frame_size = aws_h2_settings_initial[AWS_H2_SETTINGS_MAX_FRAME_SIZE];
308+
309+
if (aws_array_list_init_dynamic(
310+
&decoder->settings_buffer_list, decoder->alloc, 0, sizeof(struct aws_h2_frame_setting))) {
311+
goto array_list_failed;
312+
}
313+
293314
return decoder;
294315

295316
failed_new_hpack:
317+
array_list_failed:
296318
aws_mem_release(params->alloc, allocation);
297319
failed_alloc:
298320
return NULL;
@@ -309,6 +331,7 @@ void aws_h2_decoder_destroy(struct aws_h2_decoder *decoder) {
309331
if (!decoder) {
310332
return;
311333
}
334+
aws_array_list_clean_up(&decoder->settings_buffer_list);
312335
aws_hpack_context_destroy(decoder->hpack);
313336
s_reset_header_block_in_progress(decoder);
314337
aws_mem_release(decoder->alloc, decoder);
@@ -587,13 +610,13 @@ static int s_state_fn_prefix(struct aws_h2_decoder *decoder, struct aws_byte_cur
587610
}
588611

589612
/* Validate payload length. */
590-
static const uint32_t MAX_FRAME_SIZE = 16384; /* #TODO handle the SETTINGS_MAX_FRAME_SIZE setting */
591-
if (frame->payload_len > MAX_FRAME_SIZE) {
613+
uint32_t max_frame_size = decoder->settings.max_frame_size;
614+
if (frame->payload_len > max_frame_size) {
592615
DECODER_LOGF(
593616
ERROR,
594617
decoder,
595618
"Decoder's max frame size is %" PRIu32 ", but frame of size %" PRIu32 " was received.",
596-
MAX_FRAME_SIZE,
619+
max_frame_size,
597620
frame->payload_len);
598621
return aws_raise_error(AWS_ERROR_HTTP_INVALID_FRAME_SIZE);
599622
}
@@ -785,9 +808,6 @@ static int s_state_fn_frame_settings_begin(struct aws_h2_decoder *decoder, struc
785808
return aws_raise_error(AWS_ERROR_HTTP_INVALID_FRAME_SIZE);
786809
}
787810

788-
/* Report start of non-ACK settings frame */
789-
DECODER_CALL_VTABLE(decoder, on_settings_begin);
790-
791811
/* Enter looping states until all entries are consumed. */
792812
return s_decoder_switch_state(decoder, &s_state_frame_settings_loop);
793813
}
@@ -797,8 +817,12 @@ static int s_state_fn_frame_settings_loop(struct aws_h2_decoder *decoder, struct
797817
(void)input;
798818

799819
if (decoder->frame_in_progress.payload_len == 0) {
800-
/* Huzzah, done with the frame */
801-
DECODER_CALL_VTABLE(decoder, on_settings_end);
820+
/* Huzzah, done with the frame, fire the callback */
821+
struct aws_array_list *buffer = &decoder->settings_buffer_list;
822+
DECODER_CALL_VTABLE_ARGS(
823+
decoder, on_settings, buffer->data, aws_array_list_length(&decoder->settings_buffer_list));
824+
/* clean up the buffer */
825+
aws_array_list_clear(&decoder->settings_buffer_list);
802826
return s_decoder_reset_state(decoder);
803827
}
804828

@@ -831,7 +855,25 @@ static int s_state_fn_frame_settings_i(struct aws_h2_decoder *decoder, struct aw
831855
/* An endpoint that receives a SETTINGS frame with any unknown or unsupported identifier MUST ignore that setting.
832856
* RFC-7540 6.5.2 */
833857
if (id >= AWS_H2_SETTINGS_BEGIN_RANGE && id < AWS_H2_SETTINGS_END_RANGE) {
834-
DECODER_CALL_VTABLE_ARGS(decoder, on_settings_i, id, value);
858+
/* check the value meets the settings bounds */
859+
if (value < aws_h2_settings_bounds[id][0] || value > aws_h2_settings_bounds[id][1]) {
860+
DECODER_LOGF(
861+
ERROR, decoder, "A value of SETTING frame is invalid, id: %" PRIu16 ", value: %" PRIu32, id, value);
862+
if (id == AWS_H2_SETTINGS_INITIAL_WINDOW_SIZE) {
863+
return aws_raise_error(AWS_H2_ERR_FLOW_CONTROL_ERROR);
864+
} else {
865+
/* TODO: translates errors from AWS_ERROR_HTTP_XYZ into AWS_H2_ERR_XYZ */
866+
return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
867+
}
868+
}
869+
struct aws_h2_frame_setting setting;
870+
setting.id = id;
871+
setting.value = value;
872+
/* array_list will keep a copy of setting, it is fine to be a local variable */
873+
if (aws_array_list_push_back(&decoder->settings_buffer_list, &setting)) {
874+
DECODER_LOGF(ERROR, decoder, "Writing setting to buffer failed, %s", aws_error_name(aws_last_error()));
875+
return AWS_OP_ERR;
876+
}
835877
}
836878

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

893+
if (decoder->settings.enable_push == 0) {
894+
/* treat the receipt of a PUSH_PROMISE frame as a connection error of type PROTOCOL_ERROR.(RFC-7540 6.5.2) */
895+
DECODER_LOG(ERROR, decoder, "PUSH_PROMISE is invalid, the seting for enable push is 0");
896+
return aws_raise_error(AWS_ERROR_HTTP_PROTOCOL_ERROR);
897+
}
898+
851899
AWS_ASSERT(input->len >= s_state_frame_push_promise_requires_4_bytes);
852900

853901
uint32_t promised_stream_id = 0;
@@ -1342,3 +1390,15 @@ static int s_state_fn_connection_preface_string(struct aws_h2_decoder *decoder,
13421390
/* Remain in state until more data arrives */
13431391
return AWS_OP_SUCCESS;
13441392
}
1393+
1394+
void aws_h2_decoder_set_setting_header_table_size(struct aws_h2_decoder *decoder, uint32_t data) {
1395+
decoder->settings.header_table_size = data;
1396+
}
1397+
1398+
void aws_h2_decoder_set_setting_enable_push(struct aws_h2_decoder *decoder, uint32_t data) {
1399+
decoder->settings.enable_push = data;
1400+
}
1401+
1402+
void aws_h2_decoder_set_setting_max_frame_size(struct aws_h2_decoder *decoder, uint32_t data) {
1403+
decoder->settings.max_frame_size = data;
1404+
}

source/h2_frames.c

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -136,9 +136,7 @@ static int s_get_max_contiguous_payload_length(
136136
return aws_raise_error(AWS_ERROR_SHORT_BUFFER);
137137
}
138138

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

143141
*max_payload_length = aws_min_size(max_payload_given_space_available, max_payload_given_settings);
144142
return AWS_OP_SUCCESS;
@@ -248,6 +246,9 @@ int aws_h2_frame_encoder_init(
248246
return AWS_OP_ERR;
249247
}
250248

249+
encoder->settings.header_table_size = aws_h2_settings_initial[AWS_H2_SETTINGS_HEADER_TABLE_SIZE];
250+
encoder->settings.max_frame_size = aws_h2_settings_initial[AWS_H2_SETTINGS_MAX_FRAME_SIZE];
251+
251252
return AWS_OP_SUCCESS;
252253
}
253254
void aws_h2_frame_encoder_clean_up(struct aws_h2_frame_encoder *encoder) {
@@ -1138,3 +1139,12 @@ int aws_h2_encode_frame(
11381139
encoder->current_frame = *frame_complete ? NULL : frame;
11391140
return AWS_OP_SUCCESS;
11401141
}
1142+
1143+
void aws_h2_frame_encoder_set_setting_header_table_size(struct aws_h2_frame_encoder *encoder, uint32_t data) {
1144+
/* TODO:: pass down to the HPACK context aws_hpack_resize_dynamic_table() */
1145+
encoder->settings.header_table_size = data;
1146+
}
1147+
1148+
void aws_h2_frame_encoder_set_setting_max_frame_size(struct aws_h2_frame_encoder *encoder, uint32_t data) {
1149+
encoder->settings.max_frame_size = data;
1150+
}

0 commit comments

Comments
 (0)