@@ -1978,3 +1978,125 @@ TEST_CASE(h2_client_push_promise_automatically_rejected) {
19781978 client_stream_tester_clean_up (& stream_tester );
19791979 return s_tester_clean_up ();
19801980}
1981+
1982+ /* Test client receives the GOAWAY frame, stop creating new stream and complete the streams whose id are higher than the
1983+ * last stream id included in GOAWAY frame */
1984+ TEST_CASE (h2_client_conn_receive_goaway ) {
1985+ ASSERT_SUCCESS (s_tester_init (allocator , ctx ));
1986+
1987+ /* get connection preface and acks out of the way */
1988+ ASSERT_SUCCESS (h2_fake_peer_send_connection_preface_default_settings (& s_tester .peer ));
1989+ testing_channel_drain_queued_tasks (& s_tester .testing_channel );
1990+ ASSERT_SUCCESS (h2_fake_peer_decode_messages_from_testing_channel (& s_tester .peer ));
1991+
1992+ /* send multiple requests */
1993+ enum { NUM_STREAMS = 3 };
1994+ struct aws_http_message * requests [NUM_STREAMS ];
1995+ struct aws_http_header request_headers_src [NUM_STREAMS ][3 ] = {
1996+ {
1997+ DEFINE_HEADER (":method" , "GET" ),
1998+ DEFINE_HEADER (":scheme" , "https" ),
1999+ DEFINE_HEADER (":path" , "/a.txt" ),
2000+ },
2001+ {
2002+ DEFINE_HEADER (":method" , "GET" ),
2003+ DEFINE_HEADER (":scheme" , "https" ),
2004+ DEFINE_HEADER (":path" , "/b.txt" ),
2005+ },
2006+ {
2007+ DEFINE_HEADER (":method" , "GET" ),
2008+ DEFINE_HEADER (":scheme" , "https" ),
2009+ DEFINE_HEADER (":path" , "/c.txt" ),
2010+ },
2011+ };
2012+ struct client_stream_tester stream_testers [NUM_STREAMS ];
2013+ for (size_t i = 0 ; i < NUM_STREAMS ; ++ i ) {
2014+ requests [i ] = aws_http_message_new_request (allocator );
2015+ aws_http_message_add_header_array (requests [i ], request_headers_src [i ], AWS_ARRAY_SIZE (request_headers_src [i ]));
2016+ }
2017+ /* Send the first two requests */
2018+ ASSERT_SUCCESS (s_stream_tester_init (& stream_testers [0 ], requests [0 ]));
2019+ ASSERT_SUCCESS (s_stream_tester_init (& stream_testers [1 ], requests [1 ]));
2020+ testing_channel_drain_queued_tasks (& s_tester .testing_channel );
2021+
2022+ /* fake peer send a GOAWAY frame indicating only the first request will be processed */
2023+ uint32_t stream_id = aws_http_stream_get_id (stream_testers [0 ].stream );
2024+ struct aws_byte_cursor debug_info ;
2025+ AWS_ZERO_STRUCT (debug_info );
2026+ struct aws_h2_frame * peer_frame = aws_h2_frame_new_goaway (allocator , stream_id , AWS_H2_ERR_NO_ERROR , debug_info );
2027+ ASSERT_SUCCESS (h2_fake_peer_send_frame (& s_tester .peer , peer_frame ));
2028+ testing_channel_drain_queued_tasks (& s_tester .testing_channel );
2029+
2030+ /* validate the connection is still open, and the second request finished with GOAWAY_RECEIVED */
2031+ ASSERT_TRUE (aws_http_connection_is_open (s_tester .connection ));
2032+ ASSERT_FALSE (stream_testers [0 ].complete );
2033+ ASSERT_TRUE (stream_testers [1 ].complete );
2034+ ASSERT_INT_EQUALS (AWS_ERROR_HTTP_GOAWAY_RECEIVED , stream_testers [1 ].on_complete_error_code );
2035+
2036+ /* validate the new requst will no be accepted */
2037+ ASSERT_FAILS (s_stream_tester_init (& stream_testers [2 ], requests [2 ]));
2038+
2039+ /* Try gracefully shutting down the connection */
2040+ struct aws_http_header response_headers_src [] = {DEFINE_HEADER (":status" , "200" )};
2041+ struct aws_http_headers * response_headers = aws_http_headers_new (allocator );
2042+ aws_http_headers_add_array (response_headers , response_headers_src , AWS_ARRAY_SIZE (response_headers_src ));
2043+ struct aws_h2_frame * response_frame = aws_h2_frame_new_headers (
2044+ allocator , aws_http_stream_get_id (stream_testers [0 ].stream ), response_headers , true /* end_stream */ , 0 , NULL );
2045+ ASSERT_SUCCESS (h2_fake_peer_send_frame (& s_tester .peer , response_frame ));
2046+ /* shutdown channel */
2047+ aws_channel_shutdown (s_tester .testing_channel .channel , AWS_ERROR_SUCCESS );
2048+ testing_channel_drain_queued_tasks (& s_tester .testing_channel );
2049+ ASSERT_TRUE (testing_channel_is_shutdown_completed (& s_tester .testing_channel ));
2050+
2051+ /* validate the first request finishes successfully */
2052+ testing_channel_drain_queued_tasks (& s_tester .testing_channel );
2053+ ASSERT_TRUE (stream_testers [0 ].complete );
2054+ ASSERT_INT_EQUALS (200 , stream_testers [0 ].response_status );
2055+
2056+ ASSERT_FALSE (aws_http_connection_is_open (s_tester .connection ));
2057+
2058+ /* clean up */
2059+ aws_http_headers_release (response_headers );
2060+ for (size_t i = 0 ; i < NUM_STREAMS ; ++ i ) {
2061+ client_stream_tester_clean_up (& stream_testers [i ]);
2062+ aws_http_message_release (requests [i ]);
2063+ }
2064+ return s_tester_clean_up ();
2065+ }
2066+
2067+ /* Test client receives the GOAWAY frame, stop creating new stream and complete the streams whose id are higher than the
2068+ * last stream id included in GOAWAY frame */
2069+ TEST_CASE (h2_client_conn_err_invalid_last_stream_id_goaway ) {
2070+ ASSERT_SUCCESS (s_tester_init (allocator , ctx ));
2071+
2072+ /* get connection preface and acks out of the way */
2073+ ASSERT_SUCCESS (h2_fake_peer_send_connection_preface_default_settings (& s_tester .peer ));
2074+ testing_channel_drain_queued_tasks (& s_tester .testing_channel );
2075+ ASSERT_SUCCESS (h2_fake_peer_decode_messages_from_testing_channel (& s_tester .peer ));
2076+
2077+ /* fake peer send multiple GOAWAY frames */
2078+ struct aws_byte_cursor debug_info ;
2079+ AWS_ZERO_STRUCT (debug_info );
2080+ /* First on with last_stream_id as AWS_H2_STREAM_ID_MAX */
2081+ struct aws_h2_frame * peer_frame =
2082+ aws_h2_frame_new_goaway (allocator , AWS_H2_STREAM_ID_MAX , AWS_H2_ERR_NO_ERROR , debug_info );
2083+ ASSERT_SUCCESS (h2_fake_peer_send_frame (& s_tester .peer , peer_frame ));
2084+ /* Second one with last_stream_id as 1 and some error */
2085+ peer_frame = aws_h2_frame_new_goaway (allocator , 1 , AWS_H2_ERR_FLOW_CONTROL_ERROR , debug_info );
2086+ ASSERT_SUCCESS (h2_fake_peer_send_frame (& s_tester .peer , peer_frame ));
2087+ testing_channel_drain_queued_tasks (& s_tester .testing_channel );
2088+
2089+ /* validate the connection is still open, everything is fine */
2090+ ASSERT_TRUE (aws_http_connection_is_open (s_tester .connection ));
2091+
2092+ /* Another GOAWAY with higher last stream id will cause connection closed with an error */
2093+ peer_frame = aws_h2_frame_new_goaway (allocator , 3 , AWS_H2_ERR_FLOW_CONTROL_ERROR , debug_info );
2094+ ASSERT_SUCCESS (h2_fake_peer_send_frame (& s_tester .peer , peer_frame ));
2095+ testing_channel_drain_queued_tasks (& s_tester .testing_channel );
2096+
2097+ ASSERT_FALSE (aws_http_connection_is_open (s_tester .connection ));
2098+ ASSERT_INT_EQUALS (
2099+ AWS_ERROR_HTTP_PROTOCOL_ERROR , testing_channel_get_shutdown_error_code (& s_tester .testing_channel ));
2100+ /* clean up */
2101+ return s_tester_clean_up ();
2102+ }
0 commit comments