1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 
6 #include <aws/http/private/connection_impl.h>
7 #include <aws/http/private/connection_monitor.h>
8 
9 #include <aws/http/private/h1_connection.h>
10 #include <aws/http/private/h2_connection.h>
11 
12 #include <aws/http/private/proxy_impl.h>
13 
14 #include <aws/common/hash_table.h>
15 #include <aws/common/mutex.h>
16 #include <aws/common/string.h>
17 #include <aws/http/request_response.h>
18 #include <aws/io/channel_bootstrap.h>
19 #include <aws/io/logging.h>
20 #include <aws/io/socket.h>
21 #include <aws/io/tls_channel_handler.h>
22 
23 #if _MSC_VER
24 #    pragma warning(disable : 4204) /* non-constant aggregate initializer */
25 #    pragma warning(disable : 4232) /* function pointer to dll symbol */
26 #endif
27 
28 static struct aws_http_connection_system_vtable s_default_system_vtable = {
29     .new_socket_channel = aws_client_bootstrap_new_socket_channel,
30 };
31 
32 static const struct aws_http_connection_system_vtable *s_system_vtable_ptr = &s_default_system_vtable;
33 
aws_http_client_bootstrap_destroy(struct aws_http_client_bootstrap * bootstrap)34 void aws_http_client_bootstrap_destroy(struct aws_http_client_bootstrap *bootstrap) {
35     /* During allocating, the underlying stuctures should be allocated with the bootstrap by aws_mem_acquire_many. Thus,
36      * we only need to clean up the first pointer which is the bootstrap */
37     if (bootstrap->alpn_string_map) {
38         aws_hash_table_clean_up(bootstrap->alpn_string_map);
39     }
40     aws_mem_release(bootstrap->alloc, bootstrap);
41 }
42 
aws_http_connection_set_system_vtable(const struct aws_http_connection_system_vtable * system_vtable)43 void aws_http_connection_set_system_vtable(const struct aws_http_connection_system_vtable *system_vtable) {
44     s_system_vtable_ptr = system_vtable;
45 }
46 
47 AWS_STATIC_STRING_FROM_LITERAL(s_alpn_protocol_http_1_1, "http/1.1");
48 AWS_STATIC_STRING_FROM_LITERAL(s_alpn_protocol_http_2, "h2");
49 
50 struct aws_http_server {
51     struct aws_allocator *alloc;
52     struct aws_server_bootstrap *bootstrap;
53     bool is_using_tls;
54     bool manual_window_management;
55     size_t initial_window_size;
56     void *user_data;
57     aws_http_server_on_incoming_connection_fn *on_incoming_connection;
58     aws_http_server_on_destroy_fn *on_destroy_complete;
59     struct aws_socket *socket;
60 
61     /* Any thread may touch this data, but the lock must be held */
62     struct {
63         struct aws_mutex lock;
64         bool is_shutting_down;
65         struct aws_hash_table channel_to_connection_map;
66     } synced_data;
67 };
68 
s_server_lock_synced_data(struct aws_http_server * server)69 static void s_server_lock_synced_data(struct aws_http_server *server) {
70     int err = aws_mutex_lock(&server->synced_data.lock);
71     AWS_ASSERT(!err);
72     (void)err;
73 }
74 
s_server_unlock_synced_data(struct aws_http_server * server)75 static void s_server_unlock_synced_data(struct aws_http_server *server) {
76     int err = aws_mutex_unlock(&server->synced_data.lock);
77     AWS_ASSERT(!err);
78     (void)err;
79 }
80 
81 /* Determine the http-version, create appropriate type of connection, and insert it into the channel. */
aws_http_connection_new_channel_handler(struct aws_allocator * alloc,struct aws_channel * channel,bool is_server,bool is_using_tls,bool manual_window_management,bool prior_knowledge_http2,size_t initial_window_size,const struct aws_hash_table * alpn_string_map,const struct aws_http1_connection_options * http1_options,const struct aws_http2_connection_options * http2_options)82 struct aws_http_connection *aws_http_connection_new_channel_handler(
83     struct aws_allocator *alloc,
84     struct aws_channel *channel,
85     bool is_server,
86     bool is_using_tls,
87     bool manual_window_management,
88     bool prior_knowledge_http2,
89     size_t initial_window_size,
90     const struct aws_hash_table *alpn_string_map,
91     const struct aws_http1_connection_options *http1_options,
92     const struct aws_http2_connection_options *http2_options) {
93 
94     struct aws_channel_slot *connection_slot = NULL;
95     struct aws_http_connection *connection = NULL;
96 
97     /* Create slot for connection. */
98     connection_slot = aws_channel_slot_new(channel);
99     if (!connection_slot) {
100         AWS_LOGF_ERROR(
101             AWS_LS_HTTP_CONNECTION,
102             "static: Failed to create slot in channel %p, error %d (%s).",
103             (void *)channel,
104             aws_last_error(),
105             aws_error_name(aws_last_error()));
106 
107         goto error;
108     }
109 
110     int err = aws_channel_slot_insert_end(channel, connection_slot);
111     if (err) {
112         AWS_LOGF_ERROR(
113             AWS_LS_HTTP_CONNECTION,
114             "static: Failed to insert slot into channel %p, error %d (%s).",
115             (void *)channel,
116             aws_last_error(),
117             aws_error_name(aws_last_error()));
118         goto error;
119     }
120 
121     /* Determine HTTP version */
122     enum aws_http_version version = AWS_HTTP_VERSION_1_1;
123 
124     if (is_using_tls) {
125         /* Query TLS channel handler (immediately to left in the channel) for negotiated ALPN protocol */
126         if (!connection_slot->adj_left || !connection_slot->adj_left->handler) {
127             aws_raise_error(AWS_ERROR_INVALID_STATE);
128             AWS_LOGF_ERROR(
129                 AWS_LS_HTTP_CONNECTION, "static: Failed to find TLS handler in channel %p.", (void *)channel);
130             goto error;
131         }
132 
133         struct aws_channel_slot *tls_slot = connection_slot->adj_left;
134         struct aws_channel_handler *tls_handler = tls_slot->handler;
135         struct aws_byte_buf protocol = aws_tls_handler_protocol(tls_handler);
136         if (protocol.len) {
137             bool customized = false;
138             if (alpn_string_map) {
139                 customized = true;
140                 struct aws_string *negotiated_result = aws_string_new_from_buf(alloc, &protocol);
141                 struct aws_hash_element *found = NULL;
142                 aws_hash_table_find(alpn_string_map, (void *)negotiated_result, &found);
143                 if (found) {
144                     version = (enum aws_http_version)(size_t)found->value;
145                     AWS_LOGF_DEBUG(
146                         AWS_LS_HTTP_CONNECTION,
147                         "static: Customized ALPN protocol " PRInSTR " used. " PRInSTR " client connection established.",
148                         AWS_BYTE_BUF_PRI(protocol),
149                         AWS_BYTE_CURSOR_PRI(aws_http_version_to_str(version)));
150                 } else {
151                     AWS_LOGF_ERROR(
152                         AWS_LS_HTTP_CONNECTION,
153                         "static: Customized ALPN protocol " PRInSTR
154                         " used. However the it's not found in the ALPN map provided.",
155                         AWS_BYTE_BUF_PRI(protocol));
156                     version = AWS_HTTP_VERSION_UNKNOWN;
157                 }
158                 aws_string_destroy(negotiated_result);
159             }
160             if (customized) {
161                 /* Do nothing */
162             } else if (aws_string_eq_byte_buf(s_alpn_protocol_http_1_1, &protocol)) {
163                 version = AWS_HTTP_VERSION_1_1;
164             } else if (aws_string_eq_byte_buf(s_alpn_protocol_http_2, &protocol)) {
165                 version = AWS_HTTP_VERSION_2;
166             } else {
167                 AWS_LOGF_WARN(AWS_LS_HTTP_CONNECTION, "static: Unrecognized ALPN protocol. Assuming HTTP/1.1");
168                 AWS_LOGF_DEBUG(
169                     AWS_LS_HTTP_CONNECTION, "static: Unrecognized ALPN protocol " PRInSTR, AWS_BYTE_BUF_PRI(protocol));
170 
171                 version = AWS_HTTP_VERSION_1_1;
172             }
173         }
174     } else {
175         if (prior_knowledge_http2) {
176             version = AWS_HTTP_VERSION_2;
177         }
178     }
179 
180     /* Create connection/handler */
181     switch (version) {
182         case AWS_HTTP_VERSION_1_1:
183             if (is_server) {
184                 connection = aws_http_connection_new_http1_1_server(
185                     alloc, manual_window_management, initial_window_size, http1_options);
186             } else {
187                 connection = aws_http_connection_new_http1_1_client(
188                     alloc, manual_window_management, initial_window_size, http1_options);
189             }
190             break;
191         case AWS_HTTP_VERSION_2:
192             if (is_server) {
193                 connection = aws_http_connection_new_http2_server(alloc, manual_window_management, http2_options);
194             } else {
195                 connection = aws_http_connection_new_http2_client(alloc, manual_window_management, http2_options);
196             }
197             break;
198         default:
199             AWS_LOGF_ERROR(
200                 AWS_LS_HTTP_CONNECTION,
201                 "static: Unsupported version " PRInSTR,
202                 AWS_BYTE_CURSOR_PRI(aws_http_version_to_str(version)));
203 
204             aws_raise_error(AWS_ERROR_HTTP_UNSUPPORTED_PROTOCOL);
205             goto error;
206     }
207 
208     if (!connection) {
209         AWS_LOGF_ERROR(
210             AWS_LS_HTTP_CONNECTION,
211             "static: Failed to create " PRInSTR " %s connection object, error %d (%s).",
212             AWS_BYTE_CURSOR_PRI(aws_http_version_to_str(version)),
213             is_server ? "server" : "client",
214             aws_last_error(),
215             aws_error_name(aws_last_error()));
216 
217         goto error;
218     }
219 
220     /* Connect handler and slot */
221     if (aws_channel_slot_set_handler(connection_slot, &connection->channel_handler)) {
222         AWS_LOGF_ERROR(
223             AWS_LS_HTTP_CONNECTION,
224             "static: Failed to set HTTP handler into slot on channel %p, error %d (%s).",
225             (void *)channel,
226             aws_last_error(),
227             aws_error_name(aws_last_error()));
228 
229         goto error;
230     }
231 
232     /* Success! Inform connection that installation is complete */
233     connection->vtable->on_channel_handler_installed(&connection->channel_handler, connection_slot);
234 
235     return connection;
236 
237 error:
238     if (connection_slot) {
239         if (!connection_slot->handler && connection) {
240             aws_channel_handler_destroy(&connection->channel_handler);
241         }
242 
243         aws_channel_slot_remove(connection_slot);
244     }
245 
246     return NULL;
247 }
248 
aws_http_connection_close(struct aws_http_connection * connection)249 void aws_http_connection_close(struct aws_http_connection *connection) {
250     AWS_ASSERT(connection);
251     connection->vtable->close(connection);
252 }
253 
aws_http_connection_is_open(const struct aws_http_connection * connection)254 bool aws_http_connection_is_open(const struct aws_http_connection *connection) {
255     AWS_ASSERT(connection);
256     return connection->vtable->is_open(connection);
257 }
258 
aws_http_connection_new_requests_allowed(const struct aws_http_connection * connection)259 bool aws_http_connection_new_requests_allowed(const struct aws_http_connection *connection) {
260     AWS_ASSERT(connection);
261     return connection->vtable->new_requests_allowed(connection);
262 }
263 
aws_http_connection_is_client(const struct aws_http_connection * connection)264 bool aws_http_connection_is_client(const struct aws_http_connection *connection) {
265     return connection->client_data;
266 }
267 
aws_http_connection_is_server(const struct aws_http_connection * connection)268 bool aws_http_connection_is_server(const struct aws_http_connection *connection) {
269     return connection->server_data;
270 }
271 
aws_http2_connection_change_settings(struct aws_http_connection * http2_connection,const struct aws_http2_setting * settings_array,size_t num_settings,aws_http2_on_change_settings_complete_fn * on_completed,void * user_data)272 int aws_http2_connection_change_settings(
273     struct aws_http_connection *http2_connection,
274     const struct aws_http2_setting *settings_array,
275     size_t num_settings,
276     aws_http2_on_change_settings_complete_fn *on_completed,
277     void *user_data) {
278     AWS_ASSERT(http2_connection);
279     AWS_PRECONDITION(http2_connection->vtable);
280     AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
281     return http2_connection->vtable->change_settings(
282         http2_connection, settings_array, num_settings, on_completed, user_data);
283 }
284 
aws_http2_connection_ping(struct aws_http_connection * http2_connection,const struct aws_byte_cursor * optional_opaque_data,aws_http2_on_ping_complete_fn * on_ack,void * user_data)285 int aws_http2_connection_ping(
286     struct aws_http_connection *http2_connection,
287     const struct aws_byte_cursor *optional_opaque_data,
288     aws_http2_on_ping_complete_fn *on_ack,
289     void *user_data) {
290     AWS_ASSERT(http2_connection);
291     AWS_PRECONDITION(http2_connection->vtable);
292     AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
293     return http2_connection->vtable->send_ping(http2_connection, optional_opaque_data, on_ack, user_data);
294 }
295 
aws_http2_connection_send_goaway(struct aws_http_connection * http2_connection,uint32_t http2_error,bool allow_more_streams,const struct aws_byte_cursor * optional_debug_data)296 int aws_http2_connection_send_goaway(
297     struct aws_http_connection *http2_connection,
298     uint32_t http2_error,
299     bool allow_more_streams,
300     const struct aws_byte_cursor *optional_debug_data) {
301     AWS_ASSERT(http2_connection);
302     AWS_PRECONDITION(http2_connection->vtable);
303     AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
304     return http2_connection->vtable->send_goaway(
305         http2_connection, http2_error, allow_more_streams, optional_debug_data);
306 }
307 
aws_http2_connection_get_sent_goaway(struct aws_http_connection * http2_connection,uint32_t * out_http2_error,uint32_t * out_last_stream_id)308 int aws_http2_connection_get_sent_goaway(
309     struct aws_http_connection *http2_connection,
310     uint32_t *out_http2_error,
311     uint32_t *out_last_stream_id) {
312     AWS_ASSERT(http2_connection);
313     AWS_PRECONDITION(out_http2_error);
314     AWS_PRECONDITION(out_last_stream_id);
315     AWS_PRECONDITION(http2_connection->vtable);
316     AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
317     return http2_connection->vtable->get_sent_goaway(http2_connection, out_http2_error, out_last_stream_id);
318 }
319 
aws_http2_connection_get_received_goaway(struct aws_http_connection * http2_connection,uint32_t * out_http2_error,uint32_t * out_last_stream_id)320 int aws_http2_connection_get_received_goaway(
321     struct aws_http_connection *http2_connection,
322     uint32_t *out_http2_error,
323     uint32_t *out_last_stream_id) {
324     AWS_ASSERT(http2_connection);
325     AWS_PRECONDITION(out_http2_error);
326     AWS_PRECONDITION(out_last_stream_id);
327     AWS_PRECONDITION(http2_connection->vtable);
328     AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
329     return http2_connection->vtable->get_received_goaway(http2_connection, out_http2_error, out_last_stream_id);
330 }
331 
aws_http2_connection_get_local_settings(const struct aws_http_connection * http2_connection,struct aws_http2_setting out_settings[AWS_HTTP2_SETTINGS_COUNT])332 void aws_http2_connection_get_local_settings(
333     const struct aws_http_connection *http2_connection,
334     struct aws_http2_setting out_settings[AWS_HTTP2_SETTINGS_COUNT]) {
335     AWS_ASSERT(http2_connection);
336     AWS_PRECONDITION(http2_connection->vtable);
337     AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
338     http2_connection->vtable->get_local_settings(http2_connection, out_settings);
339 }
340 
aws_http2_connection_get_remote_settings(const struct aws_http_connection * http2_connection,struct aws_http2_setting out_settings[AWS_HTTP2_SETTINGS_COUNT])341 void aws_http2_connection_get_remote_settings(
342     const struct aws_http_connection *http2_connection,
343     struct aws_http2_setting out_settings[AWS_HTTP2_SETTINGS_COUNT]) {
344     AWS_ASSERT(http2_connection);
345     AWS_PRECONDITION(http2_connection->vtable);
346     AWS_FATAL_ASSERT(http2_connection->http_version == AWS_HTTP_VERSION_2);
347     http2_connection->vtable->get_remote_settings(http2_connection, out_settings);
348 }
349 
aws_http2_connection_update_window(struct aws_http_connection * http2_connection,uint32_t increment_size)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);
355 }
356 
aws_http_connection_get_channel(struct aws_http_connection * connection)357 struct aws_channel *aws_http_connection_get_channel(struct aws_http_connection *connection) {
358     AWS_ASSERT(connection);
359     return connection->channel_slot->channel;
360 }
361 
aws_http_alpn_map_init(struct aws_allocator * allocator,struct aws_hash_table * map)362 int aws_http_alpn_map_init(struct aws_allocator *allocator, struct aws_hash_table *map) {
363     AWS_ASSERT(allocator);
364     AWS_ASSERT(map);
365     int result = aws_hash_table_init(
366         map,
367         allocator,
368         5 /* initial size */,
369         aws_hash_string,
370         aws_hash_callback_string_eq,
371         aws_hash_callback_string_destroy,
372         NULL);
373     if (result) {
374         /* OOM will crash */
375         int error_code = aws_last_error();
376         AWS_LOGF_ERROR(
377             AWS_LS_HTTP_CONNECTION,
378             "Failed to initialize ALPN map with error code %d (%s)",
379             error_code,
380             aws_error_name(error_code));
381     }
382     return result;
383 }
384 
aws_http_connection_acquire(struct aws_http_connection * connection)385 void aws_http_connection_acquire(struct aws_http_connection *connection) {
386     AWS_ASSERT(connection);
387     aws_atomic_fetch_add(&connection->refcount, 1);
388 }
389 
aws_http_connection_release(struct aws_http_connection * connection)390 void aws_http_connection_release(struct aws_http_connection *connection) {
391     AWS_ASSERT(connection);
392     size_t prev_refcount = aws_atomic_fetch_sub(&connection->refcount, 1);
393     if (prev_refcount == 1) {
394         AWS_LOGF_TRACE(
395             AWS_LS_HTTP_CONNECTION,
396             "id=%p: Final connection refcount released, shut down if necessary.",
397             (void *)connection);
398 
399         /* Channel might already be shut down, but make sure */
400         aws_channel_shutdown(connection->channel_slot->channel, AWS_ERROR_SUCCESS);
401 
402         /* When the channel's refcount reaches 0, it destroys its slots/handlers, which will destroy the connection */
403         aws_channel_release_hold(connection->channel_slot->channel);
404     } else {
405         AWS_FATAL_ASSERT(prev_refcount != 0);
406         AWS_LOGF_TRACE(
407             AWS_LS_HTTP_CONNECTION,
408             "id=%p: Connection refcount released, %zu remaining.",
409             (void *)connection,
410             prev_refcount - 1);
411     }
412 }
413 
414 /* At this point, the server bootstrapper has accepted an incoming connection from a client and set up a channel.
415  * Now we need to create an aws_http_connection and insert it into the channel as a channel-handler.
416  * Note: Be careful not to access server->socket until lock is acquired to avoid race conditions */
s_server_bootstrap_on_accept_channel_setup(struct aws_server_bootstrap * bootstrap,int error_code,struct aws_channel * channel,void * user_data)417 static void s_server_bootstrap_on_accept_channel_setup(
418     struct aws_server_bootstrap *bootstrap,
419     int error_code,
420     struct aws_channel *channel,
421     void *user_data) {
422 
423     (void)bootstrap;
424     AWS_ASSERT(user_data);
425     struct aws_http_server *server = user_data;
426     bool user_cb_invoked = false;
427     struct aws_http_connection *connection = NULL;
428     if (error_code) {
429         AWS_LOGF_ERROR(
430             AWS_LS_HTTP_SERVER,
431             "%p: Incoming connection failed with error code %d (%s)",
432             (void *)server,
433             error_code,
434             aws_error_name(error_code));
435 
436         goto error;
437     }
438     /* Create connection */
439     /* TODO: expose http1/2 options to server API */
440     struct aws_http1_connection_options http1_options;
441     AWS_ZERO_STRUCT(http1_options);
442     struct aws_http2_connection_options http2_options;
443     AWS_ZERO_STRUCT(http2_options);
444     connection = aws_http_connection_new_channel_handler(
445         server->alloc,
446         channel,
447         true,
448         server->is_using_tls,
449         server->manual_window_management,
450         false, /* prior_knowledge_http2 */
451         server->initial_window_size,
452         NULL, /* alpn_string_map */
453         &http1_options,
454         &http2_options);
455     if (!connection) {
456         AWS_LOGF_ERROR(
457             AWS_LS_HTTP_SERVER,
458             "%p: Failed to create connection object, error %d (%s).",
459             (void *)server,
460             aws_last_error(),
461             aws_error_name(aws_last_error()));
462 
463         goto error;
464     }
465 
466     int put_err = 0;
467     /* BEGIN CRITICAL SECTION */
468     s_server_lock_synced_data(server);
469     if (server->synced_data.is_shutting_down) {
470         error_code = AWS_ERROR_HTTP_CONNECTION_CLOSED;
471     }
472     if (!error_code) {
473         put_err = aws_hash_table_put(&server->synced_data.channel_to_connection_map, channel, connection, NULL);
474     }
475     s_server_unlock_synced_data(server);
476     /* END CRITICAL SECTION */
477     if (error_code) {
478         AWS_LOGF_ERROR(
479             AWS_ERROR_HTTP_SERVER_CLOSED,
480             "id=%p: Incoming connection failed. The server is shutting down.",
481             (void *)server);
482         goto error;
483     }
484 
485     if (put_err) {
486         AWS_LOGF_ERROR(
487             AWS_LS_HTTP_SERVER,
488             "%p: %s:%d: Failed to store connection object, error %d (%s).",
489             (void *)server,
490             server->socket->local_endpoint.address,
491             server->socket->local_endpoint.port,
492             aws_last_error(),
493             aws_error_name(aws_last_error()));
494 
495         goto error;
496     }
497 
498     /* Tell user of successful connection. */
499     AWS_LOGF_INFO(
500         AWS_LS_HTTP_CONNECTION,
501         "id=%p: " PRInSTR " server connection established at %p %s:%d.",
502         (void *)connection,
503         AWS_BYTE_CURSOR_PRI(aws_http_version_to_str(connection->http_version)),
504         (void *)server,
505         server->socket->local_endpoint.address,
506         server->socket->local_endpoint.port);
507 
508     server->on_incoming_connection(server, connection, AWS_ERROR_SUCCESS, server->user_data);
509     user_cb_invoked = true;
510 
511     /* If user failed to configure the server during callback, shut down the channel. */
512     if (!connection->server_data->on_incoming_request) {
513         AWS_LOGF_ERROR(
514             AWS_LS_HTTP_CONNECTION,
515             "id=%p: Caller failed to invoke aws_http_connection_configure_server() during on_incoming_connection "
516             "callback, closing connection.",
517             (void *)connection);
518 
519         aws_raise_error(AWS_ERROR_HTTP_REACTION_REQUIRED);
520         goto error;
521     }
522     return;
523 
524 error:
525 
526     if (!error_code) {
527         error_code = aws_last_error();
528     }
529 
530     if (!user_cb_invoked) {
531         server->on_incoming_connection(server, NULL, error_code, server->user_data);
532     }
533 
534     if (channel) {
535         aws_channel_shutdown(channel, error_code);
536     }
537 
538     if (connection) {
539         /* release the ref count for the user side */
540         aws_http_connection_release(connection);
541     }
542 }
543 
544 /* clean the server memory up */
s_http_server_clean_up(struct aws_http_server * server)545 static void s_http_server_clean_up(struct aws_http_server *server) {
546     if (!server) {
547         return;
548     }
549 
550     aws_server_bootstrap_release(server->bootstrap);
551 
552     /* invoke the user callback */
553     if (server->on_destroy_complete) {
554         server->on_destroy_complete(server->user_data);
555     }
556     aws_hash_table_clean_up(&server->synced_data.channel_to_connection_map);
557     aws_mutex_clean_up(&server->synced_data.lock);
558     aws_mem_release(server->alloc, server);
559 }
560 
561 /* At this point, the channel for a server connection has completed shutdown, but hasn't been destroyed yet. */
s_server_bootstrap_on_accept_channel_shutdown(struct aws_server_bootstrap * bootstrap,int error_code,struct aws_channel * channel,void * user_data)562 static void s_server_bootstrap_on_accept_channel_shutdown(
563     struct aws_server_bootstrap *bootstrap,
564     int error_code,
565     struct aws_channel *channel,
566     void *user_data) {
567 
568     (void)bootstrap;
569     AWS_ASSERT(user_data);
570     struct aws_http_server *server = user_data;
571 
572     /* Figure out which connection this was, and remove that entry from the map.
573      * It won't be in the map if something went wrong while setting up the connection. */
574     struct aws_hash_element map_elem;
575     int was_present;
576 
577     /* BEGIN CRITICAL SECTION */
578     s_server_lock_synced_data(server);
579     int remove_err =
580         aws_hash_table_remove(&server->synced_data.channel_to_connection_map, channel, &map_elem, &was_present);
581     s_server_unlock_synced_data(server);
582     /* END CRITICAL SECTION */
583 
584     if (!remove_err && was_present) {
585         struct aws_http_connection *connection = map_elem.value;
586         AWS_LOGF_INFO(AWS_LS_HTTP_CONNECTION, "id=%p: Server connection shut down.", (void *)connection);
587         /* Tell user about shutdown */
588         if (connection->server_data->on_shutdown) {
589             connection->server_data->on_shutdown(connection, error_code, connection->user_data);
590         }
591     }
592 }
593 
594 /* the server listener has finished the destroy process, no existing connections
595  * finally safe to clean the server up */
s_server_bootstrap_on_server_listener_destroy(struct aws_server_bootstrap * bootstrap,void * user_data)596 static void s_server_bootstrap_on_server_listener_destroy(struct aws_server_bootstrap *bootstrap, void *user_data) {
597     (void)bootstrap;
598     AWS_ASSERT(user_data);
599     struct aws_http_server *server = user_data;
600     s_http_server_clean_up(server);
601 }
602 
aws_http_server_new(const struct aws_http_server_options * options)603 struct aws_http_server *aws_http_server_new(const struct aws_http_server_options *options) {
604     aws_http_fatal_assert_library_initialized();
605 
606     struct aws_http_server *server = NULL;
607 
608     if (!options || options->self_size == 0 || !options->allocator || !options->bootstrap || !options->socket_options ||
609         !options->on_incoming_connection || !options->endpoint) {
610 
611         AWS_LOGF_ERROR(AWS_LS_HTTP_SERVER, "static: Invalid options, cannot create server.");
612         aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
613         /* nothing to clean up */
614         return NULL;
615     }
616 
617     server = aws_mem_calloc(options->allocator, 1, sizeof(struct aws_http_server));
618     if (!server) {
619         /* nothing to clean up */
620         return NULL;
621     }
622 
623     server->alloc = options->allocator;
624     server->bootstrap = aws_server_bootstrap_acquire(options->bootstrap);
625     server->is_using_tls = options->tls_options != NULL;
626     server->initial_window_size = options->initial_window_size;
627     server->user_data = options->server_user_data;
628     server->on_incoming_connection = options->on_incoming_connection;
629     server->on_destroy_complete = options->on_destroy_complete;
630     server->manual_window_management = options->manual_window_management;
631 
632     int err = aws_mutex_init(&server->synced_data.lock);
633     if (err) {
634         AWS_LOGF_ERROR(
635             AWS_LS_HTTP_SERVER, "static: Failed to initialize mutex, error %d (%s).", err, aws_error_name(err));
636         goto mutex_error;
637     }
638     err = aws_hash_table_init(
639         &server->synced_data.channel_to_connection_map, server->alloc, 16, aws_hash_ptr, aws_ptr_eq, NULL, NULL);
640     if (err) {
641         AWS_LOGF_ERROR(
642             AWS_LS_HTTP_SERVER,
643             "static: Cannot create server, error %d (%s).",
644             aws_last_error(),
645             aws_error_name(aws_last_error()));
646         goto hash_table_error;
647     }
648     /* Protect against callbacks firing before server->socket is set */
649     s_server_lock_synced_data(server);
650     if (options->tls_options) {
651         server->is_using_tls = true;
652     }
653 
654     struct aws_server_socket_channel_bootstrap_options bootstrap_options = {
655         .enable_read_back_pressure = options->manual_window_management,
656         .tls_options = options->tls_options,
657         .bootstrap = options->bootstrap,
658         .socket_options = options->socket_options,
659         .incoming_callback = s_server_bootstrap_on_accept_channel_setup,
660         .shutdown_callback = s_server_bootstrap_on_accept_channel_shutdown,
661         .destroy_callback = s_server_bootstrap_on_server_listener_destroy,
662         .host_name = options->endpoint->address,
663         .port = options->endpoint->port,
664         .user_data = server,
665     };
666 
667     server->socket = aws_server_bootstrap_new_socket_listener(&bootstrap_options);
668 
669     s_server_unlock_synced_data(server);
670 
671     if (!server->socket) {
672         AWS_LOGF_ERROR(
673             AWS_LS_HTTP_SERVER,
674             "static: Failed creating new socket listener, error %d (%s). Cannot create server.",
675             aws_last_error(),
676             aws_error_name(aws_last_error()));
677 
678         goto socket_error;
679     }
680 
681     AWS_LOGF_INFO(
682         AWS_LS_HTTP_SERVER,
683         "%p %s:%d: Server setup complete, listening for incoming connections.",
684         (void *)server,
685         server->socket->local_endpoint.address,
686         server->socket->local_endpoint.port);
687 
688     return server;
689 
690 socket_error:
691     aws_hash_table_clean_up(&server->synced_data.channel_to_connection_map);
692 hash_table_error:
693     aws_mutex_clean_up(&server->synced_data.lock);
694 mutex_error:
695     aws_mem_release(server->alloc, server);
696     return NULL;
697 }
698 
aws_http_server_release(struct aws_http_server * server)699 void aws_http_server_release(struct aws_http_server *server) {
700     if (!server) {
701         return;
702     }
703     bool already_shutting_down = false;
704     /* BEGIN CRITICAL SECTION */
705     s_server_lock_synced_data(server);
706     if (server->synced_data.is_shutting_down) {
707         already_shutting_down = true;
708     } else {
709         server->synced_data.is_shutting_down = true;
710     }
711     if (!already_shutting_down) {
712         /* shutdown all existing channels */
713         for (struct aws_hash_iter iter = aws_hash_iter_begin(&server->synced_data.channel_to_connection_map);
714              !aws_hash_iter_done(&iter);
715              aws_hash_iter_next(&iter)) {
716             struct aws_channel *channel = (struct aws_channel *)iter.element.key;
717             aws_channel_shutdown(channel, AWS_ERROR_HTTP_CONNECTION_CLOSED);
718         }
719     }
720     s_server_unlock_synced_data(server);
721     /* END CRITICAL SECTION */
722 
723     if (already_shutting_down) {
724         /* The service is already shutting down, not shutting it down again */
725         AWS_LOGF_TRACE(AWS_LS_HTTP_SERVER, "id=%p: The server is already shutting down", (void *)server);
726         return;
727     }
728 
729     /* stop listening, clean up the socket, after all existing connections finish shutting down, the
730      * s_server_bootstrap_on_server_listener_destroy will be invoked, clean up of the server will be there */
731     AWS_LOGF_INFO(
732         AWS_LS_HTTP_SERVER,
733         "%p %s:%d: Shutting down the server.",
734         (void *)server,
735         server->socket->local_endpoint.address,
736         server->socket->local_endpoint.port);
737 
738     aws_server_bootstrap_destroy_socket_listener(server->bootstrap, server->socket);
739 
740     /* wait for connections to finish shutting down
741      * clean up will be called from eventloop */
742 }
743 
744 /* At this point, the channel bootstrapper has established a connection to the server and set up a channel.
745  * Now we need to create the aws_http_connection and insert it into the channel as a channel-handler. */
s_client_bootstrap_on_channel_setup(struct aws_client_bootstrap * channel_bootstrap,int error_code,struct aws_channel * channel,void * user_data)746 static void s_client_bootstrap_on_channel_setup(
747     struct aws_client_bootstrap *channel_bootstrap,
748     int error_code,
749     struct aws_channel *channel,
750     void *user_data) {
751 
752     (void)channel_bootstrap;
753     AWS_ASSERT(user_data);
754     struct aws_http_client_bootstrap *http_bootstrap = user_data;
755 
756     /* Contract for setup callbacks is: channel is NULL if error_code is non-zero. */
757     AWS_FATAL_ASSERT((error_code != 0) == (channel == NULL));
758 
759     if (error_code) {
760         AWS_LOGF_ERROR(
761             AWS_LS_HTTP_CONNECTION,
762             "static: Client connection failed with error %d (%s).",
763             error_code,
764             aws_error_name(error_code));
765 
766         /* Immediately tell user of failed connection.
767          * No channel exists, so there will be no channel_shutdown callback. */
768         http_bootstrap->on_setup(NULL, error_code, http_bootstrap->user_data);
769 
770         /* Clean up the http_bootstrap, it has no more work to do. */
771         aws_http_client_bootstrap_destroy(http_bootstrap);
772         return;
773     }
774 
775     AWS_LOGF_TRACE(AWS_LS_HTTP_CONNECTION, "static: Socket connected, creating client connection object.");
776 
777     http_bootstrap->connection = aws_http_connection_new_channel_handler(
778         http_bootstrap->alloc,
779         channel,
780         false,
781         http_bootstrap->is_using_tls,
782         http_bootstrap->manual_window_management,
783         http_bootstrap->prior_knowledge_http2,
784         http_bootstrap->initial_window_size,
785         http_bootstrap->alpn_string_map,
786         &http_bootstrap->http1_options,
787         &http_bootstrap->http2_options);
788     if (!http_bootstrap->connection) {
789         AWS_LOGF_ERROR(
790             AWS_LS_HTTP_CONNECTION,
791             "static: Failed to create the client connection object, error %d (%s).",
792             aws_last_error(),
793             aws_error_name(aws_last_error()));
794 
795         goto error;
796     }
797 
798     if (aws_http_connection_monitoring_options_is_valid(&http_bootstrap->monitoring_options)) {
799         /*
800          * On creation we validate monitoring options, if they exist, and fail if they're not
801          * valid.  So at this point, is_valid() functions as an is-monitoring-on? check.  A false
802          * value here is not an error, it's just not enabled.
803          */
804         struct aws_crt_statistics_handler *http_connection_monitor =
805             aws_crt_statistics_handler_new_http_connection_monitor(
806                 http_bootstrap->alloc, &http_bootstrap->monitoring_options);
807         if (http_connection_monitor == NULL) {
808             goto error;
809         }
810 
811         aws_channel_set_statistics_handler(channel, http_connection_monitor);
812     }
813 
814     http_bootstrap->connection->proxy_request_transform = http_bootstrap->proxy_request_transform;
815     http_bootstrap->connection->user_data = http_bootstrap->user_data;
816 
817     AWS_LOGF_INFO(
818         AWS_LS_HTTP_CONNECTION,
819         "id=%p: " PRInSTR " client connection established.",
820         (void *)http_bootstrap->connection,
821         AWS_BYTE_CURSOR_PRI(aws_http_version_to_str(http_bootstrap->connection->http_version)));
822 
823     /* Tell user of successful connection.
824      * Then clear the on_setup callback so that we know it's been called */
825     http_bootstrap->on_setup(http_bootstrap->connection, AWS_ERROR_SUCCESS, http_bootstrap->user_data);
826     http_bootstrap->on_setup = NULL;
827 
828     return;
829 
830 error:
831     /* Something went wrong. Invoke channel shutdown. Then wait for channel shutdown to complete
832      * before informing the user that setup failed and cleaning up the http_bootstrap.*/
833     aws_channel_shutdown(channel, aws_last_error());
834 }
835 
836 /* At this point, the channel for a client connection has completed its shutdown */
s_client_bootstrap_on_channel_shutdown(struct aws_client_bootstrap * channel_bootstrap,int error_code,struct aws_channel * channel,void * user_data)837 static void s_client_bootstrap_on_channel_shutdown(
838     struct aws_client_bootstrap *channel_bootstrap,
839     int error_code,
840     struct aws_channel *channel,
841     void *user_data) {
842 
843     (void)channel_bootstrap;
844     (void)channel;
845 
846     AWS_ASSERT(user_data);
847     struct aws_http_client_bootstrap *http_bootstrap = user_data;
848 
849     /* If on_setup hasn't been called yet, inform user of failed setup.
850      * If on_setup was already called, inform user that it's shut down now. */
851     if (http_bootstrap->on_setup) {
852         /* make super duper sure that failed setup receives a non-zero error_code */
853         if (error_code == 0) {
854             error_code = AWS_ERROR_UNKNOWN;
855         }
856 
857         AWS_LOGF_ERROR(
858             AWS_LS_HTTP_CONNECTION,
859             "static: Client setup failed with error %d (%s).",
860             error_code,
861             aws_error_name(error_code));
862 
863         http_bootstrap->on_setup(NULL, error_code, http_bootstrap->user_data);
864 
865     } else if (http_bootstrap->on_shutdown) {
866         AWS_LOGF_INFO(
867             AWS_LS_HTTP_CONNECTION,
868             "%p: Client shutdown completed with error %d (%s).",
869             (void *)http_bootstrap->connection,
870             error_code,
871             aws_error_name(error_code));
872 
873         http_bootstrap->on_shutdown(http_bootstrap->connection, error_code, http_bootstrap->user_data);
874     }
875 
876     /* Clean up bootstrapper */
877     aws_http_client_bootstrap_destroy(http_bootstrap);
878 }
879 
s_validate_http_client_connection_options(const struct aws_http_client_connection_options * options)880 static int s_validate_http_client_connection_options(const struct aws_http_client_connection_options *options) {
881     if (options->self_size == 0) {
882         AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION, "static: Invalid connection options, self size not initialized");
883         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
884     }
885 
886     if (!options->allocator) {
887         AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION, "static: Invalid connection options, no allocator supplied");
888         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
889     }
890 
891     if (options->host_name.len == 0) {
892         AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION, "static: Invalid connection options, empty host name.");
893         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
894     }
895 
896     if (!options->socket_options) {
897         AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION, "static: Invalid connection options, socket options are null.");
898         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
899     }
900 
901     if (!options->on_setup) {
902         AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION, "static: Invalid connection options, setup callback is null");
903         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
904     }
905 
906     /* http2_options cannot be NULL here, calling function adds them if they were missing */
907     if (options->http2_options->num_initial_settings > 0 && options->http2_options->initial_settings_array) {
908         AWS_LOGF_ERROR(
909             AWS_LS_HTTP_CONNECTION,
910             "static: Invalid connection options, h2 settings count is non-zero but settings array is null");
911         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
912     }
913 
914     if (options->monitoring_options && !aws_http_connection_monitoring_options_is_valid(options->monitoring_options)) {
915         AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION, "static: Invalid connection options, invalid monitoring options");
916         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
917     }
918 
919     return AWS_OP_SUCCESS;
920 }
921 
922 struct s_copy_alpn_string_map_context {
923     struct aws_hash_table *map;
924     struct aws_allocator *allocator;
925 };
926 
927 /* put every item into the source to make a deep copy of the map */
s_copy_alpn_string_map(void * context,struct aws_hash_element * item)928 static int s_copy_alpn_string_map(void *context, struct aws_hash_element *item) {
929     struct s_copy_alpn_string_map_context *func_context = context;
930     struct aws_hash_table *dest = func_context->map;
931     /* make a deep copy of the string and hash map will own the copy */
932     struct aws_string *key_copy = aws_string_new_from_string(func_context->allocator, item->key);
933     int was_created;
934     if (aws_hash_table_put(dest, key_copy, item->value, &was_created)) {
935         int error_code = aws_last_error();
936         AWS_LOGF_ERROR(
937             AWS_LS_HTTP_CONNECTION,
938             "Failed to copy ALPN map with error code %d (%s)",
939             error_code,
940             aws_error_name(error_code));
941         /* failed to put into the table, we need to clean up the copy ourselves */
942         aws_string_destroy(key_copy);
943         /* return error to stop iteration */
944         return AWS_COMMON_HASH_TABLE_ITER_ERROR;
945     }
946     if (!was_created) {
947         /* no new entry created, clean up the copy ourselves */
948         aws_string_destroy(key_copy);
949     }
950     return AWS_COMMON_HASH_TABLE_ITER_CONTINUE;
951 }
952 
aws_http_alpn_map_init_copy(struct aws_allocator * allocator,struct aws_hash_table * dest,struct aws_hash_table * src)953 int aws_http_alpn_map_init_copy(
954     struct aws_allocator *allocator,
955     struct aws_hash_table *dest,
956     struct aws_hash_table *src) {
957     if (aws_http_alpn_map_init(allocator, dest)) {
958         return AWS_OP_ERR;
959     }
960     struct s_copy_alpn_string_map_context context;
961     context.allocator = allocator;
962     context.map = dest;
963     /* make a deep copy of the map */
964     if (aws_hash_table_foreach(src, s_copy_alpn_string_map, &context)) {
965         int error_code = aws_last_error();
966         AWS_LOGF_ERROR(
967             AWS_LS_HTTP_CONNECTION,
968             "Failed to copy ALPN map with error code %d (%s)",
969             error_code,
970             aws_error_name(error_code));
971         aws_hash_table_clean_up(dest);
972         return AWS_OP_ERR;
973     }
974     return AWS_OP_SUCCESS;
975 }
976 
aws_http_client_connect_internal(const struct aws_http_client_connection_options * orig_options,aws_http_proxy_request_transform_fn * proxy_request_transform)977 int aws_http_client_connect_internal(
978     const struct aws_http_client_connection_options *orig_options,
979     aws_http_proxy_request_transform_fn *proxy_request_transform) {
980 
981     if (!orig_options) {
982         AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION, "static: http connection options are null.");
983         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
984     }
985     struct aws_http_client_bootstrap *http_bootstrap = NULL;
986     struct aws_string *host_name = NULL;
987     int err = 0;
988 
989     /* make copy of options, and add defaults for missing optional structs */
990     struct aws_http_client_connection_options options = *orig_options;
991     if (options.prior_knowledge_http2 && options.tls_options) {
992         AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION, "static: HTTP/2 prior knowledge only works with cleartext TCP.");
993         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
994     }
995 
996     struct aws_http1_connection_options default_http1_options;
997     AWS_ZERO_STRUCT(default_http1_options);
998     if (options.http1_options == NULL) {
999         options.http1_options = &default_http1_options;
1000     }
1001 
1002     struct aws_http2_connection_options default_http2_options;
1003     AWS_ZERO_STRUCT(default_http2_options);
1004     if (options.http2_options == NULL) {
1005         options.http2_options = &default_http2_options;
1006     }
1007 
1008     /* validate options */
1009     if (s_validate_http_client_connection_options(&options)) {
1010         goto error;
1011     }
1012 
1013     AWS_FATAL_ASSERT(options.proxy_options == NULL);
1014 
1015     /* bootstrap_new() functions requires a null-terminated c-str */
1016     host_name = aws_string_new_from_cursor(options.allocator, &options.host_name);
1017     if (!host_name) {
1018         goto error;
1019     }
1020 
1021     struct aws_http2_setting *setting_array = NULL;
1022     struct aws_hash_table *alpn_string_map = NULL;
1023     if (!aws_mem_acquire_many(
1024             options.allocator,
1025             3,
1026             &http_bootstrap,
1027             sizeof(struct aws_http_client_bootstrap),
1028             &setting_array,
1029             options.http2_options->num_initial_settings * sizeof(struct aws_http2_setting),
1030             &alpn_string_map,
1031             sizeof(struct aws_hash_table))) {
1032         goto error;
1033     }
1034 
1035     AWS_ZERO_STRUCT(*http_bootstrap);
1036 
1037     http_bootstrap->alloc = options.allocator;
1038     http_bootstrap->is_using_tls = options.tls_options != NULL;
1039     http_bootstrap->manual_window_management = options.manual_window_management;
1040     http_bootstrap->prior_knowledge_http2 = options.prior_knowledge_http2;
1041     http_bootstrap->initial_window_size = options.initial_window_size;
1042     http_bootstrap->user_data = options.user_data;
1043     http_bootstrap->on_setup = options.on_setup;
1044     http_bootstrap->on_shutdown = options.on_shutdown;
1045     http_bootstrap->proxy_request_transform = proxy_request_transform;
1046     http_bootstrap->http1_options = *options.http1_options;
1047     http_bootstrap->http2_options = *options.http2_options;
1048 
1049     /* keep a copy of the settings array if it's not NULL */
1050     if (options.http2_options->num_initial_settings > 0) {
1051         memcpy(
1052             setting_array,
1053             options.http2_options->initial_settings_array,
1054             options.http2_options->num_initial_settings * sizeof(struct aws_http2_setting));
1055         http_bootstrap->http2_options.initial_settings_array = setting_array;
1056     }
1057 
1058     if (options.alpn_string_map) {
1059         if (aws_http_alpn_map_init_copy(options.allocator, alpn_string_map, options.alpn_string_map)) {
1060             goto error;
1061         }
1062         http_bootstrap->alpn_string_map = alpn_string_map;
1063     }
1064 
1065     if (options.monitoring_options) {
1066         http_bootstrap->monitoring_options = *options.monitoring_options;
1067     }
1068 
1069     AWS_LOGF_TRACE(
1070         AWS_LS_HTTP_CONNECTION,
1071         "static: attempting to initialize a new client channel to %s:%d",
1072         aws_string_c_str(host_name),
1073         (int)options.port);
1074 
1075     struct aws_socket_channel_bootstrap_options channel_options = {
1076         .bootstrap = options.bootstrap,
1077         .host_name = aws_string_c_str(host_name),
1078         .port = options.port,
1079         .socket_options = options.socket_options,
1080         .tls_options = options.tls_options,
1081         .setup_callback = s_client_bootstrap_on_channel_setup,
1082         .shutdown_callback = s_client_bootstrap_on_channel_shutdown,
1083         .enable_read_back_pressure = options.manual_window_management,
1084         .user_data = http_bootstrap,
1085     };
1086 
1087     err = s_system_vtable_ptr->new_socket_channel(&channel_options);
1088 
1089     if (err) {
1090         AWS_LOGF_ERROR(
1091             AWS_LS_HTTP_CONNECTION,
1092             "static: Failed to initiate socket channel for new client connection, error %d (%s).",
1093             aws_last_error(),
1094             aws_error_name(aws_last_error()));
1095 
1096         goto error;
1097     }
1098 
1099     aws_string_destroy(host_name);
1100     return AWS_OP_SUCCESS;
1101 
1102 error:
1103     if (http_bootstrap) {
1104         aws_http_client_bootstrap_destroy(http_bootstrap);
1105     }
1106 
1107     if (host_name) {
1108         aws_string_destroy(host_name);
1109     }
1110 
1111     return AWS_OP_ERR;
1112 }
1113 
aws_http_client_connect(const struct aws_http_client_connection_options * options)1114 int aws_http_client_connect(const struct aws_http_client_connection_options *options) {
1115     aws_http_fatal_assert_library_initialized();
1116     if (options->prior_knowledge_http2 && options->tls_options) {
1117         AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION, "static: HTTP/2 prior knowledge only works with cleartext TCP.");
1118         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
1119     }
1120 
1121     if (options->proxy_options != NULL) {
1122         return aws_http_client_connect_via_proxy(options);
1123     } else {
1124         if (!options->proxy_ev_settings || options->proxy_ev_settings->env_var_type != AWS_HPEV_ENABLE) {
1125             return aws_http_client_connect_internal(options, NULL);
1126         } else {
1127             /* Proxy through envrionment variable is enabled */
1128             return aws_http_client_connect_via_proxy(options);
1129         }
1130     }
1131 }
1132 
aws_http_connection_get_version(const struct aws_http_connection * connection)1133 enum aws_http_version aws_http_connection_get_version(const struct aws_http_connection *connection) {
1134     return connection->http_version;
1135 }
1136 
aws_http_connection_configure_server(struct aws_http_connection * connection,const struct aws_http_server_connection_options * options)1137 int aws_http_connection_configure_server(
1138     struct aws_http_connection *connection,
1139     const struct aws_http_server_connection_options *options) {
1140 
1141     if (!connection || !options || !options->on_incoming_request) {
1142         AWS_LOGF_ERROR(AWS_LS_HTTP_CONNECTION, "id=%p: Invalid server configuration options.", (void *)connection);
1143         return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
1144     }
1145 
1146     if (!connection->server_data) {
1147         AWS_LOGF_WARN(
1148             AWS_LS_HTTP_CONNECTION,
1149             "id=%p: Server-only function invoked on client, ignoring call.",
1150             (void *)connection);
1151         return aws_raise_error(AWS_ERROR_INVALID_STATE);
1152     }
1153     if (connection->server_data->on_incoming_request) {
1154         AWS_LOGF_WARN(
1155             AWS_LS_HTTP_CONNECTION, "id=%p: Connection is already configured, ignoring call.", (void *)connection);
1156         return aws_raise_error(AWS_ERROR_INVALID_STATE);
1157     }
1158 
1159     connection->user_data = options->connection_user_data;
1160     connection->server_data->on_incoming_request = options->on_incoming_request;
1161     connection->server_data->on_shutdown = options->on_shutdown;
1162 
1163     return AWS_OP_SUCCESS;
1164 }
1165 
1166 /* Stream IDs are only 31 bits [5.1.1] */
1167 static const uint32_t MAX_STREAM_ID = UINT32_MAX >> 1;
1168 
aws_http_connection_get_next_stream_id(struct aws_http_connection * connection)1169 uint32_t aws_http_connection_get_next_stream_id(struct aws_http_connection *connection) {
1170 
1171     uint32_t next_id = connection->next_stream_id;
1172 
1173     if (AWS_UNLIKELY(next_id > MAX_STREAM_ID)) {
1174         AWS_LOGF_INFO(AWS_LS_HTTP_CONNECTION, "id=%p: All available stream ids are gone", (void *)connection);
1175 
1176         next_id = 0;
1177         aws_raise_error(AWS_ERROR_HTTP_STREAM_IDS_EXHAUSTED);
1178     } else {
1179         connection->next_stream_id += 2;
1180     }
1181     return next_id;
1182 }
1183