From 4a080b8cfc9a29387457eb8f4088dd39a1ec038e Mon Sep 17 00:00:00 2001 From: Igor Abdrakhimov Date: Thu, 9 Nov 2023 16:58:07 -0800 Subject: [PATCH] Check that messages are not reordering --- source/secure_tunneling.c | 2 +- tests/secure_tunnel_tests.c | 82 ++++++++++++++++++++++++++++++------- 2 files changed, 68 insertions(+), 16 deletions(-) diff --git a/source/secure_tunneling.c b/source/secure_tunneling.c index b5e11394..61ce7dc2 100644 --- a/source/secure_tunneling.c +++ b/source/secure_tunneling.c @@ -1712,7 +1712,7 @@ static uint64_t s_aws_secure_tunnel_compute_operational_state_service_time( struct aws_secure_tunnel *secure_tunnel, uint64_t now) { - /* If a message is in transit down the WebSocket, then wait for it to complete */ + /* A message is in transit down the WebSocket, schedule it to the nearest time. */ if (secure_tunnel->pending_write_completion) { return now; } diff --git a/tests/secure_tunnel_tests.c b/tests/secure_tunnel_tests.c index d056725e..72e7587b 100644 --- a/tests/secure_tunnel_tests.c +++ b/tests/secure_tunnel_tests.c @@ -20,7 +20,7 @@ #include #include #include -#include +#include #define PAYLOAD_BYTE_LENGTH_PREFIX 2 AWS_STATIC_STRING_FROM_LITERAL(s_access_token, "IAmAnAccessToken"); @@ -85,6 +85,10 @@ typedef int(aws_secure_tunnel_mock_test_fixture_header_check_fn)( const struct aws_http_headers *request_headers, void *user_data); +typedef void(aws_secure_tunnel_mock_test_fixture_on_message_received_fn)( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_message_view *message_view); + struct aws_secure_tunnel_mock_test_fixture { struct aws_allocator *allocator; @@ -101,7 +105,7 @@ struct aws_secure_tunnel_mock_test_fixture { struct aws_secure_tunnel_vtable secure_tunnel_vtable; aws_secure_tunnel_mock_test_fixture_header_check_fn *header_check; - + aws_secure_tunnel_mock_test_fixture_on_message_received_fn *on_server_message_received; struct aws_mutex lock; struct aws_condition_variable signal; bool listener_destroyed; @@ -120,6 +124,7 @@ struct aws_secure_tunnel_mock_test_fixture { struct aws_byte_buf last_message_payload_buf; + /* The following fields are intended to validate things from the mocked secure tunnel perspective. */ int secure_tunnel_message_received_count; int secure_tunnel_message_sent_count; int secure_tunnel_stream_started_count; @@ -130,6 +135,8 @@ struct aws_secure_tunnel_mock_test_fixture { int secure_tunnel_message_sent_count_target; int secure_tunnel_message_sent_connection_reset_count; int secure_tunnel_message_sent_data_count; + int secure_tunnel_message_sent_previous_data_value; + bool secure_tunnel_messages_received_in_order; bool on_send_message_complete_fired; int on_send_message_complete_fired_cnt; @@ -637,7 +644,7 @@ int aws_websocket_client_connect_mock_fn(const struct aws_websocket_client_conne return AWS_OP_SUCCESS; } -/* Mock for a server-side receiving WebSocket frames. */ +/* Mock for a server-side code receiving WebSocket frames. */ void aws_secure_tunnel_test_on_message_received( struct aws_secure_tunnel *secure_tunnel, struct aws_secure_tunnel_message_view *message_view) { @@ -660,6 +667,41 @@ void aws_secure_tunnel_test_on_message_received( aws_mutex_unlock(&test_fixture->lock); } +void aws_secure_tunnel_test_on_message_received_with_order_validation( + struct aws_secure_tunnel *secure_tunnel, + struct aws_secure_tunnel_message_view *message_view) { + (void)message_view; + struct aws_secure_tunnel_mock_test_fixture *test_fixture = secure_tunnel->config->user_data; + + aws_mutex_lock(&test_fixture->lock); + test_fixture->secure_tunnel_message_sent_count++; + int32_t data_value; + switch (message_view->type) { + case AWS_SECURE_TUNNEL_MT_DATA: + test_fixture->secure_tunnel_message_sent_data_count++; + sscanf((const char *)message_view->payload->ptr, "%" PRIu32, &data_value); + if (test_fixture->secure_tunnel_message_sent_previous_data_value > 0 && + data_value != test_fixture->secure_tunnel_message_sent_previous_data_value + 1) { + /* We cannot assert in this callback, log error and set corresponding fail flag instead. */ + fprintf( + stderr, + "ERROR: secure tunnel expected %d, received %d\n", + test_fixture->secure_tunnel_message_sent_previous_data_value + 1, + data_value); + test_fixture->secure_tunnel_messages_received_in_order = false; + } + test_fixture->secure_tunnel_message_sent_previous_data_value = data_value; + break; + case AWS_SECURE_TUNNEL_MT_CONNECTION_RESET: + test_fixture->secure_tunnel_message_sent_connection_reset_count++; + break; + default: + break; + } + aws_condition_variable_notify_all(&test_fixture->signal); + aws_mutex_unlock(&test_fixture->lock); +} + struct aws_secure_tunnel_mock_websocket_send_frame_task { struct aws_task task; struct aws_secure_tunnel_mock_test_fixture *test_fixture; @@ -679,12 +721,14 @@ static void s_secure_tunneling_mock_websocket_send_frame_task_fn( return; } + struct aws_secure_tunnel_mock_test_fixture *test_fixture = send_task->test_fixture; + aws_secure_tunnel_deserialize_message_from_cursor( - send_task->test_fixture->secure_tunnel, &send_task->pair->cur, &aws_secure_tunnel_test_on_message_received); + test_fixture->secure_tunnel, &send_task->pair->cur, test_fixture->on_server_message_received); - send_task->on_complete((struct aws_websocket *)send_task->test_fixture, AWS_OP_SUCCESS, send_task->pair); + send_task->on_complete((struct aws_websocket *)test_fixture, AWS_OP_SUCCESS, send_task->pair); - aws_mem_release(send_task->test_fixture->allocator, send_task); + aws_mem_release(test_fixture->allocator, send_task); } int aws_websocket_send_frame_mock_fn( @@ -812,6 +856,9 @@ int aws_secure_tunnel_mock_test_fixture_init( test_fixture->secure_tunnel_vtable.aws_websocket_close_fn = aws_websocket_close_mock_fn; test_fixture->secure_tunnel_vtable.vtable_user_data = test_fixture; + test_fixture->on_server_message_received = aws_secure_tunnel_test_on_message_received; + test_fixture->secure_tunnel_messages_received_in_order = true; + aws_secure_tunnel_set_vtable(test_fixture->secure_tunnel, &test_fixture->secure_tunnel_vtable); return AWS_OP_SUCCESS; @@ -1414,6 +1461,7 @@ static int s_secure_tunneling_max_payload_exceed_test_fn(struct aws_allocator *a AWS_TEST_CASE(secure_tunneling_max_payload_exceed_test, s_secure_tunneling_max_payload_exceed_test_fn) +/* Test that messages sent by a user one after another without delay are actually being sent to server. */ static int s_secure_tunneling_subsequent_writes_test_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; struct secure_tunnel_test_options test_options; @@ -1421,6 +1469,8 @@ static int s_secure_tunneling_subsequent_writes_test_fn(struct aws_allocator *al aws_secure_tunnel_mock_test_init(allocator, &test_options, &test_fixture, AWS_SECURE_TUNNELING_DESTINATION_MODE); struct aws_secure_tunnel *secure_tunnel = test_fixture.secure_tunnel; + test_fixture.on_server_message_received = aws_secure_tunnel_test_on_message_received_with_order_validation; + ASSERT_SUCCESS(aws_secure_tunnel_start(secure_tunnel)); s_wait_for_connected_successfully(&test_fixture); @@ -1437,30 +1487,32 @@ static int s_secure_tunneling_subsequent_writes_test_fn(struct aws_allocator *al s_wait_for_stream_started(&test_fixture); ASSERT_TRUE(s_secure_tunnel_check_active_stream_id(secure_tunnel, &service_1, 1)); - int cnt = 3; - for (int i = 0; i < cnt; ++i) { - uint8_t *buf = aws_mem_calloc(test_fixture.allocator, 9, 1); - struct aws_byte_cursor payload_cursor = { + int total_messages = 100; + for (int i = 0; i < total_messages; ++i) { + uint8_t buf[10]; + struct aws_byte_cursor s_payload_buf = { .ptr = buf, - .len = 9, + .len = 10, }; + snprintf((char *)buf, sizeof(buf), "%d", i); + struct aws_secure_tunnel_message_view data_message_view = { .type = AWS_SECURE_TUNNEL_MT_DATA, .stream_id = 0, .service_id = &service_1, - .payload = &payload_cursor, + .payload = &s_payload_buf, }; int result = aws_secure_tunnel_send_message(secure_tunnel, &data_message_view); ASSERT_INT_EQUALS(result, AWS_OP_SUCCESS); - aws_mem_release(test_fixture.allocator, buf); } - /* Since there is no feedback on successful sending, simply sleep. */ + /* 1 second must be enough to send few messages. */ aws_thread_current_sleep(aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL)); - ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_fired_cnt, cnt); + ASSERT_INT_EQUALS(test_fixture.on_send_message_complete_fired_cnt, total_messages); + ASSERT_TRUE(test_fixture.secure_tunnel_messages_received_in_order); ASSERT_SUCCESS(aws_secure_tunnel_stop(secure_tunnel)); s_wait_for_connection_shutdown(&test_fixture);