Skip to content

Commit 98b3e49

Browse files
authored
Websocket close timeout (#283)
Cancel the CLOSE frame when it is blocked
1 parent 40d1c65 commit 98b3e49

File tree

2 files changed

+40
-0
lines changed

2 files changed

+40
-0
lines changed

include/aws/http/websocket.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ enum aws_websocket_opcode {
3434

3535
#define AWS_WEBSOCKET_MAX_PAYLOAD_LENGTH 0x7FFFFFFFFFFFFFFF
3636
#define AWS_WEBSOCKET_MAX_HANDSHAKE_KEY_LENGTH 25
37+
#define AWS_WEBSOCKET_CLOSE_TIMEOUT 1000000000 // nanos -> 1 sec
3738

3839
/**
3940
* Called when websocket setup is complete.

source/websocket.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ struct aws_websocket {
5555
struct aws_channel_task shutdown_channel_task;
5656
struct aws_channel_task increment_read_window_task;
5757
struct aws_channel_task waiting_on_payload_stream_task;
58+
struct aws_channel_task close_timeout_task;
5859
bool is_server;
5960

6061
/* Data that should only be accessed from the websocket's channel thread. */
@@ -183,6 +184,7 @@ static void s_move_synced_data_to_thread_task(struct aws_channel_task *task, voi
183184
static void s_increment_read_window_task(struct aws_channel_task *task, void *arg, enum aws_task_status status);
184185
static void s_shutdown_channel_task(struct aws_channel_task *task, void *arg, enum aws_task_status status);
185186
static void s_waiting_on_payload_stream_task(struct aws_channel_task *task, void *arg, enum aws_task_status status);
187+
static void s_close_timeout_task(struct aws_channel_task *task, void *arg, enum aws_task_status status);
186188
static void s_schedule_channel_shutdown(struct aws_websocket *websocket, int error_code);
187189
static void s_shutdown_due_to_write_err(struct aws_websocket *websocket, int error_code);
188190
static void s_shutdown_due_to_read_err(struct aws_websocket *websocket, int error_code);
@@ -291,6 +293,7 @@ struct aws_websocket *aws_websocket_handler_new(const struct aws_websocket_handl
291293
s_waiting_on_payload_stream_task,
292294
websocket,
293295
"websocket_waiting_on_payload_stream");
296+
aws_channel_task_init(&websocket->close_timeout_task, s_close_timeout_task, websocket, "websocket_close_timeout");
294297

295298
aws_linked_list_init(&websocket->thread_data.outgoing_frame_list);
296299

@@ -1118,13 +1121,49 @@ static int s_handler_shutdown(
11181121
AWS_LS_HTTP_WEBSOCKET,
11191122
"id=%p: Outgoing CLOSE frame queued, handler will finish shutdown once it's sent.",
11201123
(void *)websocket);
1124+
/* schedule a task to run after 1 sec. If the CLOSE still not sent at that time, we should just cancel
1125+
* sending it and shutdown the channel. */
1126+
uint64_t schedule_time = 0;
1127+
aws_channel_current_clock_time(websocket->channel_slot->channel, &schedule_time);
1128+
schedule_time += AWS_WEBSOCKET_CLOSE_TIMEOUT;
1129+
AWS_LOGF_TRACE(
1130+
AWS_LS_HTTP_WEBSOCKET,
1131+
"id=%p: websocket_close_timeout task will be run at timestamp %" PRIu64,
1132+
(void *)websocket,
1133+
schedule_time);
1134+
aws_channel_schedule_task_future(
1135+
websocket->channel_slot->channel, &websocket->close_timeout_task, schedule_time);
11211136
}
11221137
}
11231138
}
11241139

11251140
return AWS_OP_SUCCESS;
11261141
}
11271142

1143+
static void s_close_timeout_task(struct aws_channel_task *task, void *arg, enum aws_task_status status) {
1144+
(void)task;
1145+
if (status != AWS_TASK_STATUS_RUN_READY) {
1146+
/* If channel has shut down, don't need to resume sending payload */
1147+
return;
1148+
}
1149+
1150+
struct aws_websocket *websocket = arg;
1151+
AWS_ASSERT(aws_channel_thread_is_callers_thread(websocket->channel_slot->channel));
1152+
1153+
if (!websocket->thread_data.is_shutting_down_and_waiting_for_close_frame_to_be_written) {
1154+
/* Not waiting for write to complete, which means the CLOSE frame has sent, just do nothing */
1155+
return;
1156+
}
1157+
1158+
AWS_LOGF_WARN(
1159+
AWS_LS_HTTP_WEBSOCKET,
1160+
"id=%p: Failed to send CLOSE frame, timeout happened, shutdown the channel",
1161+
(void *)websocket);
1162+
1163+
s_stop_writing(websocket, AWS_ERROR_HTTP_CONNECTION_CLOSED);
1164+
s_finish_shutdown(websocket);
1165+
}
1166+
11281167
static void s_finish_shutdown(struct aws_websocket *websocket) {
11291168
AWS_ASSERT(aws_channel_thread_is_callers_thread(websocket->channel_slot->channel));
11301169
AWS_ASSERT(websocket->thread_data.is_writing_stopped);

0 commit comments

Comments
 (0)