@@ -130,7 +130,6 @@ void aws_h1_connection_unlock_synced_data(struct aws_h1_connection *connection)
130
130
* - Channel is shutting down in the read direction.
131
131
* - Channel is shutting down in the write direction.
132
132
* - An error occurs.
133
- * - User wishes to close the connection (this is the only case where the function may run off-thread).
134
133
*/
135
134
static void s_stop (
136
135
struct aws_h1_connection * connection ,
@@ -139,15 +138,14 @@ static void s_stop(
139
138
bool schedule_shutdown ,
140
139
int error_code ) {
141
140
141
+ AWS_ASSERT (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
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
- AWS_ASSERT (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
146
145
connection -> thread_data .is_reading_stopped = true;
147
146
}
148
147
149
148
if (stop_writing ) {
150
- AWS_ASSERT (aws_channel_thread_is_callers_thread (connection -> base .channel_slot -> channel ));
151
149
connection -> thread_data .is_writing_stopped = true;
152
150
}
153
151
{ /* BEGIN CRITICAL SECTION */
@@ -169,6 +167,11 @@ static void s_stop(
169
167
aws_error_name (error_code ));
170
168
171
169
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
+ }
172
175
}
173
176
}
174
177
@@ -189,14 +192,45 @@ static void s_shutdown_due_to_error(struct aws_h1_connection *connection, int er
189
192
s_stop (connection , true /*stop_reading*/ , true /*stop_writing*/ , true /*schedule_shutdown*/ , error_code );
190
193
}
191
194
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
+
192
228
/**
193
229
* Public function for closing connection.
194
230
*/
195
231
static void s_connection_close (struct aws_http_connection * connection_base ) {
196
232
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 );
200
234
}
201
235
202
236
static 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) {
412
446
(void * )stream ,
413
447
error_code ,
414
448
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 );
417
450
}
418
451
419
452
struct aws_http_stream * s_make_request (
@@ -495,10 +528,17 @@ static void s_cross_thread_work_task(struct aws_channel_task *channel_task, void
495
528
bool has_new_client_streams = !aws_linked_list_empty (& connection -> synced_data .new_client_stream_list );
496
529
aws_linked_list_move_all_back (
497
530
& 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 ;
498
535
499
536
aws_h1_connection_unlock_synced_data (connection );
500
537
/* END CRITICAL SECTION */
501
538
539
+ if (shutdown_requested ) {
540
+ s_stop (connection , true /*stop_reading*/ , true /*stop_writing*/ , true /*schedule_shutdown*/ , shutdown_error );
541
+ }
502
542
/* Kick off outgoing-stream task if necessary */
503
543
if (has_new_client_streams ) {
504
544
aws_h1_connection_try_write_outgoing_stream (connection );
@@ -785,13 +825,8 @@ static void s_http_stream_response_first_byte_timeout_task(
785
825
(void * )connection_base ,
786
826
response_first_byte_timeout_ms );
787
827
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 );
795
830
}
796
831
797
832
static void s_set_outgoing_message_done (struct aws_h1_stream * stream ) {
@@ -1804,6 +1839,12 @@ static int s_handler_process_read_message(
1804
1839
1805
1840
AWS_LOGF_TRACE (
1806
1841
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
+ }
1807
1848
1808
1849
/* Shrink connection window by amount of data received. See comments at variable's
1809
1850
* declaration site on why we use this instead of the official `aws_channel_slot.window_size`. */
0 commit comments