/** * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * SPDX-License-Identifier: Apache-2.0. */ #include #include #include struct test_decoder_data { struct aws_event_stream_message_prelude latest_prelude; struct aws_array_list headers_list; uint8_t *latest_payload; size_t written; struct aws_allocator *alloc; int latest_error; uint32_t message_crc; }; static void s_decoder_test_on_payload_segment( struct aws_event_stream_streaming_decoder *decoder, struct aws_byte_buf *data, int8_t final_segment, void *user_data) { (void)final_segment; (void)decoder; struct test_decoder_data *decoder_data = (struct test_decoder_data *)user_data; memcpy(decoder_data->latest_payload + decoder_data->written, data->buffer, data->len); decoder_data->written += data->len; } static void s_decoder_test_on_prelude_received( struct aws_event_stream_streaming_decoder *decoder, struct aws_event_stream_message_prelude *prelude, void *user_data) { (void)decoder; struct test_decoder_data *decoder_data = (struct test_decoder_data *)user_data; decoder_data->latest_prelude = *prelude; if (decoder_data->latest_payload) { aws_mem_release(decoder_data->alloc, decoder_data->latest_payload); } const size_t payload_size = decoder_data->latest_prelude.total_len - AWS_EVENT_STREAM_PRELUDE_LENGTH - AWS_EVENT_STREAM_TRAILER_LENGTH - decoder_data->latest_prelude.headers_len; if (payload_size) { decoder_data->latest_payload = aws_mem_acquire(decoder_data->alloc, payload_size); } else { decoder_data->latest_payload = NULL; } decoder_data->written = 0; } static void s_decoder_test_header_received( struct aws_event_stream_streaming_decoder *decoder, struct aws_event_stream_message_prelude *prelude, struct aws_event_stream_header_value_pair *header, void *user_data) { (void)decoder; (void)prelude; struct test_decoder_data *decoder_data = (struct test_decoder_data *)user_data; aws_event_stream_add_header(&decoder_data->headers_list, header); } static void s_decoder_test_on_complete( struct aws_event_stream_streaming_decoder *decoder, uint32_t message_crc, void *user_data) { (void)decoder; struct test_decoder_data *decoder_data = (struct test_decoder_data *)user_data; decoder_data->message_crc = message_crc; } static void s_decoder_test_on_error( struct aws_event_stream_streaming_decoder *decoder, struct aws_event_stream_message_prelude *prelude, int error_code, const char *message, void *user_data) { (void)decoder; (void)prelude; (void)message; struct test_decoder_data *decoder_data = (struct test_decoder_data *)user_data; decoder_data->latest_error = error_code; } static int s_test_streaming_decoder_incoming_no_op_valid_single_message_fn(struct aws_allocator *allocator, void *ctx) { uint8_t test_data[] = { 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x05, 0xc2, 0x48, 0xeb, 0x7d, 0x98, 0xc8, 0xff, }; (void)ctx; struct test_decoder_data decoder_data = {.latest_payload = 0, .written = 0, .alloc = allocator, .latest_error = 0}; struct aws_event_stream_streaming_decoder_options decoder_options = { .on_payload_segment = s_decoder_test_on_payload_segment, .on_prelude = s_decoder_test_on_prelude_received, .on_header = s_decoder_test_header_received, .on_complete = s_decoder_test_on_complete, .on_error = s_decoder_test_on_error, .user_data = &decoder_data}; struct aws_event_stream_streaming_decoder decoder; aws_event_stream_streaming_decoder_init_from_options(&decoder, allocator, &decoder_options); struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data, sizeof(test_data)); ASSERT_SUCCESS( aws_event_stream_streaming_decoder_pump(&decoder, &test_buf), "Message validation should have succeeded"); ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); ASSERT_INT_EQUALS(0x00000010, decoder_data.latest_prelude.total_len, "Message length should have been 0x10"); ASSERT_INT_EQUALS(0x00000000, decoder_data.latest_prelude.headers_len, "Headers Length should have been 0x00"); ASSERT_INT_EQUALS(0x05c248eb, decoder_data.latest_prelude.prelude_crc, "Prelude CRC should have been 0x8c335472"); ASSERT_INT_EQUALS(0, decoder_data.written, "No payload data should have been written"); if (decoder_data.latest_payload) { aws_mem_release(allocator, decoder_data.latest_payload); } ASSERT_UINT_EQUALS(0x7D98C8FF, decoder_data.message_crc); aws_event_stream_streaming_decoder_clean_up(&decoder); return 0; } AWS_TEST_CASE( test_streaming_decoder_incoming_no_op_valid_single_message, s_test_streaming_decoder_incoming_no_op_valid_single_message_fn) static int s_test_streaming_decoder_incoming_application_no_headers_fn(struct aws_allocator *allocator, void *ctx) { uint8_t test_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, }; (void)ctx; struct test_decoder_data decoder_data = {.latest_payload = 0, .written = 0, .alloc = allocator, .latest_error = 0}; struct aws_event_stream_streaming_decoder_options decoder_options = { .on_payload_segment = s_decoder_test_on_payload_segment, .on_prelude = s_decoder_test_on_prelude_received, .on_header = s_decoder_test_header_received, .on_complete = s_decoder_test_on_complete, .on_error = s_decoder_test_on_error, .user_data = &decoder_data}; struct aws_event_stream_streaming_decoder decoder; aws_event_stream_streaming_decoder_init_from_options(&decoder, allocator, &decoder_options); struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data, sizeof(test_data)); ASSERT_SUCCESS( aws_event_stream_streaming_decoder_pump(&decoder, &test_buf), "Message validation should have succeeded"); ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); ASSERT_INT_EQUALS(0x0000001D, decoder_data.latest_prelude.total_len, "Message length should have been 0x1D"); ASSERT_INT_EQUALS(0x00000000, decoder_data.latest_prelude.headers_len, "Headers Length should have been 0x00"); ASSERT_INT_EQUALS(0xfd528c5a, decoder_data.latest_prelude.prelude_crc, "Prelude CRC should have been 0xfd528c5a"); const char *expected_str = "{'foo':'bar'}"; size_t payload_len = decoder_data.latest_prelude.total_len - AWS_EVENT_STREAM_PRELUDE_LENGTH - AWS_EVENT_STREAM_TRAILER_LENGTH - decoder_data.latest_prelude.headers_len; ASSERT_INT_EQUALS( strlen(expected_str), payload_len, "payload length should have been %d", (int)(strlen(expected_str))); ASSERT_BIN_ARRAYS_EQUALS( expected_str, strlen(expected_str), decoder_data.latest_payload, payload_len, "payload should have been %s", expected_str); if (decoder_data.latest_payload) { aws_mem_release(allocator, decoder_data.latest_payload); } ASSERT_UINT_EQUALS(0xC3653936, decoder_data.message_crc); aws_event_stream_streaming_decoder_clean_up(&decoder); return 0; } AWS_TEST_CASE( test_streaming_decoder_incoming_application_no_headers, s_test_streaming_decoder_incoming_application_no_headers_fn) static int s_test_streaming_decoder_incoming_application_one_compressed_header_pair_valid_fn( struct aws_allocator *allocator, void *ctx) { (void)ctx; uint8_t 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 test_decoder_data decoder_data = { .latest_payload = 0, .written = 0, .alloc = allocator, .latest_error = 0, }; aws_event_stream_headers_list_init(&decoder_data.headers_list, allocator); struct aws_event_stream_streaming_decoder_options decoder_options = { .on_payload_segment = s_decoder_test_on_payload_segment, .on_prelude = s_decoder_test_on_prelude_received, .on_header = s_decoder_test_header_received, .on_complete = s_decoder_test_on_complete, .on_error = s_decoder_test_on_error, .user_data = &decoder_data}; struct aws_event_stream_streaming_decoder decoder; aws_event_stream_streaming_decoder_init_from_options(&decoder, allocator, &decoder_options); struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data, sizeof(test_data)); ASSERT_SUCCESS( aws_event_stream_streaming_decoder_pump(&decoder, &test_buf), "Message validation should have succeeded"); ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); ASSERT_INT_EQUALS(0x0000003D, decoder_data.latest_prelude.total_len, "Message length should have been 0x3D"); ASSERT_INT_EQUALS(0x00000020, decoder_data.latest_prelude.headers_len, "Headers Length should have been 0x20"); ASSERT_INT_EQUALS(0x07FD8396, decoder_data.latest_prelude.prelude_crc, "Prelude CRC should have been 0x07FD8396"); const char *content_type = "content-type"; const char *content_type_value = "application/json"; struct aws_event_stream_header_value_pair latest_header; aws_array_list_get_at(&decoder_data.headers_list, &latest_header, 0); struct aws_byte_buf latest_header_value = aws_event_stream_header_value_as_string(&latest_header); ASSERT_BIN_ARRAYS_EQUALS( content_type, strlen(content_type), latest_header.header_name, latest_header.header_name_len, "header name should have been %s", content_type); ASSERT_BIN_ARRAYS_EQUALS( content_type_value, strlen(content_type_value), latest_header_value.buffer, latest_header_value.len, "header value should have been %s", content_type_value); const char *expected_str = "{'foo':'bar'}"; size_t payload_len = decoder_data.latest_prelude.total_len - AWS_EVENT_STREAM_PRELUDE_LENGTH - AWS_EVENT_STREAM_TRAILER_LENGTH - decoder_data.latest_prelude.headers_len; ASSERT_INT_EQUALS( strlen(expected_str), payload_len, "payload length should have been %d", (int)(strlen(expected_str))); ASSERT_BIN_ARRAYS_EQUALS( expected_str, strlen(expected_str), decoder_data.latest_payload, payload_len, "payload should have been %s", expected_str); if (decoder_data.latest_payload) { aws_mem_release(allocator, decoder_data.latest_payload); } ASSERT_UINT_EQUALS(0x8D9C08B1, decoder_data.message_crc); aws_event_stream_headers_list_cleanup(&decoder_data.headers_list); return 0; } AWS_TEST_CASE( test_streaming_decoder_incoming_application_one_compressed_header_pair_valid, s_test_streaming_decoder_incoming_application_one_compressed_header_pair_valid_fn) static int s_test_streaming_decoder_incoming_application_one_int32_header_pair_valid_fn( struct aws_allocator *allocator, void *ctx) { (void)ctx; /* clang-format off */ uint8_t test_data[] = { 0x00, 0x00, 0x00, 0x1b, /* total length */ 0x00, 0x00, 0x00, 0x0b, /* headers length */ 0xe5, 0xc0, 0xa0, 0x72, /* prelude crc */ 0x05, /* header name length */ 'e', 'v', 'e', 'n', 't', /* header name */ 0x04, /* header value type */ 0x00, 0x00, /* header value length */ 0x00, 0x20, /* header value */ 0x04, 0xa1, 0xd4, 0x7c /* message crc */ }; /* clang-format on */ struct test_decoder_data decoder_data = { .latest_payload = 0, .written = 0, .alloc = allocator, .latest_error = 0, }; aws_event_stream_headers_list_init(&decoder_data.headers_list, allocator); struct aws_event_stream_streaming_decoder_options decoder_options = { .on_payload_segment = s_decoder_test_on_payload_segment, .on_prelude = s_decoder_test_on_prelude_received, .on_header = s_decoder_test_header_received, .on_complete = s_decoder_test_on_complete, .on_error = s_decoder_test_on_error, .user_data = &decoder_data}; struct aws_event_stream_streaming_decoder decoder; aws_event_stream_streaming_decoder_init_from_options(&decoder, allocator, &decoder_options); struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data, sizeof(test_data)); ASSERT_SUCCESS( aws_event_stream_streaming_decoder_pump(&decoder, &test_buf), "Message validation should have succeeded"); ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); ASSERT_INT_EQUALS(0x0000001B, decoder_data.latest_prelude.total_len, "Message length should have been 0x1B"); ASSERT_INT_EQUALS(0x0000000B, decoder_data.latest_prelude.headers_len, "Headers Length should have been 0xB"); ASSERT_INT_EQUALS(0xE5C0A072, decoder_data.latest_prelude.prelude_crc, "Prelude CRC should have been 0xE5C0A072"); const char *expected_header_name = "event"; struct aws_event_stream_header_value_pair latest_header; aws_array_list_get_at(&decoder_data.headers_list, &latest_header, 0); ASSERT_BIN_ARRAYS_EQUALS( expected_header_name, strlen(expected_header_name), latest_header.header_name, latest_header.header_name_len, "header name should have been %s", expected_header_name); int32_t latest_header_value = aws_event_stream_header_value_as_int32(&latest_header); ASSERT_INT_EQUALS(0x00000020, latest_header_value, "Header value should have been 0x00000020"); ASSERT_UINT_EQUALS(0x04A1D47C, decoder_data.message_crc); aws_event_stream_headers_list_cleanup(&decoder_data.headers_list); return 0; } AWS_TEST_CASE( test_streaming_decoder_incoming_application_one_int32_header_pair_valid, s_test_streaming_decoder_incoming_application_one_int32_header_pair_valid_fn) static int s_test_streaming_decoder_incoming_application_one_bool_header_pair_valid_fn( struct aws_allocator *allocator, void *ctx) { (void)ctx; /* clang-format off */ uint8_t test_data[] = { 0x00, 0x00, 0x00, 0x17, /* total length */ 0x00, 0x00, 0x00, 0x07, /* headers length */ 0x29, 0x86, 0x01, 0x58, /* prelude crc */ 0x05, /* header name length */ 'e', 'v', 'e', 'n', 't', /* header name */ 0x00, /* header value type */ 0x4b, 0x4d, 0x2b, 0xe7 /* message crc */ }; /* clang-format on */ struct test_decoder_data decoder_data = { .latest_payload = 0, .written = 0, .alloc = allocator, .latest_error = 0, }; aws_event_stream_headers_list_init(&decoder_data.headers_list, allocator); struct aws_event_stream_streaming_decoder_options decoder_options = { .on_payload_segment = s_decoder_test_on_payload_segment, .on_prelude = s_decoder_test_on_prelude_received, .on_header = s_decoder_test_header_received, .on_complete = s_decoder_test_on_complete, .on_error = s_decoder_test_on_error, .user_data = &decoder_data}; struct aws_event_stream_streaming_decoder decoder; aws_event_stream_streaming_decoder_init_from_options(&decoder, allocator, &decoder_options); struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data, sizeof(test_data)); ASSERT_SUCCESS( aws_event_stream_streaming_decoder_pump(&decoder, &test_buf), "Message validation should have succeeded"); ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); ASSERT_INT_EQUALS(0x00000017, decoder_data.latest_prelude.total_len, "Message length should have been 0x17"); ASSERT_INT_EQUALS(0x00000007, decoder_data.latest_prelude.headers_len, "Headers Length should have been 0x7"); ASSERT_INT_EQUALS(0x29860158, decoder_data.latest_prelude.prelude_crc, "Prelude CRC should have been 0x29860158"); const char *expected_header_name = "event"; struct aws_event_stream_header_value_pair latest_header; aws_array_list_get_at(&decoder_data.headers_list, &latest_header, 0); ASSERT_BIN_ARRAYS_EQUALS( expected_header_name, strlen(expected_header_name), latest_header.header_name, latest_header.header_name_len, "header name should have been %s", expected_header_name); int8_t latest_header_value = aws_event_stream_header_value_as_bool(&latest_header); ASSERT_INT_EQUALS(1, latest_header_value, "Header value should have been true"); ASSERT_UINT_EQUALS(0x4B4D2BE7, decoder_data.message_crc); aws_event_stream_headers_list_cleanup(&decoder_data.headers_list); return 0; } AWS_TEST_CASE( test_streaming_decoder_incoming_application_one_bool_header_pair_valid, s_test_streaming_decoder_incoming_application_one_bool_header_pair_valid_fn) static int s_test_streaming_decoder_incoming_multiple_messages_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; uint8_t test_data[] = { /* message 1 */ 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x05, 0xc2, 0x48, 0xeb, 0x7d, 0x98, 0xc8, 0xff, /* message 2 */ 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, }; size_t first_message_size = 0x10; size_t read_size = 7; /* make this a weird number to force edge case coverage in the parser. This will fall into the middle of message boundaries and preludes. */ struct test_decoder_data decoder_data = {.latest_payload = 0, .written = 0, .alloc = allocator, .latest_error = 0}; aws_event_stream_headers_list_init(&decoder_data.headers_list, allocator); struct aws_event_stream_streaming_decoder_options decoder_options = { .on_payload_segment = s_decoder_test_on_payload_segment, .on_prelude = s_decoder_test_on_prelude_received, .on_header = s_decoder_test_header_received, .on_complete = s_decoder_test_on_complete, .on_error = s_decoder_test_on_error, .user_data = &decoder_data}; struct aws_event_stream_streaming_decoder decoder; aws_event_stream_streaming_decoder_init_from_options(&decoder, allocator, &decoder_options); size_t current_written = 0; int err_code = 0; while (current_written < first_message_size && !err_code) { struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data + current_written, read_size); err_code = aws_event_stream_streaming_decoder_pump(&decoder, &test_buf); current_written += read_size; } /* we should have written into the second message, but prior to the new prelude being found. check first message was parsed correctly */ ASSERT_SUCCESS(err_code, "Message validation should have succeeded"); ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); ASSERT_INT_EQUALS(0x00000010, decoder_data.latest_prelude.total_len, "Message length should have been 0x10"); ASSERT_INT_EQUALS(0x00000000, decoder_data.latest_prelude.headers_len, "Headers Length should have been 0x00"); ASSERT_INT_EQUALS(0x05c248eb, decoder_data.latest_prelude.prelude_crc, "Prelude CRC should have been 0x8c335472"); ASSERT_INT_EQUALS(0, decoder_data.written, "No payload data should have been written"); ASSERT_UINT_EQUALS(0x7D98C8FF, decoder_data.message_crc); while (current_written < sizeof(test_data) && !err_code) { size_t to_write = current_written + read_size < sizeof(test_data) ? read_size : sizeof(test_data) - current_written; struct aws_byte_buf test_buf = aws_byte_buf_from_array(test_data + current_written, to_write); err_code = aws_event_stream_streaming_decoder_pump(&decoder, &test_buf); current_written += to_write; } /* Second message should have been found and fully parsed at this point. */ ASSERT_SUCCESS(err_code, "Message validation should have succeeded"); ASSERT_SUCCESS(decoder_data.latest_error, "No Error callback shouldn't have been called"); ASSERT_INT_EQUALS(0x0000003D, decoder_data.latest_prelude.total_len, "Message length should have been 0x3D"); ASSERT_INT_EQUALS(0x00000020, decoder_data.latest_prelude.headers_len, "Headers Length should have been 0x20"); ASSERT_INT_EQUALS(0x07FD8396, decoder_data.latest_prelude.prelude_crc, "Prelude CRC should have been 0x07FD8396"); const char *content_type = "content-type"; const char *content_type_value = "application/json"; struct aws_event_stream_header_value_pair latest_header; aws_array_list_get_at(&decoder_data.headers_list, &latest_header, 0); struct aws_byte_buf latest_header_value = aws_event_stream_header_value_as_string(&latest_header); ASSERT_BIN_ARRAYS_EQUALS( content_type, strlen(content_type), latest_header.header_name, latest_header.header_name_len, "header name should have been %s", content_type); ASSERT_BIN_ARRAYS_EQUALS( content_type_value, strlen(content_type_value), latest_header_value.buffer, latest_header_value.len, "header value should have been %s", content_type_value); const char *expected_str = "{'foo':'bar'}"; size_t payload_len = decoder_data.latest_prelude.total_len - AWS_EVENT_STREAM_PRELUDE_LENGTH - AWS_EVENT_STREAM_TRAILER_LENGTH - decoder_data.latest_prelude.headers_len; ASSERT_INT_EQUALS( strlen(expected_str), payload_len, "payload length should have been %d", (int)(strlen(expected_str))); ASSERT_BIN_ARRAYS_EQUALS( expected_str, strlen(expected_str), decoder_data.latest_payload, payload_len, "payload should have been %s", expected_str); if (decoder_data.latest_payload) { aws_mem_release(allocator, decoder_data.latest_payload); } ASSERT_UINT_EQUALS(0x8D9C08B1, decoder_data.message_crc); aws_event_stream_streaming_decoder_clean_up(&decoder); aws_event_stream_headers_list_cleanup(&decoder_data.headers_list); return 0; } AWS_TEST_CASE(test_streaming_decoder_incoming_multiple_messages, s_test_streaming_decoder_incoming_multiple_messages_fn)