Skip to content

Commit 5d1871f

Browse files
authored
aws_http_connection_update_window -> aws_http2_connection_update_window (#327)
- aws_http_connection_update_window -> aws_http2_connection_update_window - Error out when the wrong connection used as user cannot do anything to recover.
1 parent bf598f4 commit 5d1871f

File tree

9 files changed

+208
-126
lines changed

9 files changed

+208
-126
lines changed

include/aws/http/connection.h

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -296,6 +296,9 @@ struct aws_http_client_connection_options {
296296
* If a stream's flow-control window reaches 0, no further data will be received.
297297
* The user must call aws_http_stream_update_window() to increment the stream's
298298
* window and keep data flowing.
299+
*
300+
* If you created a HTTP/2 connection, it will also control the connection window
301+
* management.
299302
*/
300303
bool manual_window_management;
301304

@@ -453,13 +456,6 @@ bool aws_http_connection_new_requests_allowed(const struct aws_http_connection *
453456
AWS_HTTP_API
454457
bool aws_http_connection_is_client(const struct aws_http_connection *connection);
455458

456-
/**
457-
* DEPRECATED
458-
* TODO: Delete once this is removed from H2.
459-
*/
460-
AWS_HTTP_API
461-
void aws_http_connection_update_window(struct aws_http_connection *connection, size_t increment_size);
462-
463459
AWS_HTTP_API
464460
enum aws_http_version aws_http_connection_get_version(const struct aws_http_connection *connection);
465461

@@ -537,7 +533,7 @@ int aws_http2_connection_ping(
537533
* @param out_settings fixed size array of aws_http2_setting gets set to the local settings
538534
*/
539535
AWS_HTTP_API
540-
int aws_http2_connection_get_local_settings(
536+
void aws_http2_connection_get_local_settings(
541537
const struct aws_http_connection *http2_connection,
542538
struct aws_http2_setting out_settings[AWS_HTTP2_SETTINGS_COUNT]);
543539

@@ -548,7 +544,7 @@ int aws_http2_connection_get_local_settings(
548544
* @param out_settings fixed size array of aws_http2_setting gets set to the remote settings
549545
*/
550546
AWS_HTTP_API
551-
int aws_http2_connection_get_remote_settings(
547+
void aws_http2_connection_get_remote_settings(
552548
const struct aws_http_connection *http2_connection,
553549
struct aws_http2_setting out_settings[AWS_HTTP2_SETTINGS_COUNT]);
554550

@@ -562,6 +558,8 @@ int aws_http2_connection_get_remote_settings(
562558
* (http2_error=0, allow_more_streams=true), or to customize the final GOAWAY
563559
* frame that is sent by this connection.
564560
*
561+
* The other end may not receive the goaway, if the connection already closed.
562+
*
565563
* @param http2_connection HTTP/2 connection.
566564
* @param http2_error The HTTP/2 error code (RFC-7540 section 7) to send.
567565
* `enum aws_http2_error_code` lists official codes.
@@ -610,6 +608,32 @@ int aws_http2_connection_get_received_goaway(
610608
uint32_t *out_http2_error,
611609
uint32_t *out_last_stream_id);
612610

611+
/**
612+
* Increment the connection's flow-control window to keep data flowing (HTTP/2 only).
613+
*
614+
* If the connection was created with `manual_window_management` set true,
615+
* the flow-control window of the connection will shrink as body data is received for all the streams created on it.
616+
* (headers, padding, and other metadata do not affect the window).
617+
* The initial connection flow-control window is 65,535.
618+
* Once the connection's flow-control window reaches to 0, all the streams on the connection stop receiving any further
619+
* data.
620+
*
621+
* If `manual_window_management` is false, this call will have no effect.
622+
* The connection maintains its flow-control windows such that
623+
* no back-pressure is applied and data arrives as fast as possible.
624+
*
625+
* If you are not connected, this call will have no effect.
626+
*
627+
* Crashes when the connection is not http2 connection.
628+
* The limit of the Maximum Size is 2**31 - 1. If the increment size cause the connection flow window exceeds the
629+
* Maximum size, this call will result in the connection lost.
630+
*
631+
* @param http2_connection HTTP/2 connection.
632+
* @param increment_size The size to increment for the connection's flow control window
633+
*/
634+
AWS_HTTP_API
635+
void aws_http2_connection_update_window(struct aws_http_connection *http2_connection, uint32_t increment_size);
636+
613637
AWS_EXTERN_C_END
614638

615639
#endif /* AWS_HTTP_CONNECTION_H */

include/aws/http/private/connection_impl.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,9 @@ struct aws_http_connection_vtable {
4242
void (*close)(struct aws_http_connection *connection);
4343
bool (*is_open)(const struct aws_http_connection *connection);
4444
bool (*new_requests_allowed)(const struct aws_http_connection *connection);
45-
void (*update_window)(struct aws_http_connection *connection, size_t increment_size);
4645

4746
/* HTTP/2 specific functions */
47+
void (*update_window)(struct aws_http_connection *connection, uint32_t increment_size);
4848
int (*change_settings)(
4949
struct aws_http_connection *http2_connection,
5050
const struct aws_http2_setting *settings_array,

include/aws/http/private/h2_connection.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ int aws_h2_connection_send_rst_and_close_reserved_stream(
263263
uint32_t h2_error_code);
264264

265265
/**
266-
* Error happens while writing into channel, shutdown the connection.
266+
* Error happens while writing into channel, shutdown the connection. Only called within the eventloop thread
267267
*/
268268
void aws_h2_connection_shutdown_due_to_write_err(struct aws_h2_connection *connection, int error_code);
269269

include/aws/http/private/h2_stream.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,9 +80,9 @@ struct aws_h2_stream {
8080
/* The window_update value for `thread_data.window_size_self` that haven't applied yet */
8181
size_t window_update_size;
8282

83-
/* The aws_http2_error_code user wanted to send to remote peer via rst_stream. */
84-
uint32_t user_reset_error_code;
85-
83+
/* The combined aws_http2_error_code user wanted to send to remote peer via rst_stream and internal aws error
84+
* code we want to inform user about. */
85+
struct aws_h2err reset_error;
8686
bool reset_called;
8787

8888
/* Simplified stream state. */

source/connection.c

Lines changed: 16 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -269,23 +269,6 @@ bool aws_http_connection_is_server(const struct aws_http_connection *connection)
269269
return connection->server_data;
270270
}
271271

272-
void aws_http_connection_update_window(struct aws_http_connection *connection, size_t increment_size) {
273-
AWS_ASSERT(connection);
274-
connection->vtable->update_window(connection, increment_size);
275-
}
276-
277-
static int s_check_http2_connection(const struct aws_http_connection *http2_connection) {
278-
if (http2_connection->http_version == AWS_HTTP_VERSION_2) {
279-
return AWS_OP_SUCCESS;
280-
} else {
281-
AWS_LOGF_WARN(
282-
AWS_LS_HTTP_CONNECTION,
283-
"id=%p: HTTP/2 connection only function invoked on connection with other protocol, ignoring call.",
284-
(void *)http2_connection);
285-
return aws_raise_error(AWS_ERROR_INVALID_STATE);
286-
}
287-
}
288-
289272
int aws_http2_connection_change_settings(
290273
struct aws_http_connection *http2_connection,
291274
const struct aws_http2_setting *settings_array,
@@ -294,9 +277,7 @@ int aws_http2_connection_change_settings(
294277
void *user_data) {
295278
AWS_ASSERT(http2_connection);
296279
AWS_PRECONDITION(http2_connection->vtable);
297-
if (s_check_http2_connection(http2_connection)) {
298-
return AWS_OP_ERR;
299-
}
280+
AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
300281
return http2_connection->vtable->change_settings(
301282
http2_connection, settings_array, num_settings, on_completed, user_data);
302283
}
@@ -308,9 +289,7 @@ int aws_http2_connection_ping(
308289
void *user_data) {
309290
AWS_ASSERT(http2_connection);
310291
AWS_PRECONDITION(http2_connection->vtable);
311-
if (s_check_http2_connection(http2_connection)) {
312-
return AWS_OP_ERR;
313-
}
292+
AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
314293
return http2_connection->vtable->send_ping(http2_connection, optional_opaque_data, on_ack, user_data);
315294
}
316295

@@ -321,9 +300,7 @@ int aws_http2_connection_send_goaway(
321300
const struct aws_byte_cursor *optional_debug_data) {
322301
AWS_ASSERT(http2_connection);
323302
AWS_PRECONDITION(http2_connection->vtable);
324-
if (s_check_http2_connection(http2_connection)) {
325-
return AWS_OP_ERR;
326-
}
303+
AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
327304
return http2_connection->vtable->send_goaway(
328305
http2_connection, http2_error, allow_more_streams, optional_debug_data);
329306
}
@@ -336,9 +313,7 @@ int aws_http2_connection_get_sent_goaway(
336313
AWS_PRECONDITION(out_http2_error);
337314
AWS_PRECONDITION(out_last_stream_id);
338315
AWS_PRECONDITION(http2_connection->vtable);
339-
if (s_check_http2_connection(http2_connection)) {
340-
return AWS_OP_ERR;
341-
}
316+
AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
342317
return http2_connection->vtable->get_sent_goaway(http2_connection, out_http2_error, out_last_stream_id);
343318
}
344319

@@ -350,34 +325,33 @@ int aws_http2_connection_get_received_goaway(
350325
AWS_PRECONDITION(out_http2_error);
351326
AWS_PRECONDITION(out_last_stream_id);
352327
AWS_PRECONDITION(http2_connection->vtable);
353-
if (s_check_http2_connection(http2_connection)) {
354-
return AWS_OP_ERR;
355-
}
328+
AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
356329
return http2_connection->vtable->get_received_goaway(http2_connection, out_http2_error, out_last_stream_id);
357330
}
358331

359-
int aws_http2_connection_get_local_settings(
332+
void aws_http2_connection_get_local_settings(
360333
const struct aws_http_connection *http2_connection,
361334
struct aws_http2_setting out_settings[AWS_HTTP2_SETTINGS_COUNT]) {
362335
AWS_ASSERT(http2_connection);
363336
AWS_PRECONDITION(http2_connection->vtable);
364-
if (s_check_http2_connection(http2_connection)) {
365-
return AWS_OP_ERR;
366-
}
337+
AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
367338
http2_connection->vtable->get_local_settings(http2_connection, out_settings);
368-
return AWS_OP_SUCCESS;
369339
}
370340

371-
int aws_http2_connection_get_remote_settings(
341+
void aws_http2_connection_get_remote_settings(
372342
const struct aws_http_connection *http2_connection,
373343
struct aws_http2_setting out_settings[AWS_HTTP2_SETTINGS_COUNT]) {
374344
AWS_ASSERT(http2_connection);
375345
AWS_PRECONDITION(http2_connection->vtable);
376-
if (s_check_http2_connection(http2_connection)) {
377-
return AWS_OP_ERR;
378-
}
346+
AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
379347
http2_connection->vtable->get_remote_settings(http2_connection, out_settings);
380-
return AWS_OP_SUCCESS;
348+
}
349+
350+
void aws_http2_connection_update_window(struct aws_http_connection *http2_connection, uint32_t increment_size) {
351+
AWS_ASSERT(http2_connection);
352+
AWS_PRECONDITION(http2_connection->vtable);
353+
AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
354+
http2_connection->vtable->update_window(http2_connection, increment_size);
381355
}
382356

383357
struct aws_channel *aws_http_connection_get_channel(struct aws_http_connection *connection) {

source/h2_connection.c

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ static struct aws_http_stream *s_connection_make_request(
5656
static void s_connection_close(struct aws_http_connection *connection_base);
5757
static bool s_connection_is_open(const struct aws_http_connection *connection_base);
5858
static bool s_connection_new_requests_allowed(const struct aws_http_connection *connection_base);
59-
static void s_connection_update_window(struct aws_http_connection *connection_base, size_t increment_size);
59+
static void s_connection_update_window(struct aws_http_connection *connection_base, uint32_t increment_size);
6060
static int s_connection_change_settings(
6161
struct aws_http_connection *connection_base,
6262
const struct aws_http2_setting *settings_array,
@@ -268,6 +268,7 @@ static void s_stop(
268268

269269
void aws_h2_connection_shutdown_due_to_write_err(struct aws_h2_connection *connection, int error_code) {
270270
AWS_PRECONDITION(error_code);
271+
AWS_PRECONDITION(aws_channel_thread_is_callers_thread(connection->base.channel_slot->channel));
271272

272273
if (connection->thread_data.channel_shutdown_waiting_for_goaway_to_be_written) {
273274
/* If shutdown is waiting for writes to complete, but writes are now broken,
@@ -2152,28 +2153,29 @@ static bool s_connection_new_requests_allowed(const struct aws_http_connection *
21522153
return new_stream_error_code == 0;
21532154
}
21542155

2155-
static void s_connection_update_window(struct aws_http_connection *connection_base, size_t increment_size) {
2156+
static void s_connection_update_window(struct aws_http_connection *connection_base, uint32_t increment_size) {
21562157
struct aws_h2_connection *connection = AWS_CONTAINER_OF(connection_base, struct aws_h2_connection, base);
21572158
if (!increment_size) {
2159+
/* Silently do nothing. */
21582160
return;
21592161
}
21602162
if (!connection_base->manual_window_management) {
2161-
/* auto-mode, manual update window is not supported */
2163+
/* auto-mode, manual update window is not supported, silently do nothing with warning log. */
21622164
CONNECTION_LOG(
2163-
WARN, connection, "Manual window management is off, update window operations are not supported.");
2165+
DEBUG, connection, "Manual window management is off, update window operations are not supported.");
21642166
return;
21652167
}
2166-
/* Type cast the increment size here, if overflow happens, we will detect it later, and the frame will be destroyed
2167-
*/
21682168
struct aws_h2_frame *connection_window_update_frame =
2169-
aws_h2_frame_new_window_update(connection->base.alloc, 0, (uint32_t)increment_size);
2169+
aws_h2_frame_new_window_update(connection->base.alloc, 0, increment_size);
21702170
if (!connection_window_update_frame) {
21712171
CONNECTION_LOGF(
21722172
ERROR,
21732173
connection,
21742174
"Failed to create WINDOW_UPDATE frame on connection, error %s",
21752175
aws_error_name(aws_last_error()));
2176-
return;
2176+
/* OOM should result in a crash. And the increment size is too huge is the only other failure case, which will
2177+
* result in overflow. */
2178+
goto overflow;
21772179
}
21782180

21792181
int err = 0;
@@ -2196,32 +2198,34 @@ static void s_connection_update_window(struct aws_http_connection *connection_ba
21962198
}
21972199
s_unlock_synced_data(connection);
21982200
} /* END CRITICAL SECTION */
2201+
if (err) {
2202+
CONNECTION_LOG(
2203+
ERROR,
2204+
connection,
2205+
"The connection's flow-control windows has been incremented beyond 2**31 -1, the max for HTTP/2. The ");
2206+
aws_h2_frame_destroy(connection_window_update_frame);
2207+
goto overflow;
2208+
}
21992209

22002210
if (cross_thread_work_should_schedule) {
22012211
CONNECTION_LOG(TRACE, connection, "Scheduling cross-thread work task");
22022212
aws_channel_schedule_task_now(connection->base.channel_slot->channel, &connection->cross_thread_work_task);
22032213
}
22042214

22052215
if (!connection_open) {
2206-
CONNECTION_LOG(ERROR, connection, "Failed to update connection window, connection is closed or closing.");
2207-
aws_raise_error(AWS_ERROR_INVALID_STATE);
2208-
aws_h2_frame_destroy(connection_window_update_frame);
2209-
return;
2210-
}
2211-
2212-
if (err) {
2213-
/* The increment_size is still not 100% safe, since we cannot control the incoming data frame. So just
2214-
* ruled out the value that is obviously wrong values */
2215-
CONNECTION_LOGF(
2216-
ERROR,
2217-
connection,
2218-
"The increment size is too big for HTTP/2 protocol, max flow-control "
2219-
"window size is 2147483647. We got %zu, which will cause the flow-control window to exceed the maximum",
2220-
increment_size);
2221-
aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
2216+
/* connection already closed, just do nothing */
22222217
aws_h2_frame_destroy(connection_window_update_frame);
22232218
return;
22242219
}
2220+
return;
2221+
overflow:
2222+
/* Shutdown the connection as overflow detected */
2223+
s_stop(
2224+
connection,
2225+
false /*stop_reading*/,
2226+
false /*stop_writing*/,
2227+
true /*schedule_shutdown*/,
2228+
AWS_ERROR_OVERFLOW_DETECTED);
22252229
}
22262230

22272231
static int s_connection_change_settings(
@@ -2371,7 +2375,9 @@ static int s_connection_send_goaway(
23712375
connection_open = connection->synced_data.is_open;
23722376
if (!connection_open) {
23732377
s_unlock_synced_data(connection);
2374-
goto closed;
2378+
CONNECTION_LOG(DEBUG, connection, "Goaway not sent, connection is closed or closing.");
2379+
aws_mem_release(connection->base.alloc, pending_goaway);
2380+
goto done;
23752381
}
23762382
was_cross_thread_work_scheduled = connection->synced_data.is_cross_thread_work_task_scheduled;
23772383
connection->synced_data.is_cross_thread_work_task_scheduled = true;
@@ -2392,12 +2398,8 @@ static int s_connection_send_goaway(
23922398
CONNECTION_LOG(TRACE, connection, "Scheduling cross-thread work task");
23932399
aws_channel_schedule_task_now(connection->base.channel_slot->channel, &connection->cross_thread_work_task);
23942400
}
2401+
done:
23952402
return AWS_OP_SUCCESS;
2396-
2397-
closed:
2398-
CONNECTION_LOG(ERROR, connection, "Failed to send goaway, connection is closed or closing.");
2399-
aws_mem_release(connection->base.alloc, pending_goaway);
2400-
return aws_raise_error(AWS_ERROR_INVALID_STATE);
24012403
}
24022404

24032405
static void s_get_settings_general(

0 commit comments

Comments
 (0)