Skip to content

Commit f794b5f

Browse files
committed
add tests
and revamp tester to make it easier to send custom responses
1 parent 2f0bef0 commit f794b5f

File tree

2 files changed

+180
-47
lines changed

2 files changed

+180
-47
lines changed

tests/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,10 @@ add_test_case(websocket_boot_report_unexpected_http_shutdown)
228228
add_test_case(websocket_boot_fail_from_handshake_rejection)
229229
add_test_case(websocket_boot_fail_before_handshake_rejection_body)
230230
add_test_case(websocket_boot_fail_before_handshake_rejection_stream_complete)
231+
add_test_case(websocket_boot_fail_from_invalid_upgrade_header)
232+
add_test_case(websocket_boot_fail_from_missing_upgrade_header)
233+
add_test_case(websocket_boot_fail_from_invalid_connection_header)
234+
add_test_case(websocket_boot_fail_from_invalid_sec_websocket_upgrade_header)
231235
add_test_case(websocket_handshake_key_max_length)
232236
add_test_case(websocket_handshake_key_randomness)
233237

tests/test_websocket_bootstrap.c

Lines changed: 176 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -50,28 +50,42 @@ static const struct aws_websocket_client_bootstrap_system_vtable s_mock_system_v
5050
/* Hardcoded value for "Sec-WebSocket-Key" header in handshake request. */
5151
static const char *s_sec_websocket_key_value = "dGhlIHNhbXBsZSBub25jZQ==";
5252

53-
static const struct aws_http_header s_accepted_response_headers[] = {
54-
{
55-
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Upgrade"),
56-
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("websocket"),
57-
},
58-
{
59-
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Connection"),
60-
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Upgrade"),
61-
},
62-
{
63-
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Sec-WebSocket-Accept"),
64-
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("s3pPLMBiTxaQ9kYGzzhZRbK+xOo="),
65-
},
53+
struct test_response {
54+
int status_code;
55+
struct aws_http_header headers[10];
56+
const char *body;
6657
};
6758

68-
static const struct aws_http_header s_rejected_response_headers[] = {
69-
{
70-
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Content-Length"),
71-
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("43"),
72-
},
59+
static const struct test_response s_accepted_response = {
60+
.status_code = 101,
61+
.headers =
62+
{
63+
{
64+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Upgrade"),
65+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("websocket"),
66+
},
67+
{
68+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Connection"),
69+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Upgrade"),
70+
},
71+
{
72+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Sec-WebSocket-Accept"),
73+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("s3pPLMBiTxaQ9kYGzzhZRbK+xOo="),
74+
},
75+
},
76+
};
77+
78+
static const struct test_response s_rejected_response = {
79+
.status_code = 403,
80+
.headers =
81+
{
82+
{
83+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Content-Length"),
84+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("43"),
85+
},
86+
},
87+
.body = "your request is bad and you should feel bad",
7388
};
74-
static const char *s_rejected_response_body = "your request is bad and you should feel bad";
7589

7690
/* If fail_at_step is set to one of these, that step will explicitly fail.
7791
* These represent the steps where an external system could fail. */
@@ -93,14 +107,11 @@ static struct tester {
93107
/* Settings */
94108
struct aws_allocator *alloc;
95109
enum boot_step fail_at_step;
96-
bool send_rejected_response;
97110

98111
struct aws_http_message *handshake_request;
99112

100-
int handshake_response_status;
101-
const struct aws_http_header *handshake_response_headers;
113+
const struct test_response *handshake_response;
102114
size_t num_handshake_response_headers;
103-
struct aws_byte_cursor handshake_response_body;
104115

105116
/* State */
106117
bool http_connect_called_successfully;
@@ -147,15 +158,16 @@ static int s_tester_init(struct aws_allocator *alloc) {
147158
/* Set default settings for tester (unless the test already configured it) */
148159
s_tester.alloc = alloc;
149160

150-
if (s_tester.send_rejected_response) {
151-
s_tester.handshake_response_status = 403;
152-
s_tester.handshake_response_headers = s_rejected_response_headers;
153-
s_tester.num_handshake_response_headers = AWS_ARRAY_SIZE(s_rejected_response_headers);
154-
s_tester.handshake_response_body = aws_byte_cursor_from_c_str(s_rejected_response_body);
155-
} else {
156-
s_tester.handshake_response_status = 101;
157-
s_tester.handshake_response_headers = s_accepted_response_headers;
158-
s_tester.num_handshake_response_headers = AWS_ARRAY_SIZE(s_accepted_response_headers);
161+
if (!s_tester.handshake_response) {
162+
s_tester.handshake_response = &s_accepted_response;
163+
}
164+
165+
/* Count number of headers being sent */
166+
for (size_t i = 0; i < AWS_ARRAY_SIZE(s_tester.handshake_response->headers); ++i) {
167+
if (s_tester.handshake_response->headers[i].name.len == 0) {
168+
break;
169+
}
170+
s_tester.num_handshake_response_headers = i + 1;
159171
}
160172

161173
return AWS_OP_SUCCESS;
@@ -343,7 +355,7 @@ static int s_mock_http_stream_get_incoming_response_status(const struct aws_http
343355
AWS_FATAL_ASSERT(!s_tester.http_stream_release_called);
344356
AWS_FATAL_ASSERT(out_status);
345357

346-
*out_status = s_tester.handshake_response_status;
358+
*out_status = s_tester.handshake_response->status_code;
347359
return AWS_OP_SUCCESS;
348360
}
349361

@@ -371,7 +383,7 @@ static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_
371383

372384
if (setup->handshake_response_status) {
373385
s_tester.websocket_setup_had_response_status = true;
374-
AWS_FATAL_ASSERT(*setup->handshake_response_status == s_tester.handshake_response_status);
386+
AWS_FATAL_ASSERT(*setup->handshake_response_status == s_tester.handshake_response->status_code);
375387

376388
/* If we're reporting a status code, we should also be reporting the headers */
377389
AWS_FATAL_ASSERT(setup->handshake_response_header_array != NULL);
@@ -380,7 +392,7 @@ static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_
380392
if (setup->handshake_response_header_array) {
381393
s_tester.websocket_setup_had_response_headers = true;
382394
AWS_FATAL_ASSERT(s_headers_eq(
383-
s_tester.handshake_response_headers,
395+
s_tester.handshake_response->headers,
384396
s_tester.num_handshake_response_headers,
385397
setup->handshake_response_header_array,
386398
setup->num_handshake_response_headers));
@@ -391,7 +403,7 @@ static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_
391403

392404
if (setup->handshake_response_body) {
393405
s_tester.websocket_setup_had_response_body = true;
394-
AWS_FATAL_ASSERT(aws_byte_cursor_eq(&s_tester.handshake_response_body, setup->handshake_response_body));
406+
AWS_FATAL_ASSERT(aws_byte_cursor_eq_c_str(setup->handshake_response_body, s_tester.handshake_response->body));
395407

396408
/* If we're reporting the body, we should also be reporting the headers and status code */
397409
AWS_FATAL_ASSERT(setup->handshake_response_status != NULL);
@@ -496,10 +508,13 @@ static int s_drive_websocket_connect(int *out_error_code) {
496508
}
497509

498510
/* Headers arrive, HTTP connection ends if callback returns error */
511+
enum aws_http_header_block header_block = s_tester.handshake_response->status_code / 100 == 1
512+
? AWS_HTTP_HEADER_BLOCK_INFORMATIONAL
513+
: AWS_HTTP_HEADER_BLOCK_MAIN;
499514
if (s_tester.http_stream_on_response_headers(
500515
s_mock_stream,
501-
s_tester.send_rejected_response ? AWS_HTTP_HEADER_BLOCK_MAIN : AWS_HTTP_HEADER_BLOCK_INFORMATIONAL,
502-
s_tester.handshake_response_headers,
516+
header_block,
517+
s_tester.handshake_response->headers,
503518
s_tester.num_handshake_response_headers,
504519
s_tester.http_stream_user_data)) {
505520

@@ -515,9 +530,7 @@ static int s_drive_websocket_connect(int *out_error_code) {
515530

516531
/* Headers are done, HTTP connection ends if error returned */
517532
if (s_tester.http_stream_on_response_header_block_done(
518-
s_mock_stream,
519-
s_tester.send_rejected_response ? AWS_HTTP_HEADER_BLOCK_MAIN : AWS_HTTP_HEADER_BLOCK_INFORMATIONAL,
520-
s_tester.http_stream_user_data)) {
533+
s_mock_stream, header_block, s_tester.http_stream_user_data)) {
521534
s_complete_http_stream_and_connection(aws_last_error());
522535
goto finishing_checks;
523536
}
@@ -527,8 +540,9 @@ static int s_drive_websocket_connect(int *out_error_code) {
527540
goto finishing_checks;
528541
}
529542

530-
if (s_tester.send_rejected_response) {
543+
if (header_block == AWS_HTTP_HEADER_BLOCK_MAIN) {
531544
/* If the response is a rejection, it will have a body */
545+
struct aws_byte_cursor body = aws_byte_cursor_from_c_str(s_tester.handshake_response->body);
532546

533547
/* HTTP connection could fail before the body is delivered */
534548
if (s_tester.fail_at_step == BOOT_STEP_BEFORE_REJECTION_BODY) {
@@ -537,10 +551,9 @@ static int s_drive_websocket_connect(int *out_error_code) {
537551
}
538552

539553
/* Response body arrives, HTTP connection ends if error returned */
540-
if (s_tester.handshake_response_body.len > 0) {
554+
if (body.len > 0) {
541555

542556
/* If we're testing the stream dying before the whole body is delivered, then only deliver a bit of it */
543-
struct aws_byte_cursor body = s_tester.handshake_response_body;
544557
if (s_tester.fail_at_step == BOOT_STEP_BEFORE_REJECTION_STREAM_COMPLETE) {
545558
body.len = 1;
546559
}
@@ -707,7 +720,7 @@ TEST_CASE(websocket_boot_report_unexpected_http_shutdown) {
707720
* an "unexpected" HTTP failure. */
708721
TEST_CASE(websocket_boot_fail_from_handshake_rejection) {
709722
(void)ctx;
710-
s_tester.send_rejected_response = true;
723+
s_tester.handshake_response = &s_rejected_response;
711724
ASSERT_SUCCESS(s_tester_init(allocator));
712725

713726
int websocket_connect_error_code;
@@ -722,7 +735,7 @@ TEST_CASE(websocket_boot_fail_from_handshake_rejection) {
722735
* Specifically, after the headers are received but before the body is received. */
723736
TEST_CASE(websocket_boot_fail_before_handshake_rejection_body) {
724737
(void)ctx;
725-
s_tester.send_rejected_response = true;
738+
s_tester.handshake_response = &s_rejected_response;
726739
s_tester.fail_at_step = BOOT_STEP_BEFORE_REJECTION_BODY;
727740
ASSERT_SUCCESS(s_tester_init(allocator));
728741

@@ -746,7 +759,7 @@ TEST_CASE(websocket_boot_fail_before_handshake_rejection_body) {
746759
* Specifically, after some of the body is received, but before the stream completes. */
747760
TEST_CASE(websocket_boot_fail_before_handshake_rejection_stream_complete) {
748761
(void)ctx;
749-
s_tester.send_rejected_response = true;
762+
s_tester.handshake_response = &s_rejected_response;
750763
s_tester.fail_at_step = BOOT_STEP_BEFORE_REJECTION_STREAM_COMPLETE;
751764
ASSERT_SUCCESS(s_tester_init(allocator));
752765

@@ -766,6 +779,122 @@ TEST_CASE(websocket_boot_fail_before_handshake_rejection_stream_complete) {
766779
return AWS_OP_SUCCESS;
767780
}
768781

782+
/* Function to be reused by all tests that pass a bad 101 response */
783+
static int s_websocket_boot_fail_from_bad_101_response(
784+
struct aws_allocator *alloc,
785+
const struct test_response *bad_response) {
786+
787+
ASSERT_INT_EQUALS(101, bad_response->status_code, "This helper function is only for bad 101 responses");
788+
789+
s_tester.handshake_response = bad_response;
790+
ASSERT_SUCCESS(s_tester_init(alloc));
791+
792+
int websocket_connect_error_code;
793+
ASSERT_SUCCESS(s_drive_websocket_connect(&websocket_connect_error_code));
794+
ASSERT_INT_EQUALS(AWS_ERROR_HTTP_WEBSOCKET_UPGRADE_FAILURE, websocket_connect_error_code);
795+
ASSERT_TRUE(s_tester.websocket_setup_had_response_status);
796+
ASSERT_TRUE(s_tester.websocket_setup_had_response_headers);
797+
ASSERT_FALSE(s_tester.websocket_setup_had_response_body);
798+
799+
ASSERT_SUCCESS(s_tester_clean_up());
800+
return AWS_OP_SUCCESS;
801+
}
802+
803+
TEST_CASE(websocket_boot_fail_from_invalid_upgrade_header) {
804+
(void)ctx;
805+
struct test_response bad_response = {
806+
.status_code = 101,
807+
.headers =
808+
{
809+
{
810+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Upgrade"),
811+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("HTTP/9000"), /* ought to be "websocket" */
812+
},
813+
{
814+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Connection"),
815+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Upgrade"),
816+
},
817+
{
818+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Sec-WebSocket-Accept"),
819+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("s3pPLMBiTxaQ9kYGzzhZRbK+xOo="),
820+
},
821+
},
822+
};
823+
return s_websocket_boot_fail_from_bad_101_response(allocator, &bad_response);
824+
}
825+
826+
TEST_CASE(websocket_boot_fail_from_missing_upgrade_header) {
827+
(void)ctx;
828+
struct test_response bad_response = {
829+
.status_code = 101,
830+
.headers =
831+
{
832+
/* Commenting out required header
833+
{
834+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Upgrade"),
835+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("websocket"),
836+
},
837+
*/
838+
{
839+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Connection"),
840+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Upgrade"),
841+
},
842+
{
843+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Sec-WebSocket-Accept"),
844+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("s3pPLMBiTxaQ9kYGzzhZRbK+xOo="),
845+
},
846+
},
847+
};
848+
return s_websocket_boot_fail_from_bad_101_response(allocator, &bad_response);
849+
}
850+
851+
TEST_CASE(websocket_boot_fail_from_invalid_connection_header) {
852+
(void)ctx;
853+
struct test_response bad_response = {
854+
.status_code = 101,
855+
.headers =
856+
{
857+
{
858+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Upgrade"),
859+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("websocket"),
860+
},
861+
{
862+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Connection"),
863+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("HeartToHeart"), /* ought to be "Upgrade" */
864+
},
865+
{
866+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Sec-WebSocket-Accept"),
867+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("s3pPLMBiTxaQ9kYGzzhZRbK+xOo="),
868+
},
869+
},
870+
};
871+
return s_websocket_boot_fail_from_bad_101_response(allocator, &bad_response);
872+
}
873+
874+
TEST_CASE(websocket_boot_fail_from_invalid_sec_websocket_upgrade_header) {
875+
(void)ctx;
876+
struct test_response bad_response = {
877+
.status_code = 101,
878+
.headers =
879+
{
880+
{
881+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Upgrade"),
882+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("websocket"),
883+
},
884+
{
885+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Connection"),
886+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Upgrade"),
887+
},
888+
{
889+
.name = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("Sec-WebSocket-Accept"),
890+
/* ought to be "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="*/
891+
.value = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("S3PPLMBITXAQ9KYGZZHZRBK+XOO="),
892+
},
893+
},
894+
};
895+
return s_websocket_boot_fail_from_bad_101_response(allocator, &bad_response);
896+
}
897+
769898
/* Check that AWS_WEBSOCKET_MAX_HANDSHAKE_KEY_LENGTH is sufficiently large */
770899
TEST_CASE(websocket_handshake_key_max_length) {
771900
(void)allocator;

0 commit comments

Comments
 (0)