@@ -142,7 +142,17 @@ static void s_stop(
142
142
AWS_ASSERT (stop_reading || stop_writing || schedule_shutdown ); /* You are required to stop at least 1 thing */
143
143
144
144
if (stop_reading ) {
145
- connection -> thread_data .is_reading_stopped = true;
145
+ if (connection -> thread_data .read_state == AWS_CONNECTION_READ_OPEN ) {
146
+ connection -> thread_data .read_state = AWS_CONNECTION_READ_SHUT_DOWN_COMPLETE ;
147
+ } else if (connection -> thread_data .read_state == AWS_CONNECTION_READ_SHUTTING_DOWN ) {
148
+ /* Shutdown after pending */
149
+ if (connection -> thread_data .pending_shutdown_error_code != 0 ) {
150
+ error_code = connection -> thread_data .pending_shutdown_error_code ;
151
+ }
152
+ connection -> thread_data .read_state = AWS_CONNECTION_READ_SHUT_DOWN_COMPLETE ;
153
+ aws_channel_slot_on_handler_shutdown_complete (
154
+ connection -> base .channel_slot , AWS_CHANNEL_DIR_READ , error_code , false);
155
+ }
146
156
}
147
157
148
158
if (stop_writing ) {
@@ -167,6 +177,7 @@ static void s_stop(
167
177
aws_error_name (error_code ));
168
178
169
179
aws_channel_shutdown (connection -> base .channel_slot -> channel , error_code );
180
+
170
181
if (stop_reading ) {
171
182
/* Increase the window size after shutdown starts, to prevent deadlock when data still pending in the TLS
172
183
* handler. */
@@ -324,7 +335,7 @@ static size_t s_calculate_stream_mode_desired_connection_window(struct aws_h1_co
324
335
static int s_update_connection_window (struct aws_h1_connection * connection ) {
325
336
AWS_ASSERT (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
326
337
327
- if (connection -> thread_data .is_reading_stopped ) {
338
+ if (connection -> thread_data .read_state == AWS_CONNECTION_READ_SHUT_DOWN_COMPLETE ) {
328
339
return AWS_OP_SUCCESS ;
329
340
}
330
341
@@ -778,7 +789,7 @@ static void s_set_incoming_stream_ptr(
778
789
static void s_client_update_incoming_stream_ptr (struct aws_h1_connection * connection ) {
779
790
struct aws_linked_list * list = & connection -> thread_data .stream_list ;
780
791
struct aws_h1_stream * desired ;
781
- if (connection -> thread_data .is_reading_stopped ) {
792
+ if (connection -> thread_data .read_state == AWS_CONNECTION_READ_SHUT_DOWN_COMPLETE ) {
782
793
desired = NULL ;
783
794
} else if (aws_linked_list_empty (list )) {
784
795
desired = NULL ;
@@ -1663,7 +1674,7 @@ static void s_handler_installed(struct aws_channel_handler *handler, struct aws_
1663
1674
static int s_try_process_next_midchannel_read_message (struct aws_h1_connection * connection , bool * out_stop_processing ) {
1664
1675
AWS_ASSERT (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
1665
1676
AWS_ASSERT (connection -> thread_data .has_switched_protocols );
1666
- AWS_ASSERT (! connection -> thread_data .is_reading_stopped );
1677
+ AWS_ASSERT (connection -> thread_data .read_state != AWS_CONNECTION_READ_SHUT_DOWN_COMPLETE );
1667
1678
AWS_ASSERT (!aws_linked_list_empty (& connection -> thread_data .read_buffer .messages ));
1668
1679
1669
1680
* out_stop_processing = false;
@@ -1839,7 +1850,7 @@ static int s_handler_process_read_message(
1839
1850
1840
1851
AWS_LOGF_TRACE (
1841
1852
AWS_LS_HTTP_CONNECTION , "id=%p: Incoming message of size %zu." , (void * )& connection -> base , message_size );
1842
- if (connection -> thread_data .is_reading_stopped ) {
1853
+ if (connection -> thread_data .read_state == AWS_CONNECTION_READ_SHUT_DOWN_COMPLETE ) {
1843
1854
/* Read has stopped, ignore the data, shutdown the channel incase it has not started yet. */
1844
1855
aws_mem_release (message -> allocator , message ); /* Release the message as we return success. */
1845
1856
s_shutdown_due_to_error (connection , AWS_ERROR_HTTP_CONNECTION_CLOSED );
@@ -1868,7 +1879,7 @@ static int s_handler_process_read_message(
1868
1879
}
1869
1880
1870
1881
void aws_h1_connection_try_process_read_messages (struct aws_h1_connection * connection ) {
1871
-
1882
+ int error_code = 0 ;
1872
1883
/* Protect against this function being called recursively. */
1873
1884
if (connection -> thread_data .is_processing_read_messages ) {
1874
1885
return ;
@@ -1877,7 +1888,7 @@ void aws_h1_connection_try_process_read_messages(struct aws_h1_connection *conne
1877
1888
1878
1889
/* Process queued messages */
1879
1890
while (!aws_linked_list_empty (& connection -> thread_data .read_buffer .messages )) {
1880
- if (connection -> thread_data .is_reading_stopped ) {
1891
+ if (connection -> thread_data .read_state == AWS_CONNECTION_READ_SHUT_DOWN_COMPLETE ) {
1881
1892
AWS_LOGF_ERROR (
1882
1893
AWS_LS_HTTP_CONNECTION ,
1883
1894
"id=%p: Cannot process message because connection is shutting down." ,
@@ -1908,6 +1919,13 @@ void aws_h1_connection_try_process_read_messages(struct aws_h1_connection *conne
1908
1919
}
1909
1920
}
1910
1921
1922
+ if (connection -> thread_data .read_state == AWS_CONNECTION_READ_SHUTTING_DOWN &&
1923
+ connection -> thread_data .read_buffer .pending_bytes == 0 ) {
1924
+ /* Done processing the pending buffer. */
1925
+ aws_raise_error (connection -> thread_data .pending_shutdown_error_code );
1926
+ goto shutdown ;
1927
+ }
1928
+
1911
1929
/* Increment connection window, if necessary */
1912
1930
if (s_update_connection_window (connection )) {
1913
1931
goto shutdown ;
@@ -1917,15 +1935,25 @@ void aws_h1_connection_try_process_read_messages(struct aws_h1_connection *conne
1917
1935
return ;
1918
1936
1919
1937
shutdown :
1920
- s_shutdown_due_to_error (connection , aws_last_error ());
1938
+ error_code = aws_last_error ();
1939
+ if (connection -> thread_data .read_state == AWS_CONNECTION_READ_SHUTTING_DOWN &&
1940
+ connection -> thread_data .pending_shutdown_error_code != 0 ) {
1941
+ error_code = connection -> thread_data .pending_shutdown_error_code ;
1942
+ }
1943
+ if (error_code == 0 ) {
1944
+ /* Graceful shutdown, don't stop writing yet. */
1945
+ s_stop (connection , true /*stop_reading*/ , false /*stop_writing*/ , true /*schedule_shutdown*/ , error_code );
1946
+ } else {
1947
+ s_shutdown_due_to_error (connection , aws_last_error ());
1948
+ }
1921
1949
}
1922
1950
1923
1951
/* Try to process the next queued aws_io_message as normal HTTP data for an aws_http_stream.
1924
1952
* This MUST NOT be called if the connection has switched protocols and become a midchannel handler. */
1925
1953
static int s_try_process_next_stream_read_message (struct aws_h1_connection * connection , bool * out_stop_processing ) {
1926
1954
AWS_ASSERT (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
1927
1955
AWS_ASSERT (!connection -> thread_data .has_switched_protocols );
1928
- AWS_ASSERT (! connection -> thread_data .is_reading_stopped );
1956
+ AWS_ASSERT (connection -> thread_data .read_state != AWS_CONNECTION_READ_SHUT_DOWN_COMPLETE );
1929
1957
AWS_ASSERT (!aws_linked_list_empty (& connection -> thread_data .read_buffer .messages ));
1930
1958
1931
1959
* out_stop_processing = false;
@@ -2122,6 +2150,31 @@ static int s_handler_increment_read_window(
2122
2150
return AWS_OP_SUCCESS ;
2123
2151
}
2124
2152
2153
+ static void s_initialize_read_delay_shutdown (struct aws_h1_connection * connection , int error_code ) {
2154
+
2155
+ AWS_LOGF_DEBUG (
2156
+ AWS_LS_HTTP_CONNECTION ,
2157
+ "id=%p: Connection still have pending data to be delivered during shutdown. Wait until downstream "
2158
+ "reads the data." ,
2159
+ (void * )& connection -> base );
2160
+
2161
+ AWS_LOGF_TRACE (
2162
+ AWS_LS_HTTP_CONNECTION ,
2163
+ "id=%p: Current window stats: connection=%zu, stream=%" PRIu64 " buffer=%zu/%zu" ,
2164
+ (void * )& connection -> base ,
2165
+ connection -> thread_data .connection_window ,
2166
+ connection -> thread_data .incoming_stream ? connection -> thread_data .incoming_stream -> thread_data .stream_window
2167
+ : 0 ,
2168
+ connection -> thread_data .read_buffer .pending_bytes ,
2169
+ connection -> thread_data .read_buffer .capacity );
2170
+
2171
+ /* Still have data buffered in connection, wait for it to be processed */
2172
+ connection -> thread_data .read_state = AWS_CONNECTION_READ_SHUTTING_DOWN ;
2173
+ connection -> thread_data .pending_shutdown_error_code = error_code ;
2174
+ /* Try to process messages in queue */
2175
+ aws_h1_connection_try_process_read_messages (connection );
2176
+ }
2177
+
2125
2178
static int s_handler_shutdown (
2126
2179
struct aws_channel_handler * handler ,
2127
2180
struct aws_channel_slot * slot ,
@@ -2142,6 +2195,12 @@ static int s_handler_shutdown(
2142
2195
2143
2196
if (dir == AWS_CHANNEL_DIR_READ ) {
2144
2197
/* This call ensures that no further streams will be created or worked on. */
2198
+ if (!free_scarce_resources_immediately && connection -> thread_data .read_state == AWS_CONNECTION_READ_OPEN &&
2199
+ connection -> thread_data .read_buffer .pending_bytes > 0 ) {
2200
+ s_initialize_read_delay_shutdown (connection , error_code );
2201
+ /* Return success, and wait for the buffered data to be processed to propagate the shutdown. */
2202
+ return AWS_OP_SUCCESS ;
2203
+ }
2145
2204
s_stop (connection , true /*stop_reading*/ , false /*stop_writing*/ , false /*schedule_shutdown*/ , error_code );
2146
2205
} else /* dir == AWS_CHANNEL_DIR_WRITE */ {
2147
2206
0 commit comments