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