/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include struct test_data { struct aws_allocator *allocator; struct testing_channel testing_channel; struct aws_channel_handler *handler; aws_event_stream_channel_handler_on_message_received_fn *received_fn; void *user_data; }; static struct test_data s_test_data; static void s_fixture_on_message(struct aws_event_stream_message *message, int error_code, void *user_data) { struct test_data *test_data = user_data; test_data->received_fn(message, error_code, test_data->user_data); } static int s_fixture_setup(struct aws_allocator *allocator, void *ctx) { aws_event_stream_library_init(allocator); struct test_data *test_data = ctx; AWS_ZERO_STRUCT(*test_data); test_data->allocator = allocator; struct aws_testing_channel_options testing_channel_options = { .clock_fn = aws_high_res_clock_get_ticks, }; ASSERT_SUCCESS(testing_channel_init(&test_data->testing_channel, allocator, &testing_channel_options)); struct aws_channel_slot *slot = aws_channel_slot_new(test_data->testing_channel.channel); ASSERT_NOT_NULL(slot); ASSERT_SUCCESS(aws_channel_slot_insert_end(test_data->testing_channel.channel, slot)); struct aws_event_stream_channel_handler_options options = { .initial_window_size = SIZE_MAX, .user_data = ctx, .on_message_received = s_fixture_on_message, }; test_data->handler = aws_event_stream_channel_handler_new(allocator, &options); ASSERT_NOT_NULL(test_data->handler); ASSERT_SUCCESS(aws_channel_slot_set_handler(slot, test_data->handler)); testing_channel_run_currently_queued_tasks(&test_data->testing_channel); return AWS_OP_SUCCESS; } static int s_fixture_shutdown(struct aws_allocator *allocator, int setup_result, void *ctx) { (void)allocator; struct test_data *test_data = ctx; if (!setup_result) { testing_channel_clean_up(&test_data->testing_channel); } aws_event_stream_library_clean_up(); return AWS_OP_SUCCESS; } struct single_message_test_data { int last_error_code; struct aws_event_stream_message received_msg_cpy; }; static void s_test_on_single_message(struct aws_event_stream_message *message, int error_code, void *user_data) { struct single_message_test_data *msg_test_data = user_data; msg_test_data->last_error_code = error_code; if (!error_code) { struct aws_byte_buf message_buf = aws_byte_buf_from_array( aws_event_stream_message_buffer(message), aws_event_stream_message_total_length(message)); aws_event_stream_message_from_buffer_copy( &msg_test_data->received_msg_cpy, s_test_data.allocator, &message_buf); } } /* send various valid messages in serial to make sure the happy path of message parsing is correct. */ static int s_test_channel_handler_single_valid_messages_parse(struct aws_allocator *allocator, void *ctx) { struct test_data *test_data = ctx; struct single_message_test_data message_test_data; AWS_ZERO_STRUCT(message_test_data); test_data->received_fn = s_test_on_single_message; test_data->user_data = &message_test_data; uint8_t empty_message[] = { 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x05, 0xc2, 0x48, 0xeb, 0x7d, 0x98, 0xc8, 0xff, }; struct aws_byte_cursor empty_message_cursor = aws_byte_cursor_from_array(empty_message, sizeof(empty_message)); ASSERT_SUCCESS(testing_channel_push_read_data(&s_test_data.testing_channel, empty_message_cursor)); ASSERT_UINT_EQUALS(AWS_OP_SUCCESS, message_test_data.last_error_code); ASSERT_UINT_EQUALS(0x00000010, aws_event_stream_message_total_length(&message_test_data.received_msg_cpy)); ASSERT_UINT_EQUALS(0x00000000, aws_event_stream_message_headers_len(&message_test_data.received_msg_cpy)); ASSERT_UINT_EQUALS(0x05c248eb, aws_event_stream_message_prelude_crc(&message_test_data.received_msg_cpy)); ASSERT_UINT_EQUALS(0x7d98c8ff, aws_event_stream_message_message_crc(&message_test_data.received_msg_cpy)); aws_event_stream_message_clean_up(&message_test_data.received_msg_cpy); AWS_ZERO_STRUCT(message_test_data); uint8_t no_headers_data[] = { 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x52, 0x8c, 0x5a, 0x7b, 0x27, 0x66, 0x6f, 0x6f, 0x27, 0x3a, 0x27, 0x62, 0x61, 0x72, 0x27, 0x7d, 0xc3, 0x65, 0x39, 0x36, }; struct aws_byte_cursor no_headers_cur = aws_byte_cursor_from_array(no_headers_data, sizeof(no_headers_data)); ASSERT_SUCCESS(testing_channel_push_read_data(&s_test_data.testing_channel, no_headers_cur)); ASSERT_UINT_EQUALS(AWS_OP_SUCCESS, message_test_data.last_error_code); ASSERT_UINT_EQUALS(0x0000001D, aws_event_stream_message_total_length(&message_test_data.received_msg_cpy)); ASSERT_UINT_EQUALS(0x00000000, aws_event_stream_message_headers_len(&message_test_data.received_msg_cpy)); ASSERT_UINT_EQUALS(0xfd528c5a, aws_event_stream_message_prelude_crc(&message_test_data.received_msg_cpy)); const char *expected_str = "{'foo':'bar'}"; ASSERT_UINT_EQUALS(strlen(expected_str), aws_event_stream_message_payload_len(&message_test_data.received_msg_cpy)); ASSERT_BIN_ARRAYS_EQUALS( expected_str, strlen(expected_str), aws_event_stream_message_payload(&message_test_data.received_msg_cpy), aws_event_stream_message_payload_len(&message_test_data.received_msg_cpy)); ASSERT_UINT_EQUALS(0xc3653936, aws_event_stream_message_message_crc(&message_test_data.received_msg_cpy)); aws_event_stream_message_clean_up(&message_test_data.received_msg_cpy); AWS_ZERO_STRUCT(message_test_data); uint8_t headers_test_data[] = { 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x20, 0x07, 0xFD, 0x83, 0x96, 0x0C, 'c', 'o', 'n', 't', 'e', 'n', 't', '-', 't', 'y', 'p', 'e', 0x07, 0x00, 0x10, 'a', 'p', 'p', 'l', 'i', 'c', 'a', 't', 'i', 'o', 'n', '/', 'j', 's', 'o', 'n', 0x7b, 0x27, 0x66, 0x6f, 0x6f, 0x27, 0x3a, 0x27, 0x62, 0x61, 0x72, 0x27, 0x7d, 0x8D, 0x9C, 0x08, 0xB1, }; struct aws_byte_cursor headers_test_cur = aws_byte_cursor_from_array(headers_test_data, sizeof(headers_test_data)); ASSERT_SUCCESS(testing_channel_push_read_data(&s_test_data.testing_channel, headers_test_cur)); ASSERT_UINT_EQUALS(AWS_OP_SUCCESS, message_test_data.last_error_code); ASSERT_UINT_EQUALS(0x0000003D, aws_event_stream_message_total_length(&message_test_data.received_msg_cpy)); ASSERT_UINT_EQUALS(0x00000020, aws_event_stream_message_headers_len(&message_test_data.received_msg_cpy)); ASSERT_UINT_EQUALS(0x07FD8396, aws_event_stream_message_prelude_crc(&message_test_data.received_msg_cpy)); ASSERT_UINT_EQUALS(strlen(expected_str), aws_event_stream_message_payload_len(&message_test_data.received_msg_cpy)); ASSERT_BIN_ARRAYS_EQUALS( expected_str, strlen(expected_str), aws_event_stream_message_payload(&message_test_data.received_msg_cpy), aws_event_stream_message_payload_len(&message_test_data.received_msg_cpy)); struct aws_array_list headers; ASSERT_SUCCESS(aws_event_stream_headers_list_init(&headers, allocator)); ASSERT_SUCCESS(aws_event_stream_message_headers(&message_test_data.received_msg_cpy, &headers)); ASSERT_UINT_EQUALS(1, headers.length, ); struct aws_event_stream_header_value_pair header; ASSERT_SUCCESS(aws_array_list_front(&headers, &header)); const char *content_type = "content-type"; const char *content_type_value = "application/json"; struct aws_byte_buf header_name_buf = aws_event_stream_header_name(&header); ASSERT_BIN_ARRAYS_EQUALS(content_type, strlen(content_type), header_name_buf.buffer, header_name_buf.len); struct aws_byte_buf header_value_buf = aws_event_stream_header_value_as_string(&header); ASSERT_BIN_ARRAYS_EQUALS( content_type_value, strlen(content_type_value), header_value_buf.buffer, header_value_buf.len); ASSERT_UINT_EQUALS(0x8D9C08B1, aws_event_stream_message_message_crc(&message_test_data.received_msg_cpy)); aws_event_stream_headers_list_cleanup(&headers); aws_event_stream_message_clean_up(&message_test_data.received_msg_cpy); return AWS_OP_SUCCESS; } AWS_TEST_CASE_FIXTURE( test_channel_handler_single_valid_messages_parse, s_fixture_setup, s_test_channel_handler_single_valid_messages_parse, s_fixture_shutdown, &s_test_data) struct multiple_message_test_data { int last_error_code; struct aws_event_stream_message *received_msgs_cpy; size_t msg_count; }; static void s_test_on_multiple_message(struct aws_event_stream_message *message, int error_code, void *user_data) { struct multiple_message_test_data *msg_test_data = user_data; msg_test_data->last_error_code = error_code; if (!error_code) { struct aws_byte_buf message_buf = aws_byte_buf_from_array( aws_event_stream_message_buffer(message), aws_event_stream_message_total_length(message)); struct aws_event_stream_message msg_cpy; AWS_ZERO_STRUCT(msg_cpy); aws_event_stream_message_from_buffer_copy(&msg_cpy, s_test_data.allocator, &message_buf); msg_test_data->received_msgs_cpy[msg_test_data->msg_count++] = msg_cpy; } } /* send various valid messages as a batch to make sure the happy path of message parsing is correct. */ static int s_test_channel_handler_multiple_valid_messages_parse(struct aws_allocator *allocator, void *ctx) { struct test_data *test_data = ctx; uint8_t multi_message[] = { 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x05, 0xc2, 0x48, 0xeb, 0x7d, 0x98, 0xc8, 0xff, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x00, 0x00, 0xfd, 0x52, 0x8c, 0x5a, 0x7b, 0x27, 0x66, 0x6f, 0x6f, 0x27, 0x3a, 0x27, 0x62, 0x61, 0x72, 0x27, 0x7d, 0xc3, 0x65, 0x39, 0x36, 0x00, 0x00, 0x00, 0x3D, 0x00, 0x00, 0x00, 0x20, 0x07, 0xFD, 0x83, 0x96, 0x0C, 'c', 'o', 'n', 't', 'e', 'n', 't', '-', 't', 'y', 'p', 'e', 0x07, 0x00, 0x10, 'a', 'p', 'p', 'l', 'i', 'c', 'a', 't', 'i', 'o', 'n', '/', 'j', 's', 'o', 'n', 0x7b, 0x27, 0x66, 0x6f, 0x6f, 0x27, 0x3a, 0x27, 0x62, 0x61, 0x72, 0x27, 0x7d, 0x8D, 0x9C, 0x08, 0xB1, }; /* fuzz the parser's boundary conditions */ for (size_t fragment_size = 1; fragment_size <= sizeof(multi_message); ++fragment_size) { struct multiple_message_test_data message_test_data; AWS_ZERO_STRUCT(message_test_data); struct aws_event_stream_message messages[100]; AWS_ZERO_ARRAY(messages); message_test_data.received_msgs_cpy = messages; test_data->received_fn = s_test_on_multiple_message; test_data->user_data = &message_test_data; size_t processed = 0; while (processed < sizeof(multi_message)) { size_t remaining = sizeof(multi_message) - processed; size_t to_copy = fragment_size < remaining ? fragment_size : remaining; struct aws_byte_cursor multi_message_cursor = aws_byte_cursor_from_array(multi_message + processed, to_copy); processed += to_copy; ASSERT_SUCCESS(testing_channel_push_read_data(&s_test_data.testing_channel, multi_message_cursor)); } ASSERT_UINT_EQUALS(AWS_OP_SUCCESS, message_test_data.last_error_code); ASSERT_UINT_EQUALS(3, message_test_data.msg_count); ASSERT_UINT_EQUALS(0x00000010, aws_event_stream_message_total_length(&message_test_data.received_msgs_cpy[0])); ASSERT_UINT_EQUALS(0x00000000, aws_event_stream_message_headers_len(&message_test_data.received_msgs_cpy[0])); ASSERT_UINT_EQUALS(0x05c248eb, aws_event_stream_message_prelude_crc(&message_test_data.received_msgs_cpy[0])); ASSERT_UINT_EQUALS(0x7d98c8ff, aws_event_stream_message_message_crc(&message_test_data.received_msgs_cpy[0])); aws_event_stream_message_clean_up(&message_test_data.received_msgs_cpy[0]); ASSERT_UINT_EQUALS(0x0000001D, aws_event_stream_message_total_length(&message_test_data.received_msgs_cpy[1])); ASSERT_UINT_EQUALS(0x00000000, aws_event_stream_message_headers_len(&message_test_data.received_msgs_cpy[1])); ASSERT_UINT_EQUALS(0xfd528c5a, aws_event_stream_message_prelude_crc(&message_test_data.received_msgs_cpy[1])); const char *expected_str = "{'foo':'bar'}"; ASSERT_UINT_EQUALS( strlen(expected_str), aws_event_stream_message_payload_len(&message_test_data.received_msgs_cpy[1])); ASSERT_BIN_ARRAYS_EQUALS( expected_str, strlen(expected_str), aws_event_stream_message_payload(&message_test_data.received_msgs_cpy[1]), aws_event_stream_message_payload_len(&message_test_data.received_msgs_cpy[1])); ASSERT_UINT_EQUALS(0xc3653936, aws_event_stream_message_message_crc(&message_test_data.received_msgs_cpy[1])); aws_event_stream_message_clean_up(&message_test_data.received_msgs_cpy[1]); ASSERT_UINT_EQUALS(0x0000003D, aws_event_stream_message_total_length(&message_test_data.received_msgs_cpy[2])); ASSERT_UINT_EQUALS(0x00000020, aws_event_stream_message_headers_len(&message_test_data.received_msgs_cpy[2])); ASSERT_UINT_EQUALS(0x07FD8396, aws_event_stream_message_prelude_crc(&message_test_data.received_msgs_cpy[2])); ASSERT_UINT_EQUALS( strlen(expected_str), aws_event_stream_message_payload_len(&message_test_data.received_msgs_cpy[2])); ASSERT_BIN_ARRAYS_EQUALS( expected_str, strlen(expected_str), aws_event_stream_message_payload(&message_test_data.received_msgs_cpy[2]), aws_event_stream_message_payload_len(&message_test_data.received_msgs_cpy[2])); struct aws_array_list headers; ASSERT_SUCCESS(aws_event_stream_headers_list_init(&headers, allocator)); ASSERT_SUCCESS(aws_event_stream_message_headers(&message_test_data.received_msgs_cpy[2], &headers)); ASSERT_UINT_EQUALS(1, headers.length, ); struct aws_event_stream_header_value_pair header; ASSERT_SUCCESS(aws_array_list_front(&headers, &header)); const char *content_type = "content-type"; const char *content_type_value = "application/json"; struct aws_byte_buf header_name_buf = aws_event_stream_header_name(&header); ASSERT_BIN_ARRAYS_EQUALS(content_type, strlen(content_type), header_name_buf.buffer, header_name_buf.len); struct aws_byte_buf header_value_buf = aws_event_stream_header_value_as_string(&header); ASSERT_BIN_ARRAYS_EQUALS( content_type_value, strlen(content_type_value), header_value_buf.buffer, header_value_buf.len); ASSERT_UINT_EQUALS(0x8D9C08B1, aws_event_stream_message_message_crc(&message_test_data.received_msgs_cpy[2])); aws_event_stream_headers_list_cleanup(&headers); aws_event_stream_message_clean_up(&message_test_data.received_msgs_cpy[2]); } return AWS_OP_SUCCESS; } AWS_TEST_CASE_FIXTURE( test_channel_handler_multiple_valid_messages_parse, s_fixture_setup, s_test_channel_handler_multiple_valid_messages_parse, s_fixture_shutdown, &s_test_data) /* send various valid messages in serial to make sure the happy path of message parsing is correct. */ static int s_test_channel_handler_corrupted_crc_fails(struct aws_allocator *allocator, void *ctx) { (void)allocator; struct test_data *test_data = ctx; struct single_message_test_data message_test_data; AWS_ZERO_STRUCT(message_test_data); test_data->received_fn = s_test_on_single_message; test_data->user_data = &message_test_data; /* altered the 9th byte to a single bit flip */ uint8_t empty_message[] = { 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x05, 0xc3, 0x48, 0xeb, 0x7d, 0x98, 0xc8, 0xff, }; struct aws_byte_cursor empty_message_cursor = aws_byte_cursor_from_array(empty_message, sizeof(empty_message)); ASSERT_SUCCESS(testing_channel_push_read_data(&s_test_data.testing_channel, empty_message_cursor)); ASSERT_UINT_EQUALS(AWS_ERROR_EVENT_STREAM_PRELUDE_CHECKSUM_FAILURE, message_test_data.last_error_code); return AWS_OP_SUCCESS; } AWS_TEST_CASE_FIXTURE( test_channel_handler_corrupted_crc_fails, s_fixture_setup, s_test_channel_handler_corrupted_crc_fails, s_fixture_shutdown, &s_test_data) /* send various valid messages in serial to make sure the happy path of message parsing is correct. */ static int s_test_channel_handler_msg_too_large_fails(struct aws_allocator *allocator, void *ctx) { (void)allocator; struct test_data *test_data = ctx; struct single_message_test_data message_test_data; AWS_ZERO_STRUCT(message_test_data); test_data->received_fn = s_test_on_single_message; test_data->user_data = &message_test_data; /* message is 1 byte too large */ uint8_t empty_message[] = { 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x94, 0xE8, 0xF6, 0x47, 0xC8, 0x86, 0xFB, 0xA0, }; struct aws_byte_cursor empty_message_cursor = aws_byte_cursor_from_array(empty_message, sizeof(empty_message)); ASSERT_SUCCESS(testing_channel_push_read_data(&s_test_data.testing_channel, empty_message_cursor)); ASSERT_UINT_EQUALS(AWS_ERROR_EVENT_STREAM_MESSAGE_FIELD_SIZE_EXCEEDED, message_test_data.last_error_code); return AWS_OP_SUCCESS; } AWS_TEST_CASE_FIXTURE( test_channel_handler_msg_too_large_fails, s_fixture_setup, s_test_channel_handler_msg_too_large_fails, s_fixture_shutdown, &s_test_data) static void s_on_message_written(struct aws_event_stream_message *message, int error_code, void *user_data) { struct single_message_test_data *msg_test_data = user_data; msg_test_data->last_error_code = error_code; if (!error_code) { struct aws_byte_buf message_buf = aws_byte_buf_from_array( aws_event_stream_message_buffer(message), aws_event_stream_message_total_length(message)); aws_event_stream_message_from_buffer_copy( &msg_test_data->received_msg_cpy, s_test_data.allocator, &message_buf); } } static int s_test_channel_handler_write_message(struct aws_allocator *allocator, void *ctx) { struct test_data *test_data = ctx; struct single_message_test_data message_test_data; AWS_ZERO_STRUCT(message_test_data); uint8_t empty_message[] = { 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x05, 0xc2, 0x48, 0xeb, 0x7d, 0x98, 0xc8, 0xff, }; struct aws_byte_buf empty_message_buf = aws_byte_buf_from_array(empty_message, sizeof(empty_message)); struct aws_event_stream_message msg_to_send; AWS_ZERO_STRUCT(msg_to_send); ASSERT_SUCCESS(aws_event_stream_message_from_buffer(&msg_to_send, allocator, &empty_message_buf)); ASSERT_SUCCESS(aws_event_stream_channel_handler_write_message( test_data->handler, &msg_to_send, s_on_message_written, &message_test_data)); testing_channel_drain_queued_tasks(&s_test_data.testing_channel); ASSERT_UINT_EQUALS(0x00000010, aws_event_stream_message_total_length(&message_test_data.received_msg_cpy)); ASSERT_UINT_EQUALS(0x00000000, aws_event_stream_message_headers_len(&message_test_data.received_msg_cpy)); ASSERT_UINT_EQUALS(0x05c248eb, aws_event_stream_message_prelude_crc(&message_test_data.received_msg_cpy)); ASSERT_UINT_EQUALS(0x7d98c8ff, aws_event_stream_message_message_crc(&message_test_data.received_msg_cpy)); aws_event_stream_message_clean_up(&message_test_data.received_msg_cpy); aws_event_stream_message_clean_up(&msg_to_send); struct aws_linked_list *list = testing_channel_get_written_message_queue(&test_data->testing_channel); ASSERT_FALSE(aws_linked_list_empty(list)); struct aws_linked_list_node *node = aws_linked_list_front(list); ASSERT_NOT_NULL(node); struct aws_io_message *message = AWS_CONTAINER_OF(node, struct aws_io_message, queueing_handle); struct aws_event_stream_message sent_msg; AWS_ZERO_STRUCT(sent_msg); ASSERT_SUCCESS(aws_event_stream_message_from_buffer(&sent_msg, allocator, &message->message_data)); ASSERT_UINT_EQUALS(0x00000010, aws_event_stream_message_total_length(&sent_msg)); ASSERT_UINT_EQUALS(0x00000000, aws_event_stream_message_headers_len(&sent_msg)); ASSERT_UINT_EQUALS(0x05c248eb, aws_event_stream_message_prelude_crc(&sent_msg)); ASSERT_UINT_EQUALS(0x7d98c8ff, aws_event_stream_message_message_crc(&sent_msg)); aws_event_stream_message_clean_up(&sent_msg); return AWS_OP_SUCCESS; } AWS_TEST_CASE_FIXTURE( test_channel_handler_write_message, s_fixture_setup, s_test_channel_handler_write_message, s_fixture_shutdown, &s_test_data)