1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
|
#ifndef AWS_HTTP_H2_CONNECTION_H
#define AWS_HTTP_H2_CONNECTION_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/atomics.h>
#include <aws/common/fifo_cache.h>
#include <aws/common/hash_table.h>
#include <aws/common/mutex.h>
#include <aws/http/private/connection_impl.h>
#include <aws/http/private/h2_frames.h>
#include <aws/http/statistics.h>
struct aws_h2_decoder;
struct aws_h2_stream;
struct aws_h2_connection {
struct aws_http_connection base;
aws_http2_on_goaway_received_fn *on_goaway_received;
aws_http2_on_remote_settings_change_fn *on_remote_settings_change;
struct aws_channel_task cross_thread_work_task;
struct aws_channel_task outgoing_frames_task;
bool conn_manual_window_management;
/* Only the event-loop thread may touch this data */
struct {
struct aws_h2_decoder *decoder;
struct aws_h2_frame_encoder encoder;
/* True when reading/writing has stopped, whether due to errors or normal channel shutdown. */
bool is_reading_stopped;
bool is_writing_stopped;
bool is_outgoing_frames_task_active;
/* Settings received from peer, which restricts the message to send */
uint32_t settings_peer[AWS_HTTP2_SETTINGS_END_RANGE];
/* Local settings to send/sent to peer, which affects the decoding */
uint32_t settings_self[AWS_HTTP2_SETTINGS_END_RANGE];
/* List using aws_h2_pending_settings.node
* Contains settings waiting to be ACKed by peer and applied */
struct aws_linked_list pending_settings_queue;
/* List using aws_h2_pending_ping.node
* Pings waiting to be ACKed by peer */
struct aws_linked_list pending_ping_queue;
/* Most recent stream-id that was initiated by peer */
uint32_t latest_peer_initiated_stream_id;
/* Maps stream-id to aws_h2_stream*.
* Contains all streams in the open, reserved, and half-closed states (terms from RFC-7540 5.1).
* Once a stream enters closed state, it is removed from this map. */
struct aws_hash_table active_streams_map;
/* List using aws_h2_stream.node.
* Contains all streams with DATA frames to send.
* Any stream in this list is also in the active_streams_map. */
struct aws_linked_list outgoing_streams_list;
/* List using aws_h2_stream.node.
* Contains all streams with DATA frames to send, and cannot send now due to flow control.
* Waiting for WINDOW_UPDATE to set them free */
struct aws_linked_list stalled_window_streams_list;
/* List using aws_h2_stream.node.
* Contains all streams that are open, but are only sending data when notified, rather than polling
* for it (e.g. event streams)
* Streams are moved to the outgoing_streams_list until they send pending data, then are moved back
* to this list to sleep until more data comes in
*/
struct aws_linked_list waiting_streams_list;
/* List using aws_h2_frame.node.
* Queues all frames (except DATA frames) for connection to send.
* When queue is empty, then we send DATA frames from the outgoing_streams_list */
struct aws_linked_list outgoing_frames_queue;
/* FIFO cache for closed stream, key: stream-id, value: aws_h2_stream_closed_when.
* Contains data about streams that were recently closed.
* The oldest entry will be removed if the cache is full */
struct aws_cache *closed_streams;
/* Flow-control of connection from peer. Indicating the buffer capacity of our peer.
* Reduce the space after sending a flow-controlled frame. Increment after receiving WINDOW_UPDATE for
* connection */
size_t window_size_peer;
/* Flow-control of connection for this side.
* Reduce the space after receiving a flow-controlled frame. Increment after sending WINDOW_UPDATE for
* connection */
size_t window_size_self;
/* Highest self-initiated stream-id that peer might have processed.
* Defaults to max stream-id, may be lowered when GOAWAY frame received. */
uint32_t goaway_received_last_stream_id;
/* Last-stream-id sent in most recent GOAWAY frame. Defaults to max stream-id. */
uint32_t goaway_sent_last_stream_id;
/* Frame we are encoding now. NULL if we are not encoding anything. */
struct aws_h2_frame *current_outgoing_frame;
/* Pointer to initial pending settings. If ACKed by peer, it will be NULL. */
struct aws_h2_pending_settings *init_pending_settings;
/* Cached channel shutdown values.
* If possible, we delay shutdown-in-the-write-dir until GOAWAY is written. */
int channel_shutdown_error_code;
bool channel_shutdown_immediately;
bool channel_shutdown_waiting_for_goaway_to_be_written;
/* TODO: Consider adding stream monitor */
struct aws_crt_statistics_http2_channel stats;
/* Timestamp when connection has data to send, which is when there is an active stream with body to send */
uint64_t outgoing_timestamp_ns;
/* Timestamp when connection has data to receive, which is when there is an active stream */
uint64_t incoming_timestamp_ns;
} thread_data;
/* Any thread may touch this data, but the lock must be held (unless it's an atomic) */
struct {
struct aws_mutex lock;
/* New `aws_h2_stream *` that haven't moved to `thread_data` yet */
struct aws_linked_list pending_stream_list;
/* New `aws_h2_frames *`, connection control frames created by user that haven't moved to `thread_data` yet */
struct aws_linked_list pending_frame_list;
/* New `aws_h2_pending_settings *` created by user that haven't moved to `thread_data` yet */
struct aws_linked_list pending_settings_list;
/* New `aws_h2_pending_ping *` created by user that haven't moved to `thread_data` yet */
struct aws_linked_list pending_ping_list;
/* New `aws_h2_pending_goaway *` created by user that haven't sent yet */
struct aws_linked_list pending_goaway_list;
bool is_cross_thread_work_task_scheduled;
/* The window_update value for `thread_data.window_size_self` that haven't applied yet */
size_t window_update_size;
/* For checking status from outside the event-loop thread. */
bool is_open;
/* If non-zero, reason to immediately reject new streams. (ex: closing) */
int new_stream_error_code;
/* Last-stream-id sent in most recent GOAWAY frame. Defaults to AWS_H2_STREAM_ID_MAX + 1 indicates no GOAWAY has
* been sent so far.*/
uint32_t goaway_sent_last_stream_id;
/* aws_http2_error_code sent in most recent GOAWAY frame. Defaults to 0, check goaway_sent_last_stream_id for
* any GOAWAY has sent or not */
uint32_t goaway_sent_http2_error_code;
/* Last-stream-id received in most recent GOAWAY frame. Defaults to AWS_H2_STREAM_ID_MAX + 1 indicates no GOAWAY
* has been received so far.*/
uint32_t goaway_received_last_stream_id;
/* aws_http2_error_code received in most recent GOAWAY frame. Defaults to 0, check
* goaway_received_last_stream_id for any GOAWAY has received or not */
uint32_t goaway_received_http2_error_code;
/* For checking settings received from peer from outside the event-loop thread. */
uint32_t settings_peer[AWS_HTTP2_SETTINGS_END_RANGE];
/* For checking local settings to send/sent to peer from outside the event-loop thread. */
uint32_t settings_self[AWS_HTTP2_SETTINGS_END_RANGE];
} synced_data;
};
struct aws_h2_pending_settings {
struct aws_http2_setting *settings_array;
size_t num_settings;
struct aws_linked_list_node node;
/* user callback */
void *user_data;
aws_http2_on_change_settings_complete_fn *on_completed;
};
struct aws_h2_pending_ping {
uint8_t opaque_data[AWS_HTTP2_PING_DATA_SIZE];
/* For calculating round-trip time */
uint64_t started_time;
struct aws_linked_list_node node;
/* user callback */
void *user_data;
aws_http2_on_ping_complete_fn *on_completed;
};
struct aws_h2_pending_goaway {
bool allow_more_streams;
uint32_t http2_error;
struct aws_byte_cursor debug_data;
struct aws_linked_list_node node;
};
/**
* The action which caused the stream to close.
*/
enum aws_h2_stream_closed_when {
AWS_H2_STREAM_CLOSED_UNKNOWN,
AWS_H2_STREAM_CLOSED_WHEN_BOTH_SIDES_END_STREAM,
AWS_H2_STREAM_CLOSED_WHEN_RST_STREAM_RECEIVED,
AWS_H2_STREAM_CLOSED_WHEN_RST_STREAM_SENT,
};
enum aws_h2_data_encode_status {
AWS_H2_DATA_ENCODE_COMPLETE,
AWS_H2_DATA_ENCODE_ONGOING,
AWS_H2_DATA_ENCODE_ONGOING_BODY_STREAM_STALLED, /* stalled reading from body stream */
AWS_H2_DATA_ENCODE_ONGOING_WAITING_FOR_WRITES, /* waiting for next manual write */
AWS_H2_DATA_ENCODE_ONGOING_WINDOW_STALLED, /* stalled due to reduced window size */
};
/* When window size is too small to fit the possible padding into it, we stop sending data and wait for WINDOW_UPDATE */
#define AWS_H2_MIN_WINDOW_SIZE (256)
/* Private functions called from tests... */
AWS_EXTERN_C_BEGIN
AWS_HTTP_API
struct aws_http_connection *aws_http_connection_new_http2_server(
struct aws_allocator *allocator,
bool manual_window_management,
const struct aws_http2_connection_options *http2_options);
AWS_HTTP_API
struct aws_http_connection *aws_http_connection_new_http2_client(
struct aws_allocator *allocator,
bool manual_window_management,
const struct aws_http2_connection_options *http2_options);
AWS_EXTERN_C_END
/* Private functions called from multiple .c files... */
/**
* Enqueue outgoing frame.
* Connection takes ownership of frame.
* Frames are sent into FIFO order.
* Do not enqueue DATA frames, these are sent by other means when the frame queue is empty.
*/
void aws_h2_connection_enqueue_outgoing_frame(struct aws_h2_connection *connection, struct aws_h2_frame *frame);
/**
* Invoked immediately after a stream enters the CLOSED state.
* The connection will remove the stream from its "active" datastructures,
* guaranteeing that no further decoder callbacks are invoked on the stream.
*
* This should NOT be invoked in the case of a "Connection Error",
* though a "Stream Error", in which a RST_STREAM is sent and the stream
* is closed early, would invoke this.
*/
int aws_h2_connection_on_stream_closed(
struct aws_h2_connection *connection,
struct aws_h2_stream *stream,
enum aws_h2_stream_closed_when closed_when,
int aws_error_code);
/**
* Send RST_STREAM and close a stream reserved via PUSH_PROMISE.
*/
int aws_h2_connection_send_rst_and_close_reserved_stream(
struct aws_h2_connection *connection,
uint32_t stream_id,
uint32_t h2_error_code);
/**
* Error happens while writing into channel, shutdown the connection. Only called within the eventloop thread
*/
void aws_h2_connection_shutdown_due_to_write_err(struct aws_h2_connection *connection, int error_code);
/**
* Try to write outgoing frames, if the outgoing-frames-task isn't scheduled, run it immediately.
*/
void aws_h2_try_write_outgoing_frames(struct aws_h2_connection *connection);
#endif /* AWS_HTTP_H2_CONNECTION_H */
|