@@ -130,7 +130,6 @@ void aws_h1_connection_unlock_synced_data(struct aws_h1_connection *connection)
130130 * - Channel is shutting down in the read direction.
131131 * - Channel is shutting down in the write direction.
132132 * - An error occurs.
133- * - User wishes to close the connection (this is the only case where the function may run off-thread).
134133 */
135134static void s_stop (
136135 struct aws_h1_connection * connection ,
@@ -139,15 +138,14 @@ static void s_stop(
139138 bool schedule_shutdown ,
140139 int error_code ) {
141140
141+ AWS_ASSERT (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
142142 AWS_ASSERT (stop_reading || stop_writing || schedule_shutdown ); /* You are required to stop at least 1 thing */
143143
144144 if (stop_reading ) {
145- AWS_ASSERT (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
146145 connection -> thread_data .is_reading_stopped = true;
147146 }
148147
149148 if (stop_writing ) {
150- AWS_ASSERT (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
151149 connection -> thread_data .is_writing_stopped = true;
152150 }
153151 { /* BEGIN CRITICAL SECTION */
@@ -169,6 +167,11 @@ static void s_stop(
169167 aws_error_name (error_code ));
170168
171169 aws_channel_shutdown (connection -> base .channel_slot -> channel , error_code );
170+ if (stop_reading ) {
171+ /* Increase the window size after shutdown starts, to prevent deadlock when data still pending in the TLS
172+ * handler. */
173+ aws_channel_slot_increment_read_window (connection -> base .channel_slot , SIZE_MAX );
174+ }
172175 }
173176}
174177
@@ -189,14 +192,45 @@ static void s_shutdown_due_to_error(struct aws_h1_connection *connection, int er
189192 s_stop (connection , true /*stop_reading*/ , true /*stop_writing*/ , true /*schedule_shutdown*/ , error_code );
190193}
191194
195+ /**
196+ * Helper to shutdown the connection from non-channel thread. (User wishes to close the connection)
197+ **/
198+ static void s_shutdown_from_off_thread (struct aws_h1_connection * connection , int error_code ) {
199+ bool should_schedule_task = false;
200+ { /* BEGIN CRITICAL SECTION */
201+ aws_h1_connection_lock_synced_data (connection );
202+ if (!connection -> synced_data .is_cross_thread_work_task_scheduled ) {
203+ connection -> synced_data .is_cross_thread_work_task_scheduled = true;
204+ should_schedule_task = true;
205+ }
206+ if (!connection -> synced_data .shutdown_requested ) {
207+ connection -> synced_data .shutdown_requested = true;
208+ connection -> synced_data .shutdown_requested_error_code = error_code ;
209+ }
210+ /* Connection has shutdown, new streams should not be allowed. */
211+ connection -> synced_data .is_open = false;
212+ connection -> synced_data .new_stream_error_code = AWS_ERROR_HTTP_CONNECTION_CLOSED ;
213+ aws_h1_connection_unlock_synced_data (connection );
214+ } /* END CRITICAL SECTION */
215+
216+ if (should_schedule_task ) {
217+ AWS_LOGF_TRACE (
218+ AWS_LS_HTTP_CONNECTION , "id=%p: Scheduling connection cross-thread work task." , (void * )& connection -> base );
219+ aws_channel_schedule_task_now (connection -> base .channel_slot -> channel , & connection -> cross_thread_work_task );
220+ } else {
221+ AWS_LOGF_TRACE (
222+ AWS_LS_HTTP_CONNECTION ,
223+ "id=%p: Connection cross-thread work task was already scheduled" ,
224+ (void * )& connection -> base );
225+ }
226+ }
227+
192228/**
193229 * Public function for closing connection.
194230 */
195231static void s_connection_close (struct aws_http_connection * connection_base ) {
196232 struct aws_h1_connection * connection = AWS_CONTAINER_OF (connection_base , struct aws_h1_connection , base );
197-
198- /* Don't stop reading/writing immediately, let that happen naturally during the channel shutdown process. */
199- s_stop (connection , false /*stop_reading*/ , false /*stop_writing*/ , true /*schedule_shutdown*/ , AWS_ERROR_SUCCESS );
233+ s_shutdown_from_off_thread (connection , AWS_ERROR_SUCCESS );
200234}
201235
202236static void s_connection_stop_new_request (struct aws_http_connection * connection_base ) {
@@ -412,8 +446,7 @@ void aws_h1_stream_cancel(struct aws_http_stream *stream, int error_code) {
412446 (void * )stream ,
413447 error_code ,
414448 aws_error_name (error_code ));
415-
416- s_stop (connection , false /*stop_reading*/ , false /*stop_writing*/ , true /*schedule_shutdown*/ , error_code );
449+ s_shutdown_from_off_thread (connection , error_code );
417450}
418451
419452struct aws_http_stream * s_make_request (
@@ -495,10 +528,17 @@ static void s_cross_thread_work_task(struct aws_channel_task *channel_task, void
495528 bool has_new_client_streams = !aws_linked_list_empty (& connection -> synced_data .new_client_stream_list );
496529 aws_linked_list_move_all_back (
497530 & connection -> thread_data .stream_list , & connection -> synced_data .new_client_stream_list );
531+ bool shutdown_requested = connection -> synced_data .shutdown_requested ;
532+ int shutdown_error = connection -> synced_data .shutdown_requested_error_code ;
533+ connection -> synced_data .shutdown_requested = false;
534+ connection -> synced_data .shutdown_requested_error_code = 0 ;
498535
499536 aws_h1_connection_unlock_synced_data (connection );
500537 /* END CRITICAL SECTION */
501538
539+ if (shutdown_requested ) {
540+ s_stop (connection , true /*stop_reading*/ , true /*stop_writing*/ , true /*schedule_shutdown*/ , shutdown_error );
541+ }
502542 /* Kick off outgoing-stream task if necessary */
503543 if (has_new_client_streams ) {
504544 aws_h1_connection_try_write_outgoing_stream (connection );
@@ -785,13 +825,8 @@ static void s_http_stream_response_first_byte_timeout_task(
785825 (void * )connection_base ,
786826 response_first_byte_timeout_ms );
787827
788- /* Don't stop reading/writing immediately, let that happen naturally during the channel shutdown process. */
789- s_stop (
790- connection ,
791- false /*stop_reading*/ ,
792- false /*stop_writing*/ ,
793- true /*schedule_shutdown*/ ,
794- AWS_ERROR_HTTP_RESPONSE_FIRST_BYTE_TIMEOUT );
828+ /* Shutdown the connection. */
829+ s_shutdown_due_to_error (connection , AWS_ERROR_HTTP_RESPONSE_FIRST_BYTE_TIMEOUT );
795830}
796831
797832static void s_set_outgoing_message_done (struct aws_h1_stream * stream ) {
@@ -1804,6 +1839,12 @@ static int s_handler_process_read_message(
18041839
18051840 AWS_LOGF_TRACE (
18061841 AWS_LS_HTTP_CONNECTION , "id=%p: Incoming message of size %zu." , (void * )& connection -> base , message_size );
1842+ if (connection -> thread_data .is_reading_stopped ) {
1843+ /* Read has stopped, ignore the data, shutdown the channel incase it has not started yet. */
1844+ aws_mem_release (message -> allocator , message ); /* Release the message as we return success. */
1845+ s_shutdown_due_to_error (connection , AWS_ERROR_HTTP_CONNECTION_CLOSED );
1846+ return AWS_OP_SUCCESS ;
1847+ }
18071848
18081849 /* Shrink connection window by amount of data received. See comments at variable's
18091850 * declaration site on why we use this instead of the official `aws_channel_slot.window_size`. */
0 commit comments