@@ -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. */
5151static 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. */
708721TEST_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. */
723736TEST_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. */
747760TEST_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 */
770899TEST_CASE (websocket_handshake_key_max_length ) {
771900 (void )allocator ;
0 commit comments