Skip to content

Commit 7a6fc36

Browse files
authored
Control rate of HTTP/1 writes (#180)
* HTTP/1 only writes 1 message at a time. This avoids bloating memory with unsent messages. This also helps improve the accuracy of our upload stats. Still needs tweaking to deal with websocket upgrade edge-cases. * Install websocket handler the moment 101 response comes in, do not wait for stream to complete. Remove hacks where 101 response completed a stream, no 1xx response should complete a stream. * Simplify confusing list logic. Remove the server-only `waiting_stream_list`. Do a simple scan over available streams to find the next one to work on. This is O(N) when pipelining, but I hear that no one does HTTP/1 pipelining, so in that case O(N=1). We can always re-optimize if we have data that pipelining is super popular and this is a hotspot.
1 parent b2916e9 commit 7a6fc36

File tree

9 files changed

+313
-366
lines changed

9 files changed

+313
-366
lines changed

source/h1_connection.c

Lines changed: 155 additions & 184 deletions
Large diffs are not rendered by default.

source/h1_decoder.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -636,8 +636,7 @@ static int s_linestate_request(struct aws_h1_decoder *decoder, struct aws_byte_c
636636
}
637637

638638
static bool s_check_info_response_status_code(int code_val) {
639-
/* TODO: 101 is an info_response, we need to revise the 101 behaviour. */
640-
return code_val >= 100 && code_val < 200 && code_val != 101;
639+
return code_val >= 100 && code_val < 200;
641640
}
642641

643642
static int s_linestate_response(struct aws_h1_decoder *decoder, struct aws_byte_cursor input) {

source/websocket_bootstrap.c

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,11 @@ static int s_ws_bootstrap_on_handshake_response_headers(
9999
const struct aws_http_header *header_array,
100100
size_t num_headers,
101101
void *user_data);
102-
static void s_ws_bootstrap_on_handshake_complete(struct aws_http_stream *stream, int error_code, void *user_data);
102+
static int s_ws_bootstrap_on_handshake_response_header_block_done(
103+
struct aws_http_stream *stream,
104+
enum aws_http_header_block header_block,
105+
void *user_data);
106+
static void s_ws_bootstrap_on_stream_complete(struct aws_http_stream *stream, int error_code, void *user_data);
103107

104108
int aws_websocket_client_connect(const struct aws_websocket_client_connection_options *options) {
105109
aws_http_fatal_assert_library_initialized();
@@ -307,7 +311,8 @@ static void s_ws_bootstrap_on_http_setup(struct aws_http_connection *http_connec
307311
.request = ws_bootstrap->handshake_request,
308312
.user_data = ws_bootstrap,
309313
.on_response_headers = s_ws_bootstrap_on_handshake_response_headers,
310-
.on_complete = s_ws_bootstrap_on_handshake_complete,
314+
.on_response_header_block_done = s_ws_bootstrap_on_handshake_response_header_block_done,
315+
.on_complete = s_ws_bootstrap_on_stream_complete,
311316
};
312317

313318
struct aws_http_stream *handshake_stream =
@@ -393,6 +398,7 @@ static void s_ws_bootstrap_on_http_shutdown(
393398
s_ws_bootstrap_destroy(ws_bootstrap);
394399
}
395400

401+
/* Invoked repeatedly as handshake response headers arrive */
396402
static int s_ws_bootstrap_on_handshake_response_headers(
397403
struct aws_http_stream *stream,
398404
enum aws_http_header_block header_block,
@@ -444,15 +450,25 @@ static int s_ws_bootstrap_on_handshake_response_headers(
444450
return AWS_OP_ERR;
445451
}
446452

447-
static void s_ws_bootstrap_on_handshake_complete(struct aws_http_stream *stream, int error_code, void *user_data) {
453+
/**
454+
* Invoked each time we reach the end of a block of response headers.
455+
* If we got a valid 101 Switching Protocols response, we insert the websocket handler.
456+
* Note:
457+
* In HTTP, 1xx responses are "interim" responses. So a 101 Switching Protocols
458+
* response does not "complete" the stream. Once the connection has switched
459+
* protocols, the stream does not end until the whole connection is closed.
460+
*/
461+
static int s_ws_bootstrap_on_handshake_response_header_block_done(
462+
struct aws_http_stream *stream,
463+
enum aws_http_header_block header_block,
464+
void *user_data) {
465+
466+
(void)header_block;
467+
448468
struct aws_websocket_client_bootstrap *ws_bootstrap = user_data;
449469
struct aws_http_connection *http_connection = s_system_vtable->aws_http_stream_get_connection(stream);
450470
AWS_ASSERT(http_connection);
451471

452-
if (error_code) {
453-
goto error;
454-
}
455-
456472
/* Get data from stream */
457473
s_system_vtable->aws_http_stream_get_incoming_response_status(stream, &ws_bootstrap->response_status);
458474

@@ -522,13 +538,20 @@ static void s_ws_bootstrap_on_handshake_complete(struct aws_http_stream *stream,
522538
/* Clear setup callback so that we know that it's been invoked. */
523539
ws_bootstrap->websocket_setup_callback = NULL;
524540

525-
s_system_vtable->aws_http_stream_release(stream);
526-
return;
541+
return AWS_OP_SUCCESS;
527542

528543
error:
529-
if (!error_code) {
530-
error_code = aws_last_error();
531-
}
532-
s_ws_bootstrap_cancel_setup_due_to_err(ws_bootstrap, http_connection, error_code);
544+
s_ws_bootstrap_cancel_setup_due_to_err(ws_bootstrap, http_connection, aws_last_error());
545+
/* Returning error stops HTTP from processing any further data */
546+
return AWS_OP_ERR;
547+
}
548+
549+
static void s_ws_bootstrap_on_stream_complete(struct aws_http_stream *stream, int error_code, void *user_data) {
550+
/* Not checking error_code because a stream error ends the HTTP connection.
551+
* We'll deal with finishing setup and/or shutdown from the http-shutdown callback */
552+
(void)error_code;
553+
(void)user_data;
554+
555+
/* Done with stream, let it be cleaned up */
533556
s_system_vtable->aws_http_stream_release(stream);
534557
}

tests/CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ add_test_case(h1_client_request_send_large_head)
5050
add_test_case(h1_client_request_content_length_0_ok)
5151
add_test_case(h1_client_request_content_length_too_small_is_error)
5252
add_test_case(h1_client_request_content_length_too_large_is_error)
53-
add_test_case(h1_client_request_send_multiple_in_1_io_message)
53+
add_test_case(h1_client_request_send_multiple)
5454
add_test_case(h1_client_request_close_header_ends_connection)
5555
add_test_case(h1_client_request_close_header_with_pipelining)
5656
add_test_case(h1_client_response_get_1liner)
@@ -162,7 +162,8 @@ add_test_case(websocket_boot_golden_path)
162162
add_test_case(websocket_boot_fail_at_http_connect)
163163
add_test_case(websocket_boot_fail_at_http_connect_error)
164164
add_test_case(websocket_boot_fail_at_new_request)
165-
add_test_case(websocket_boot_fail_at_response_error)
165+
add_test_case(websocket_boot_fail_before_response_headers)
166+
add_test_case(websocket_boot_fail_before_response_headers_done)
166167
add_test_case(websocket_boot_fail_at_response_status)
167168
add_test_case(websocket_boot_fail_at_new_handler)
168169
add_test_case(websocket_boot_report_unexpected_http_shutdown)

tests/proxy_test_helper.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -315,7 +315,7 @@ int proxy_tester_send_connect_response(struct proxy_tester *tester) {
315315
}
316316

317317
/* send response */
318-
ASSERT_SUCCESS(testing_channel_send_response_str(tester->testing_channel, response_string));
318+
ASSERT_SUCCESS(testing_channel_push_read_str(tester->testing_channel, response_string));
319319

320320
testing_channel_drain_queued_tasks(tester->testing_channel);
321321

tests/test_connection_monitor.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1109,7 +1109,7 @@ static void s_add_outgoing_stream(struct test_http_stats_event *event) {
11091109
}
11101110

11111111
static void s_add_response_data(struct test_http_stats_event *event) {
1112-
testing_channel_send_response_str(&s_test_context.test_channel, event->response_stream_data);
1112+
testing_channel_push_read_str(&s_test_context.test_channel, event->response_stream_data);
11131113
}
11141114

11151115
static int s_do_http_statistics_test(

0 commit comments

Comments
 (0)