1 #ifndef AWS_HTTP_H1_CONNECTION_H 2 #define AWS_HTTP_H1_CONNECTION_H 3 4 /** 5 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 6 * SPDX-License-Identifier: Apache-2.0. 7 */ 8 9 #include <aws/common/mutex.h> 10 #include <aws/http/private/connection_impl.h> 11 #include <aws/http/private/h1_encoder.h> 12 #include <aws/http/statistics.h> 13 14 #ifdef _MSC_VER 15 # pragma warning(disable : 4214) /* nonstandard extension used: bit field types other than int */ 16 #endif 17 18 struct aws_h1_connection { 19 struct aws_http_connection base; 20 21 size_t initial_stream_window_size; 22 23 /* Task responsible for sending data. 24 * As long as there is data available to send, the task will be "active" and repeatedly: 25 * 1) Encode outgoing stream data to an aws_io_message and send it up the channel. 26 * 2) Wait until the aws_io_message's write_complete callback fires. 27 * 3) Reschedule the task to run again. 28 * 29 * `thread_data.is_outgoing_stream_task_active` tells whether the task is "active". 30 * 31 * If there is no data available to write (waiting for user to add more streams or chunks), 32 * then the task stops being active. The task is made active again when the user 33 * adds more outgoing data. */ 34 struct aws_channel_task outgoing_stream_task; 35 36 /* Task that removes items from `synced_data` and does their on-thread work. 37 * Runs once and wait until it's scheduled again. 38 * Any function that wants to schedule this task MUST: 39 * - acquire the synced_data.lock 40 * - check whether `synced_data.is_cross_thread_work_scheduled` was true or false. 41 * - set `synced_data.is_cross_thread_work_scheduled = true` 42 * - release synced_data.lock 43 * - ONLY IF `synced_data.is_cross_thread_work_scheduled` CHANGED from false to true: 44 * - then schedule the task 45 */ 46 struct aws_channel_task cross_thread_work_task; 47 48 /* Only the event-loop thread may touch this data */ 49 struct { 50 /* List of streams being worked on. */ 51 struct aws_linked_list stream_list; 52 53 /* Points to the stream whose data is currently being sent. 54 * This stream is ALWAYS in the `stream_list`. 55 * HTTP pipelining is supported, so once the stream is completely written 56 * we'll start working on the next stream in the list */ 57 struct aws_h1_stream *outgoing_stream; 58 59 /* Points to the stream being decoded. 60 * This stream is ALWAYS in the `stream_list`. */ 61 struct aws_h1_stream *incoming_stream; 62 struct aws_h1_decoder *incoming_stream_decoder; 63 64 /* Used to encode requests and responses */ 65 struct aws_h1_encoder encoder; 66 67 /** 68 * All aws_io_messages arriving in the read direction are queued here before processing. 69 * This allows the connection to receive more data than the the current HTTP-stream might allow, 70 * and process the data later when HTTP-stream's window opens or the next stream begins. 71 * 72 * The `aws_io_message.copy_mark` is used to track progress on partially processed messages. 73 * `pending_bytes` is the sum of all unprocessed bytes across all queued messages. 74 * `capacity` is the limit for how many unprocessed bytes we'd like in the queue. 75 */ 76 struct { 77 struct aws_linked_list messages; 78 size_t pending_bytes; 79 size_t capacity; 80 } read_buffer; 81 82 /** 83 * The connection's current window size. 84 * We use this variable, instead of the existing `aws_channel_slot.window_size`, 85 * because that variable is not updated immediately, the channel uses a task to update it. 86 * Since we use the difference between current and desired window size when deciding 87 * how much to increment, we need the most up-to-date values possible. 88 */ 89 size_t connection_window; 90 91 /* Only used by tests. Sum of window_increments issued by this slot. Resets each time it's queried */ 92 size_t recent_window_increments; 93 94 struct aws_crt_statistics_http1_channel stats; 95 96 uint64_t outgoing_stream_timestamp_ns; 97 uint64_t incoming_stream_timestamp_ns; 98 99 /* True when read and/or writing has stopped, whether due to errors or normal channel shutdown. */ 100 bool is_reading_stopped : 1; 101 bool is_writing_stopped : 1; 102 103 /* If true, the connection has upgraded to another protocol. 104 * It will pass data to adjacent channel handlers without altering it. 105 * The connection can no longer service request/response streams. */ 106 bool has_switched_protocols : 1; 107 108 /* Server-only. Request-handler streams can only be created while this is true. */ 109 bool can_create_request_handler_stream : 1; 110 111 /* see `outgoing_stream_task` */ 112 bool is_outgoing_stream_task_active : 1; 113 114 bool is_processing_read_messages : 1; 115 } thread_data; 116 117 /* Any thread may touch this data, but the lock must be held */ 118 struct { 119 struct aws_mutex lock; 120 121 /* New client streams that have not been moved to `stream_list` yet. 122 * This list is not used on servers. */ 123 struct aws_linked_list new_client_stream_list; 124 125 /* If non-zero, then window_update_task is scheduled */ 126 size_t window_update_size; 127 128 /* If non-zero, reason to immediately reject new streams. (ex: closing) */ 129 int new_stream_error_code; 130 131 /* See `cross_thread_work_task` */ 132 bool is_cross_thread_work_task_scheduled : 1; 133 134 /* For checking status from outside the event-loop thread. */ 135 bool is_open : 1; 136 137 } synced_data; 138 }; 139 140 /* Allow tests to check current window stats */ 141 struct aws_h1_window_stats { 142 size_t connection_window; 143 size_t recent_window_increments; /* Resets to 0 each time window stats are queried*/ 144 size_t buffer_capacity; 145 size_t buffer_pending_bytes; 146 uint64_t stream_window; 147 bool has_incoming_stream; 148 }; 149 150 AWS_EXTERN_C_BEGIN 151 152 /* The functions below are exported so they can be accessed from tests. */ 153 154 AWS_HTTP_API 155 struct aws_http_connection *aws_http_connection_new_http1_1_server( 156 struct aws_allocator *allocator, 157 bool manual_window_management, 158 size_t initial_window_size, 159 const struct aws_http1_connection_options *http1_options); 160 161 AWS_HTTP_API 162 struct aws_http_connection *aws_http_connection_new_http1_1_client( 163 struct aws_allocator *allocator, 164 bool manual_window_management, 165 size_t initial_window_size, 166 const struct aws_http1_connection_options *http1_options); 167 168 /* Allow tests to check current window stats */ 169 AWS_HTTP_API 170 struct aws_h1_window_stats aws_h1_connection_window_stats(struct aws_http_connection *connection_base); 171 172 AWS_EXTERN_C_END 173 174 /* DO NOT export functions below. They're only used by other .c files in this library */ 175 176 /* TODO: introduce naming conventions for private header functions */ 177 178 void aws_h1_connection_lock_synced_data(struct aws_h1_connection *connection); 179 void aws_h1_connection_unlock_synced_data(struct aws_h1_connection *connection); 180 181 /** 182 * Try to kick off the outgoing-stream-task. 183 * If task is already active, nothing happens. 184 * If there's nothing to do, the task will immediately stop itself. 185 * Call this whenever the user provides new outgoing data (ex: new stream, new chunk). 186 * MUST be called from the connection's event-loop thread. 187 */ 188 void aws_h1_connection_try_write_outgoing_stream(struct aws_h1_connection *connection); 189 190 /** 191 * If any read messages are queued, and the downstream window is non-zero, 192 * process data and send it downstream. Then calculate the connection's 193 * desired window size and increment it if necessary. 194 * 195 * During normal operations "downstream" means the current incoming stream. 196 * If the connection has switched protocols "downstream" means the next 197 * channel handler in the read direction. 198 */ 199 void aws_h1_connection_try_process_read_messages(struct aws_h1_connection *connection); 200 201 #endif /* AWS_HTTP_H1_CONNECTION_H */ 202