Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 60 additions & 19 deletions include/aws/http/connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,27 @@ struct aws_http_proxy_options {
};

/**
* HTTP/2 connection options.
* Options specific to HTTP/1.x connections.
* Initialize with AWS_HTTP1_CONNECTION_OPTIONS_INIT to set default values.
*/
struct aws_http1_connection_options {
/**
* Optional
* Capacity in bytes of the HTTP/1 connection's read buffer.
* The buffer grows if the flow-control window of the incoming HTTP-stream
* reaches zero. If the buffer reaches capacity, no further socket data is
* read until the HTTP-stream's window opens again, allowing data to resume flowing.
*
* Ignored if `manual_window_management` is false.
* If zero is specified (the default) then a default capacity is chosen.
* A capacity that is too small may hinder throughput.
* A capacity that is too big may waste memory without helping throughput.
*/
size_t read_buffer_capacity;
};

/**
* Options specific to HTTP/2 connections.
* Initialize with AWS_HTTP2_CONNECTION_OPTIONS_INIT to set default values.
*/
struct aws_http2_connection_options {
Expand Down Expand Up @@ -286,10 +306,24 @@ struct aws_http_client_connection_options {
const struct aws_http_connection_monitoring_options *monitoring_options;

/**
* Optional.
* The initial connection flow-control window size for HTTP/1 connection.
* Ignored by HTTP/2 connection, since the initial connection flow-control window in HTTP/2 is not configurable.
* A default size is set by AWS_HTTP_CLIENT_CONNECTION_OPTIONS_INIT.
* Set to true to manually manage the flow-control window of each stream.
*
* If false, the connection will maintain its flow-control windows such that
* no back-pressure is applied and data arrives as fast as possible.
*
* If true, the flow-control window of each stream will shrink as body data
* is received (headers, padding, and other metadata do not affect the window).
* `initial_window_size` determines the starting size of each stream's window.
* If a stream's flow-control window reaches 0, no further data will be received.
* The user must call aws_http_stream_update_window() to increment the stream's
* window and keep data flowing.
*/
bool manual_window_management;

/**
* The starting size of each HTTP stream's flow-control window.
* Required if `manual_window_management` is true,
* ignored if `manual_window_management` is false.
*/
size_t initial_window_size;

Expand All @@ -315,23 +349,20 @@ struct aws_http_client_connection_options {
aws_http_on_client_connection_shutdown_fn *on_shutdown;

/**
* Set to true to manually manage the read window size.
*
* If this is false, the connection will maintain a constant window size.
*
* If this is true, the caller must manually increment the window size using aws_http_stream_update_window().
* If the window is not incremented, it will shrink by the amount of body data received. If the window size
* reaches 0, no further data will be received.
**/
bool manual_window_management;
* Options specific to HTTP/1.x connections.
* Optional.
* Ignored if connection is not HTTP/1.x.
* If connection is HTTP/1.x and options were not specified, default values are used.
*/
const struct aws_http1_connection_options *http1_options;

/**
* HTTP/2 connection specific options.
* Options specific to HTTP/2 connections.
* Optional.
* If HTTP/2 connection created, we will use this for some configurations in HTTP/2 connection.
* If other protocol connection created, this will be ignored.
* Ignored if connection is not HTTP/2.
* If connection is HTTP/2 and options were not specified, default values are used.
*/
struct aws_http2_connection_options *http2_options;
const struct aws_http2_connection_options *http2_options;
};

/* Predefined settings identifiers (RFC-7540 6.5.2) */
Expand All @@ -352,18 +383,27 @@ struct aws_http2_setting {
uint32_t value;
};

/**
* Initializes aws_http1_connection_options with default values.
*/
#define AWS_HTTP1_CONNECTION_OPTIONS_INIT \
{ .read_buffer_capacity = 0 }

/**
* HTTP/2: Default value for max closed streams we will keep in memory.
*/
#define AWS_HTTP2_DEFAULT_MAX_CLOSED_STREAMS (32)

/**
* HTTP/2: The size of payload for HTTP/2 PING frame.
*/
#define AWS_HTTP2_PING_DATA_SIZE (8)

/**
* HTTP/2: The number of known settings.
*/
#define AWS_HTTP2_SETTINGS_COUNT (6)

/**
* Initializes aws_http2_connection_options with default values.
*/
Expand Down Expand Up @@ -426,7 +466,8 @@ AWS_HTTP_API
bool aws_http_connection_is_client(const struct aws_http_connection *connection);

/**
* Increments the connection-wide read window by the value specified.
* DEPRECATED
* TODO: Delete once this is removed from H2.
*/
AWS_HTTP_API
void aws_http_connection_update_window(struct aws_http_connection *connection, size_t increment_size);
Expand Down
1 change: 1 addition & 0 deletions include/aws/http/private/connection_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ struct aws_http_client_bootstrap {
aws_http_on_client_connection_shutdown_fn *on_shutdown;
aws_http_proxy_request_transform_fn *proxy_request_transform;

struct aws_http1_connection_options http1_options;
struct aws_http2_connection_options http2_options;
struct aws_http_connection *connection;
};
Expand Down
65 changes: 55 additions & 10 deletions include/aws/http/private/h1_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
struct aws_h1_connection {
struct aws_http_connection base;

size_t initial_window_size;
size_t initial_stream_window_size;

/* Task responsible for sending data.
* As long as there is data available to send, the task will be "active" and repeatedly:
Expand Down Expand Up @@ -64,17 +64,33 @@ struct aws_h1_connection {
/* Used to encode requests and responses */
struct aws_h1_encoder encoder;

size_t body_bytes_decoded;

/* All aws_io_messages arriving in the read direction are queued here before processing.
* The downstream window (window of next handler, or window of incoming stream)
* determines how much data can be processed. The `copy_mark` is used to
* track progress on partially processed messages at the front of the queue. */
/**
* All aws_io_messages arriving in the read direction are queued here before processing.
* This allows the connection to receive more data than the the current HTTP-stream might allow,
* and process the data later when HTTP-stream's window opens or the next stream begins.
*
* The `aws_io_message.copy_mark` is used to track progress on partially processed messages.
* `pending_bytes` is the sum of all unprocessed bytes across all queued messages.
* `capacity` is the limit for how many unprocessed bytes we'd like in the queue.
*/
struct {
struct aws_linked_list messages;
/* TODO: more variables will go in this struct in the near-future */
size_t pending_bytes;
size_t capacity;
} read_buffer;

/**
* The connection's current window size.
* We use this variable, instead of the existing `aws_channel_slot.window_size`,
* because that variable is not updated immediately, the channel uses a task to update it.
* Since we use the difference between current and desired window size when deciding
* how much to increment, we need the most up-to-date values possible.
*/
size_t connection_window;

/* Only used by tests. Sum of window_increments issued by this slot. Resets each time it's queried */
size_t recent_window_increments;

struct aws_crt_statistics_http1_channel stats;

uint64_t outgoing_stream_timestamp_ns;
Expand All @@ -94,6 +110,8 @@ struct aws_h1_connection {

/* see `outgoing_stream_task` */
bool is_outgoing_stream_task_active : 1;

bool is_processing_read_messages : 1;
} thread_data;

/* Any thread may touch this data, but the lock must be held */
Expand All @@ -119,6 +137,16 @@ struct aws_h1_connection {
} synced_data;
};

/* Allow tests to check current window stats */
struct aws_h1_window_stats {
size_t connection_window;
size_t recent_window_increments; /* Resets to 0 each time window stats are queried*/
size_t buffer_capacity;
size_t buffer_pending_bytes;
uint64_t stream_window;
bool has_incoming_stream;
};

AWS_EXTERN_C_BEGIN

/* The functions below are exported so they can be accessed from tests. */
Expand All @@ -127,13 +155,19 @@ AWS_HTTP_API
struct aws_http_connection *aws_http_connection_new_http1_1_server(
struct aws_allocator *allocator,
bool manual_window_management,
size_t initial_window_size);
size_t initial_window_size,
const struct aws_http1_connection_options *http1_options);

AWS_HTTP_API
struct aws_http_connection *aws_http_connection_new_http1_1_client(
struct aws_allocator *allocator,
bool manual_window_management,
size_t initial_window_size);
size_t initial_window_size,
const struct aws_http1_connection_options *http1_options);

/* Allow tests to check current window stats */
AWS_HTTP_API
struct aws_h1_window_stats aws_h1_connection_window_stats(struct aws_http_connection *connection_base);

AWS_EXTERN_C_END

Expand All @@ -153,4 +187,15 @@ void aws_h1_connection_unlock_synced_data(struct aws_h1_connection *connection);
*/
void aws_h1_connection_try_write_outgoing_stream(struct aws_h1_connection *connection);

/**
* If any read messages are queued, and the downstream window is non-zero,
* process data and send it downstream. Then calculate the connection's
* desired window size and increment it if necessary.
*
* During normal operations "downstream" means the current incoming stream.
* If the connection has switched protocols "downstream" means the next
* channel handler in the read direction.
*/
void aws_h1_connection_try_process_read_messages(struct aws_h1_connection *connection);

#endif /* AWS_HTTP_H1_CONNECTION_H */
7 changes: 7 additions & 0 deletions include/aws/http/private/h1_stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ struct aws_h1_stream {
* Encoder completes/frees/pops front chunk when it's done sending. */
struct aws_linked_list pending_chunk_list;

/* Size of stream's flow-control window.
* Only body data (not headers, etc) counts against the stream's flow-control window. */
uint64_t stream_window;

/* Whether a "request handler" stream has a response to send.
* Has mirror variable in synced_data */
bool has_outgoing_response : 1;
Expand All @@ -77,6 +81,9 @@ struct aws_h1_stream {

enum aws_h1_stream_api_state api_state;

/* Sum of all aws_http_stream_update_window() calls that haven't yet moved to thread_data.stream_window */
uint64_t pending_window_update;

/* See `cross_thread_work_task` */
bool is_cross_thread_work_task_scheduled : 1;

Expand Down
2 changes: 0 additions & 2 deletions include/aws/http/private/request_response_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,6 @@ struct aws_http_stream {

uint32_t id;

bool manual_window_management;

void *user_data;
aws_http_on_incoming_headers_fn *on_incoming_headers;
aws_http_on_incoming_header_block_done_fn *on_incoming_header_block_done;
Expand Down
14 changes: 10 additions & 4 deletions include/aws/http/request_response.h
Original file line number Diff line number Diff line change
Expand Up @@ -757,11 +757,17 @@ AWS_HTTP_API
int aws_http_stream_send_response(struct aws_http_stream *stream, struct aws_http_message *response);

/**
* Manually issue a window update.
* Note that the stream's default behavior is to issue updates which keep the window at its original size.
* See aws_http_make_request_options.manual_window_management for details on letting the window shrink.
* Increment the stream's flow-control window to keep data flowing.
*
* TODO: HTTP/2 vs HTTP/1, we need two different function
* If the connection was created with `manual_window_management` set true,
* the flow-control window of each stream will shrink as body data is received
* (headers, padding, and other metadata do not affect the window).
* The connection's `initial_window_size` determines the starting size of each stream's window.
* If a stream's flow-control window reaches 0, no further data will be received.
*
* If `manual_window_management` is false, this call will have no effect.
* The connection maintains its flow-control windows such that
* no back-pressure is applied and data arrives as fast as possible.
*/
AWS_HTTP_API
void aws_http_stream_update_window(struct aws_http_stream *stream, size_t increment_size);
Expand Down
Loading