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