summaryrefslogtreecommitdiffstats
path: root/contrib/restricted/aws/aws-c-io/source/channel_bootstrap.c
diff options
context:
space:
mode:
authorDevtools Arcadia <[email protected]>2022-02-07 18:08:42 +0300
committerDevtools Arcadia <[email protected]>2022-02-07 18:08:42 +0300
commit1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch)
treee26c9fed0de5d9873cce7e00bc214573dc2195b7 /contrib/restricted/aws/aws-c-io/source/channel_bootstrap.c
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'contrib/restricted/aws/aws-c-io/source/channel_bootstrap.c')
-rw-r--r--contrib/restricted/aws/aws-c-io/source/channel_bootstrap.c1421
1 files changed, 1421 insertions, 0 deletions
diff --git a/contrib/restricted/aws/aws-c-io/source/channel_bootstrap.c b/contrib/restricted/aws/aws-c-io/source/channel_bootstrap.c
new file mode 100644
index 00000000000..f5e0ad7affc
--- /dev/null
+++ b/contrib/restricted/aws/aws-c-io/source/channel_bootstrap.c
@@ -0,0 +1,1421 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+#include <aws/io/channel_bootstrap.h>
+
+#include <aws/common/ref_count.h>
+#include <aws/common/string.h>
+#include <aws/io/event_loop.h>
+#include <aws/io/logging.h>
+#include <aws/io/socket.h>
+#include <aws/io/socket_channel_handler.h>
+#include <aws/io/tls_channel_handler.h>
+
+#if _MSC_VER
+/* non-constant aggregate initializer */
+# pragma warning(disable : 4204)
+/* allow automatic variable to escape scope
+ (it's intentional and we make sure it doesn't actually return
+ before the task is finished).*/
+# pragma warning(disable : 4221)
+#endif
+
+#define DEFAULT_DNS_TTL 30
+
+static void s_client_bootstrap_destroy_impl(struct aws_client_bootstrap *bootstrap) {
+ AWS_ASSERT(bootstrap);
+ AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: destroying", (void *)bootstrap);
+ aws_client_bootstrap_shutdown_complete_fn *on_shutdown_complete = bootstrap->on_shutdown_complete;
+ void *user_data = bootstrap->user_data;
+
+ aws_event_loop_group_release(bootstrap->event_loop_group);
+ aws_host_resolver_release(bootstrap->host_resolver);
+
+ aws_mem_release(bootstrap->allocator, bootstrap);
+
+ if (on_shutdown_complete) {
+ on_shutdown_complete(user_data);
+ }
+}
+
+struct aws_client_bootstrap *aws_client_bootstrap_acquire(struct aws_client_bootstrap *bootstrap) {
+ if (bootstrap != NULL) {
+ aws_ref_count_acquire(&bootstrap->ref_count);
+ }
+
+ return bootstrap;
+}
+
+void aws_client_bootstrap_release(struct aws_client_bootstrap *bootstrap) {
+ AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: releasing bootstrap reference", (void *)bootstrap);
+ if (bootstrap != NULL) {
+ aws_ref_count_release(&bootstrap->ref_count);
+ }
+}
+
+struct aws_client_bootstrap *aws_client_bootstrap_new(
+ struct aws_allocator *allocator,
+ const struct aws_client_bootstrap_options *options) {
+ AWS_ASSERT(allocator);
+ AWS_ASSERT(options);
+ AWS_ASSERT(options->event_loop_group);
+
+ struct aws_client_bootstrap *bootstrap = aws_mem_calloc(allocator, 1, sizeof(struct aws_client_bootstrap));
+ if (!bootstrap) {
+ return NULL;
+ }
+
+ AWS_LOGF_INFO(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: Initializing client bootstrap with event-loop group %p",
+ (void *)bootstrap,
+ (void *)options->event_loop_group);
+
+ bootstrap->allocator = allocator;
+ bootstrap->event_loop_group = aws_event_loop_group_acquire(options->event_loop_group);
+ bootstrap->on_protocol_negotiated = NULL;
+ aws_ref_count_init(
+ &bootstrap->ref_count, bootstrap, (aws_simple_completion_callback *)s_client_bootstrap_destroy_impl);
+ bootstrap->host_resolver = aws_host_resolver_acquire(options->host_resolver);
+ bootstrap->on_shutdown_complete = options->on_shutdown_complete;
+ bootstrap->user_data = options->user_data;
+
+ if (options->host_resolution_config) {
+ bootstrap->host_resolver_config = *options->host_resolution_config;
+ } else {
+ bootstrap->host_resolver_config = (struct aws_host_resolution_config){
+ .impl = aws_default_dns_resolve,
+ .max_ttl = DEFAULT_DNS_TTL,
+ .impl_data = NULL,
+ };
+ }
+
+ return bootstrap;
+}
+
+int aws_client_bootstrap_set_alpn_callback(
+ struct aws_client_bootstrap *bootstrap,
+ aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated) {
+ AWS_ASSERT(on_protocol_negotiated);
+
+ AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: Setting ALPN callback", (void *)bootstrap);
+ bootstrap->on_protocol_negotiated = on_protocol_negotiated;
+ return AWS_OP_SUCCESS;
+}
+
+struct client_channel_data {
+ struct aws_channel *channel;
+ struct aws_socket *socket;
+ struct aws_tls_connection_options tls_options;
+ aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated;
+ aws_tls_on_data_read_fn *user_on_data_read;
+ aws_tls_on_negotiation_result_fn *user_on_negotiation_result;
+ aws_tls_on_error_fn *user_on_error;
+ void *tls_user_data;
+ bool use_tls;
+};
+
+struct client_connection_args {
+ struct aws_client_bootstrap *bootstrap;
+ aws_client_bootstrap_on_channel_event_fn *creation_callback;
+ aws_client_bootstrap_on_channel_event_fn *setup_callback;
+ aws_client_bootstrap_on_channel_event_fn *shutdown_callback;
+ struct client_channel_data channel_data;
+ struct aws_socket_options outgoing_options;
+ uint16_t outgoing_port;
+ struct aws_string *host_name;
+ void *user_data;
+ uint8_t addresses_count;
+ uint8_t failed_count;
+ bool connection_chosen;
+ bool setup_called;
+ bool enable_read_back_pressure;
+
+ /*
+ * It is likely that all reference adjustments to the connection args take place in a single event loop
+ * thread and are thus thread-safe. I can imagine some complex future scenarios where that might not hold true
+ * and so it seems reasonable to switch now to a safe pattern.
+ *
+ */
+ struct aws_ref_count ref_count;
+};
+
+static struct client_connection_args *s_client_connection_args_acquire(struct client_connection_args *args) {
+ if (args != NULL) {
+ aws_ref_count_acquire(&args->ref_count);
+ }
+
+ return args;
+}
+
+static void s_client_connection_args_destroy(struct client_connection_args *args) {
+ AWS_ASSERT(args);
+
+ struct aws_allocator *allocator = args->bootstrap->allocator;
+ aws_client_bootstrap_release(args->bootstrap);
+ if (args->host_name) {
+ aws_string_destroy(args->host_name);
+ }
+
+ if (args->channel_data.use_tls) {
+ aws_tls_connection_options_clean_up(&args->channel_data.tls_options);
+ }
+
+ aws_mem_release(allocator, args);
+}
+
+static void s_client_connection_args_release(struct client_connection_args *args) {
+ if (args != NULL) {
+ aws_ref_count_release(&args->ref_count);
+ }
+}
+
+static void s_connection_args_setup_callback(
+ struct client_connection_args *args,
+ int error_code,
+ struct aws_channel *channel) {
+ /* setup_callback is always called exactly once */
+ AWS_ASSERT(!args->setup_called);
+ if (!args->setup_called) {
+ AWS_ASSERT((error_code == AWS_OP_SUCCESS) == (channel != NULL));
+ aws_client_bootstrap_on_channel_event_fn *setup_callback = args->setup_callback;
+ setup_callback(args->bootstrap, error_code, channel, args->user_data);
+ args->setup_called = true;
+ /* if setup_callback is called with an error, we will not call shutdown_callback */
+ if (error_code) {
+ args->shutdown_callback = NULL;
+ }
+ s_client_connection_args_release(args);
+ }
+}
+
+static void s_connection_args_creation_callback(struct client_connection_args *args, struct aws_channel *channel) {
+
+ AWS_FATAL_ASSERT(channel != NULL);
+
+ if (args->creation_callback) {
+ args->creation_callback(args->bootstrap, AWS_ERROR_SUCCESS, channel, args->user_data);
+ }
+}
+
+static void s_connection_args_shutdown_callback(
+ struct client_connection_args *args,
+ int error_code,
+ struct aws_channel *channel) {
+
+ if (!args->setup_called) {
+ /* if setup_callback was not called yet, an error occurred, ensure we tell the user *SOMETHING* */
+ error_code = (error_code) ? error_code : AWS_ERROR_UNKNOWN;
+ s_connection_args_setup_callback(args, error_code, NULL);
+ return;
+ }
+
+ aws_client_bootstrap_on_channel_event_fn *shutdown_callback = args->shutdown_callback;
+ if (shutdown_callback) {
+ shutdown_callback(args->bootstrap, error_code, channel, args->user_data);
+ }
+}
+
+static void s_tls_client_on_negotiation_result(
+ struct aws_channel_handler *handler,
+ struct aws_channel_slot *slot,
+ int err_code,
+ void *user_data) {
+ struct client_connection_args *connection_args = user_data;
+
+ if (connection_args->channel_data.user_on_negotiation_result) {
+ connection_args->channel_data.user_on_negotiation_result(
+ handler, slot, err_code, connection_args->channel_data.tls_user_data);
+ }
+
+ AWS_LOGF_DEBUG(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: tls negotiation result %d on channel %p",
+ (void *)connection_args->bootstrap,
+ err_code,
+ (void *)slot->channel);
+
+ /* if an error occurred, the user callback will be delivered in shutdown */
+ if (err_code) {
+ aws_channel_shutdown(slot->channel, err_code);
+ return;
+ }
+
+ struct aws_channel *channel = connection_args->channel_data.channel;
+ s_connection_args_setup_callback(connection_args, AWS_ERROR_SUCCESS, channel);
+}
+
+/* in the context of a channel bootstrap, we don't care about these, but since we're hooking into these APIs we have to
+ * provide a proxy for the user actually receiving their callbacks. */
+static void s_tls_client_on_data_read(
+ struct aws_channel_handler *handler,
+ struct aws_channel_slot *slot,
+ struct aws_byte_buf *buffer,
+ void *user_data) {
+ struct client_connection_args *connection_args = user_data;
+
+ if (connection_args->channel_data.user_on_data_read) {
+ connection_args->channel_data.user_on_data_read(
+ handler, slot, buffer, connection_args->channel_data.tls_user_data);
+ }
+}
+
+/* in the context of a channel bootstrap, we don't care about these, but since we're hooking into these APIs we have to
+ * provide a proxy for the user actually receiving their callbacks. */
+static void s_tls_client_on_error(
+ struct aws_channel_handler *handler,
+ struct aws_channel_slot *slot,
+ int err,
+ const char *message,
+ void *user_data) {
+ struct client_connection_args *connection_args = user_data;
+
+ if (connection_args->channel_data.user_on_error) {
+ connection_args->channel_data.user_on_error(
+ handler, slot, err, message, connection_args->channel_data.tls_user_data);
+ }
+}
+
+static inline int s_setup_client_tls(struct client_connection_args *connection_args, struct aws_channel *channel) {
+ struct aws_channel_slot *tls_slot = aws_channel_slot_new(channel);
+
+ /* as far as cleanup goes, since this stuff is being added to a channel, the caller will free this memory
+ when they clean up the channel. */
+ if (!tls_slot) {
+ return AWS_OP_ERR;
+ }
+
+ struct aws_channel_handler *tls_handler = aws_tls_client_handler_new(
+ connection_args->bootstrap->allocator, &connection_args->channel_data.tls_options, tls_slot);
+
+ if (!tls_handler) {
+ aws_mem_release(connection_args->bootstrap->allocator, (void *)tls_slot);
+ return AWS_OP_ERR;
+ }
+
+ aws_channel_slot_insert_end(channel, tls_slot);
+ AWS_LOGF_TRACE(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: Setting up client TLS on channel %p with handler %p on slot %p",
+ (void *)connection_args->bootstrap,
+ (void *)channel,
+ (void *)tls_handler,
+ (void *)tls_slot);
+
+ if (aws_channel_slot_set_handler(tls_slot, tls_handler) != AWS_OP_SUCCESS) {
+ return AWS_OP_ERR;
+ }
+
+ if (connection_args->channel_data.on_protocol_negotiated) {
+ struct aws_channel_slot *alpn_slot = aws_channel_slot_new(channel);
+
+ if (!alpn_slot) {
+ return AWS_OP_ERR;
+ }
+
+ struct aws_channel_handler *alpn_handler = aws_tls_alpn_handler_new(
+ connection_args->bootstrap->allocator,
+ connection_args->channel_data.on_protocol_negotiated,
+ connection_args->user_data);
+
+ if (!alpn_handler) {
+ aws_mem_release(connection_args->bootstrap->allocator, (void *)alpn_slot);
+ return AWS_OP_ERR;
+ }
+
+ AWS_LOGF_TRACE(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: Setting up ALPN handler on channel "
+ "%p with handler %p on slot %p",
+ (void *)connection_args->bootstrap,
+ (void *)channel,
+ (void *)alpn_handler,
+ (void *)alpn_slot);
+
+ aws_channel_slot_insert_right(tls_slot, alpn_slot);
+ if (aws_channel_slot_set_handler(alpn_slot, alpn_handler) != AWS_OP_SUCCESS) {
+ return AWS_OP_ERR;
+ }
+ }
+
+ if (aws_tls_client_handler_start_negotiation(tls_handler) != AWS_OP_SUCCESS) {
+ return AWS_OP_ERR;
+ }
+
+ return AWS_OP_SUCCESS;
+}
+
+static void s_on_client_channel_on_setup_completed(struct aws_channel *channel, int error_code, void *user_data) {
+ struct client_connection_args *connection_args = user_data;
+ int err_code = error_code;
+
+ if (!err_code) {
+ AWS_LOGF_DEBUG(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: channel %p setup succeeded: bootstrapping.",
+ (void *)connection_args->bootstrap,
+ (void *)channel);
+
+ struct aws_channel_slot *socket_slot = aws_channel_slot_new(channel);
+
+ if (!socket_slot) {
+ err_code = aws_last_error();
+ goto error;
+ }
+
+ struct aws_channel_handler *socket_channel_handler = aws_socket_handler_new(
+ connection_args->bootstrap->allocator,
+ connection_args->channel_data.socket,
+ socket_slot,
+ g_aws_channel_max_fragment_size);
+
+ if (!socket_channel_handler) {
+ err_code = aws_last_error();
+ aws_channel_slot_remove(socket_slot);
+ socket_slot = NULL;
+ goto error;
+ }
+
+ AWS_LOGF_TRACE(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: Setting up socket handler on channel "
+ "%p with handler %p on slot %p.",
+ (void *)connection_args->bootstrap,
+ (void *)channel,
+ (void *)socket_channel_handler,
+ (void *)socket_slot);
+
+ if (aws_channel_slot_set_handler(socket_slot, socket_channel_handler)) {
+ err_code = aws_last_error();
+ goto error;
+ }
+
+ if (connection_args->channel_data.use_tls) {
+ /* we don't want to notify the user that the channel is ready yet, since tls is still negotiating, wait
+ * for the negotiation callback and handle it then.*/
+ if (s_setup_client_tls(connection_args, channel)) {
+ err_code = aws_last_error();
+ goto error;
+ }
+ } else {
+ s_connection_args_setup_callback(connection_args, AWS_OP_SUCCESS, channel);
+ }
+
+ return;
+ }
+
+error:
+ AWS_LOGF_ERROR(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: channel %p setup failed with error %d.",
+ (void *)connection_args->bootstrap,
+ (void *)channel,
+ err_code);
+ aws_channel_shutdown(channel, err_code);
+ /* the channel shutdown callback will clean the channel up */
+}
+
+static void s_on_client_channel_on_shutdown(struct aws_channel *channel, int error_code, void *user_data) {
+ struct client_connection_args *connection_args = user_data;
+
+ AWS_LOGF_DEBUG(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: channel %p shutdown with error %d.",
+ (void *)connection_args->bootstrap,
+ (void *)channel,
+ error_code);
+
+ /* note it's not safe to reference the bootstrap after the callback. */
+ struct aws_allocator *allocator = connection_args->bootstrap->allocator;
+ s_connection_args_shutdown_callback(connection_args, error_code, channel);
+
+ aws_channel_destroy(channel);
+ aws_socket_clean_up(connection_args->channel_data.socket);
+ aws_mem_release(allocator, connection_args->channel_data.socket);
+ s_client_connection_args_release(connection_args);
+}
+
+static bool s_aws_socket_domain_uses_dns(enum aws_socket_domain domain) {
+ return domain == AWS_SOCKET_IPV4 || domain == AWS_SOCKET_IPV6;
+}
+
+static void s_on_client_connection_established(struct aws_socket *socket, int error_code, void *user_data) {
+ struct client_connection_args *connection_args = user_data;
+
+ AWS_LOGF_DEBUG(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: client connection on socket %p completed with error %d.",
+ (void *)connection_args->bootstrap,
+ (void *)socket,
+ error_code);
+
+ if (error_code) {
+ connection_args->failed_count++;
+ }
+
+ if (error_code || connection_args->connection_chosen) {
+ if (s_aws_socket_domain_uses_dns(connection_args->outgoing_options.domain) && error_code) {
+ struct aws_host_address host_address;
+ host_address.host = connection_args->host_name;
+ host_address.address =
+ aws_string_new_from_c_str(connection_args->bootstrap->allocator, socket->remote_endpoint.address);
+ host_address.record_type = connection_args->outgoing_options.domain == AWS_SOCKET_IPV6
+ ? AWS_ADDRESS_RECORD_TYPE_AAAA
+ : AWS_ADDRESS_RECORD_TYPE_A;
+
+ if (host_address.address) {
+ AWS_LOGF_DEBUG(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: recording bad address %s.",
+ (void *)connection_args->bootstrap,
+ socket->remote_endpoint.address);
+ aws_host_resolver_record_connection_failure(connection_args->bootstrap->host_resolver, &host_address);
+ aws_string_destroy((void *)host_address.address);
+ }
+ }
+
+ AWS_LOGF_TRACE(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: releasing socket %p either because we already have a "
+ "successful connection or because it errored out.",
+ (void *)connection_args->bootstrap,
+ (void *)socket);
+ aws_socket_close(socket);
+
+ aws_socket_clean_up(socket);
+ aws_mem_release(connection_args->bootstrap->allocator, socket);
+
+ /* if this is the last attempted connection and it failed, notify the user */
+ if (connection_args->failed_count == connection_args->addresses_count) {
+ AWS_LOGF_ERROR(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: Connection failed with error_code %d.",
+ (void *)connection_args->bootstrap,
+ error_code);
+ /* connection_args will be released after setup_callback */
+ s_connection_args_setup_callback(connection_args, error_code, NULL);
+ }
+
+ /* every connection task adds a ref, so every failure or cancel needs to dec one */
+ s_client_connection_args_release(connection_args);
+ return;
+ }
+
+ connection_args->connection_chosen = true;
+ connection_args->channel_data.socket = socket;
+
+ struct aws_channel_options args = {
+ .on_setup_completed = s_on_client_channel_on_setup_completed,
+ .setup_user_data = connection_args,
+ .shutdown_user_data = connection_args,
+ .on_shutdown_completed = s_on_client_channel_on_shutdown,
+ };
+
+ args.enable_read_back_pressure = connection_args->enable_read_back_pressure;
+ args.event_loop = aws_socket_get_event_loop(socket);
+
+ AWS_LOGF_TRACE(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: Successful connection, creating a new channel using socket %p.",
+ (void *)connection_args->bootstrap,
+ (void *)socket);
+
+ connection_args->channel_data.channel = aws_channel_new(connection_args->bootstrap->allocator, &args);
+
+ if (!connection_args->channel_data.channel) {
+ aws_socket_clean_up(socket);
+ aws_mem_release(connection_args->bootstrap->allocator, connection_args->channel_data.socket);
+ connection_args->failed_count++;
+
+ /* if this is the last attempted connection and it failed, notify the user */
+ if (connection_args->failed_count == connection_args->addresses_count) {
+ s_connection_args_setup_callback(connection_args, error_code, NULL);
+ }
+ } else {
+ s_connection_args_creation_callback(connection_args, connection_args->channel_data.channel);
+ }
+}
+
+struct connection_task_data {
+ struct aws_task task;
+ struct aws_socket_endpoint endpoint;
+ struct aws_socket_options options;
+ struct aws_host_address host_address;
+ struct client_connection_args *args;
+ struct aws_event_loop *connect_loop;
+};
+
+static void s_attempt_connection(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+ struct connection_task_data *task_data = arg;
+ struct aws_allocator *allocator = task_data->args->bootstrap->allocator;
+ int err_code = 0;
+
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto task_cancelled;
+ }
+
+ struct aws_socket *outgoing_socket = aws_mem_acquire(allocator, sizeof(struct aws_socket));
+ if (!outgoing_socket) {
+ goto socket_alloc_failed;
+ }
+
+ if (aws_socket_init(outgoing_socket, allocator, &task_data->options)) {
+ goto socket_init_failed;
+ }
+
+ if (aws_socket_connect(
+ outgoing_socket,
+ &task_data->endpoint,
+ task_data->connect_loop,
+ s_on_client_connection_established,
+ task_data->args)) {
+
+ goto socket_connect_failed;
+ }
+
+ goto cleanup_task;
+
+socket_connect_failed:
+ aws_host_resolver_record_connection_failure(task_data->args->bootstrap->host_resolver, &task_data->host_address);
+ aws_socket_clean_up(outgoing_socket);
+socket_init_failed:
+ aws_mem_release(allocator, outgoing_socket);
+socket_alloc_failed:
+ err_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: failed to create socket with error %d",
+ (void *)task_data->args->bootstrap,
+ err_code);
+task_cancelled:
+ task_data->args->failed_count++;
+ /* if this is the last attempted connection and it failed, notify the user */
+ if (task_data->args->failed_count == task_data->args->addresses_count) {
+ s_connection_args_setup_callback(task_data->args, err_code, NULL);
+ }
+ s_client_connection_args_release(task_data->args);
+
+cleanup_task:
+ aws_host_address_clean_up(&task_data->host_address);
+ aws_mem_release(allocator, task_data);
+}
+
+static void s_on_host_resolved(
+ struct aws_host_resolver *resolver,
+ const struct aws_string *host_name,
+ int err_code,
+ const struct aws_array_list *host_addresses,
+ void *user_data) {
+ (void)resolver;
+ (void)host_name;
+
+ struct client_connection_args *client_connection_args = user_data;
+ struct aws_allocator *allocator = client_connection_args->bootstrap->allocator;
+
+ if (err_code) {
+ AWS_LOGF_ERROR(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: dns resolution failed, or all socket connections to the endpoint failed.",
+ (void *)client_connection_args->bootstrap);
+ s_connection_args_setup_callback(client_connection_args, err_code, NULL);
+ return;
+ }
+
+ size_t host_addresses_len = aws_array_list_length(host_addresses);
+ AWS_FATAL_ASSERT(host_addresses_len > 0);
+ AWS_LOGF_TRACE(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: dns resolution completed. Kicking off connections"
+ " on %llu addresses. First one back wins.",
+ (void *)client_connection_args->bootstrap,
+ (unsigned long long)host_addresses_len);
+ /* use this event loop for all outgoing connection attempts (only one will ultimately win). */
+ struct aws_event_loop *connect_loop =
+ aws_event_loop_group_get_next_loop(client_connection_args->bootstrap->event_loop_group);
+ client_connection_args->addresses_count = (uint8_t)host_addresses_len;
+
+ /* allocate all the task data first, in case it fails... */
+ AWS_VARIABLE_LENGTH_ARRAY(struct connection_task_data *, tasks, host_addresses_len);
+ for (size_t i = 0; i < host_addresses_len; ++i) {
+ struct connection_task_data *task_data = tasks[i] =
+ aws_mem_calloc(allocator, 1, sizeof(struct connection_task_data));
+ bool failed = task_data == NULL;
+ if (!failed) {
+ struct aws_host_address *host_address_ptr = NULL;
+ aws_array_list_get_at_ptr(host_addresses, (void **)&host_address_ptr, i);
+
+ task_data->endpoint.port = client_connection_args->outgoing_port;
+ AWS_ASSERT(sizeof(task_data->endpoint.address) >= host_address_ptr->address->len + 1);
+ memcpy(
+ task_data->endpoint.address,
+ aws_string_bytes(host_address_ptr->address),
+ host_address_ptr->address->len);
+ task_data->endpoint.address[host_address_ptr->address->len] = 0;
+
+ task_data->options = client_connection_args->outgoing_options;
+ task_data->options.domain =
+ host_address_ptr->record_type == AWS_ADDRESS_RECORD_TYPE_AAAA ? AWS_SOCKET_IPV6 : AWS_SOCKET_IPV4;
+
+ failed = aws_host_address_copy(host_address_ptr, &task_data->host_address) != AWS_OP_SUCCESS;
+ task_data->args = client_connection_args;
+ task_data->connect_loop = connect_loop;
+ }
+
+ if (failed) {
+ for (size_t j = 0; j <= i; ++j) {
+ if (tasks[j]) {
+ aws_host_address_clean_up(&tasks[j]->host_address);
+ aws_mem_release(allocator, tasks[j]);
+ }
+ }
+ int alloc_err_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: failed to allocate connection task data: err=%d",
+ (void *)client_connection_args->bootstrap,
+ alloc_err_code);
+ s_connection_args_setup_callback(client_connection_args, alloc_err_code, NULL);
+ return;
+ }
+ }
+
+ /* ...then schedule all the tasks, which cannot fail */
+ for (size_t i = 0; i < host_addresses_len; ++i) {
+ struct connection_task_data *task_data = tasks[i];
+ /* each task needs to hold a ref to the args until completed */
+ s_client_connection_args_acquire(task_data->args);
+
+ aws_task_init(&task_data->task, s_attempt_connection, task_data, "attempt_connection");
+ aws_event_loop_schedule_task_now(connect_loop, &task_data->task);
+ }
+}
+
+int aws_client_bootstrap_new_socket_channel(struct aws_socket_channel_bootstrap_options *options) {
+
+ struct aws_client_bootstrap *bootstrap = options->bootstrap;
+ AWS_FATAL_ASSERT(options->setup_callback);
+ AWS_FATAL_ASSERT(options->shutdown_callback);
+ AWS_FATAL_ASSERT(bootstrap);
+
+ const struct aws_socket_options *socket_options = options->socket_options;
+ AWS_FATAL_ASSERT(socket_options != NULL);
+
+ const struct aws_tls_connection_options *tls_options = options->tls_options;
+
+ AWS_FATAL_ASSERT(tls_options == NULL || socket_options->type == AWS_SOCKET_STREAM);
+ aws_io_fatal_assert_library_initialized();
+
+ struct client_connection_args *client_connection_args =
+ aws_mem_calloc(bootstrap->allocator, 1, sizeof(struct client_connection_args));
+
+ if (!client_connection_args) {
+ return AWS_OP_ERR;
+ }
+
+ const char *host_name = options->host_name;
+ uint16_t port = options->port;
+
+ AWS_LOGF_TRACE(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: attempting to initialize a new client channel to %s:%d",
+ (void *)bootstrap,
+ host_name,
+ (int)port);
+
+ aws_ref_count_init(
+ &client_connection_args->ref_count,
+ client_connection_args,
+ (aws_simple_completion_callback *)s_client_connection_args_destroy);
+ client_connection_args->user_data = options->user_data;
+ client_connection_args->bootstrap = aws_client_bootstrap_acquire(bootstrap);
+ client_connection_args->creation_callback = options->creation_callback;
+ client_connection_args->setup_callback = options->setup_callback;
+ client_connection_args->shutdown_callback = options->shutdown_callback;
+ client_connection_args->outgoing_options = *socket_options;
+ client_connection_args->outgoing_port = port;
+ client_connection_args->enable_read_back_pressure = options->enable_read_back_pressure;
+
+ if (tls_options) {
+ if (aws_tls_connection_options_copy(&client_connection_args->channel_data.tls_options, tls_options)) {
+ goto error;
+ }
+ client_connection_args->channel_data.use_tls = true;
+
+ client_connection_args->channel_data.on_protocol_negotiated = bootstrap->on_protocol_negotiated;
+ client_connection_args->channel_data.tls_user_data = tls_options->user_data;
+
+ /* in order to honor any callbacks a user may have installed on their tls_connection_options,
+ * we need to wrap them if they were set.*/
+ if (bootstrap->on_protocol_negotiated) {
+ client_connection_args->channel_data.tls_options.advertise_alpn_message = true;
+ }
+
+ if (tls_options->on_data_read) {
+ client_connection_args->channel_data.user_on_data_read = tls_options->on_data_read;
+ client_connection_args->channel_data.tls_options.on_data_read = s_tls_client_on_data_read;
+ }
+
+ if (tls_options->on_error) {
+ client_connection_args->channel_data.user_on_error = tls_options->on_error;
+ client_connection_args->channel_data.tls_options.on_error = s_tls_client_on_error;
+ }
+
+ if (tls_options->on_negotiation_result) {
+ client_connection_args->channel_data.user_on_negotiation_result = tls_options->on_negotiation_result;
+ }
+
+ client_connection_args->channel_data.tls_options.on_negotiation_result = s_tls_client_on_negotiation_result;
+ client_connection_args->channel_data.tls_options.user_data = client_connection_args;
+ }
+
+ if (s_aws_socket_domain_uses_dns(socket_options->domain)) {
+ client_connection_args->host_name = aws_string_new_from_c_str(bootstrap->allocator, host_name);
+
+ if (!client_connection_args->host_name) {
+ goto error;
+ }
+
+ if (aws_host_resolver_resolve_host(
+ bootstrap->host_resolver,
+ client_connection_args->host_name,
+ s_on_host_resolved,
+ &bootstrap->host_resolver_config,
+ client_connection_args)) {
+ goto error;
+ }
+ } else {
+ /* ensure that the pipe/domain socket name will fit in the endpoint address */
+ const size_t host_name_len = strlen(host_name);
+ if (host_name_len >= AWS_ADDRESS_MAX_LEN) {
+ aws_raise_error(AWS_IO_SOCKET_INVALID_ADDRESS);
+ goto error;
+ }
+
+ struct aws_socket_endpoint endpoint;
+ AWS_ZERO_STRUCT(endpoint);
+ memcpy(endpoint.address, host_name, host_name_len);
+ if (socket_options->domain == AWS_SOCKET_VSOCK) {
+ endpoint.port = port;
+ } else {
+ endpoint.port = 0;
+ }
+
+ struct aws_socket *outgoing_socket = aws_mem_acquire(bootstrap->allocator, sizeof(struct aws_socket));
+
+ if (!outgoing_socket) {
+ goto error;
+ }
+
+ if (aws_socket_init(outgoing_socket, bootstrap->allocator, socket_options)) {
+ aws_mem_release(bootstrap->allocator, outgoing_socket);
+ goto error;
+ }
+
+ client_connection_args->addresses_count = 1;
+
+ struct aws_event_loop *connect_loop = aws_event_loop_group_get_next_loop(bootstrap->event_loop_group);
+
+ s_client_connection_args_acquire(client_connection_args);
+ if (aws_socket_connect(
+ outgoing_socket, &endpoint, connect_loop, s_on_client_connection_established, client_connection_args)) {
+ aws_socket_clean_up(outgoing_socket);
+ aws_mem_release(client_connection_args->bootstrap->allocator, outgoing_socket);
+ s_client_connection_args_release(client_connection_args);
+ goto error;
+ }
+ }
+
+ return AWS_OP_SUCCESS;
+
+error:
+ if (client_connection_args) {
+ /* tls opt will also be freed when we clean up the connection arg */
+ s_client_connection_args_release(client_connection_args);
+ }
+ return AWS_OP_ERR;
+}
+
+void s_server_bootstrap_destroy_impl(struct aws_server_bootstrap *bootstrap) {
+ AWS_ASSERT(bootstrap);
+ aws_event_loop_group_release(bootstrap->event_loop_group);
+ aws_mem_release(bootstrap->allocator, bootstrap);
+}
+
+struct aws_server_bootstrap *aws_server_bootstrap_acquire(struct aws_server_bootstrap *bootstrap) {
+ if (bootstrap != NULL) {
+ aws_ref_count_acquire(&bootstrap->ref_count);
+ }
+
+ return bootstrap;
+}
+
+void aws_server_bootstrap_release(struct aws_server_bootstrap *bootstrap) {
+ /* if destroy is being called, the user intends to not use the bootstrap anymore
+ * so we clean up the thread local state while the event loop thread is
+ * still alive */
+ AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: releasing server bootstrap reference", (void *)bootstrap);
+ if (bootstrap != NULL) {
+ aws_ref_count_release(&bootstrap->ref_count);
+ }
+}
+
+struct aws_server_bootstrap *aws_server_bootstrap_new(
+ struct aws_allocator *allocator,
+ struct aws_event_loop_group *el_group) {
+ AWS_ASSERT(allocator);
+ AWS_ASSERT(el_group);
+
+ struct aws_server_bootstrap *bootstrap = aws_mem_calloc(allocator, 1, sizeof(struct aws_server_bootstrap));
+ if (!bootstrap) {
+ return NULL;
+ }
+
+ AWS_LOGF_INFO(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: Initializing server bootstrap with event-loop group %p",
+ (void *)bootstrap,
+ (void *)el_group);
+
+ bootstrap->allocator = allocator;
+ bootstrap->event_loop_group = aws_event_loop_group_acquire(el_group);
+ bootstrap->on_protocol_negotiated = NULL;
+ aws_ref_count_init(
+ &bootstrap->ref_count, bootstrap, (aws_simple_completion_callback *)s_server_bootstrap_destroy_impl);
+
+ return bootstrap;
+}
+
+struct server_connection_args {
+ struct aws_server_bootstrap *bootstrap;
+ struct aws_socket listener;
+ aws_server_bootstrap_on_accept_channel_setup_fn *incoming_callback;
+ aws_server_bootstrap_on_accept_channel_shutdown_fn *shutdown_callback;
+ aws_server_bootstrap_on_server_listener_destroy_fn *destroy_callback;
+ struct aws_tls_connection_options tls_options;
+ aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated;
+ aws_tls_on_data_read_fn *user_on_data_read;
+ aws_tls_on_negotiation_result_fn *user_on_negotiation_result;
+ aws_tls_on_error_fn *user_on_error;
+ struct aws_task listener_destroy_task;
+ void *tls_user_data;
+ void *user_data;
+ bool use_tls;
+ bool enable_read_back_pressure;
+ struct aws_ref_count ref_count;
+};
+
+struct server_channel_data {
+ struct aws_channel *channel;
+ struct aws_socket *socket;
+ struct server_connection_args *server_connection_args;
+ bool incoming_called;
+};
+
+static struct server_connection_args *s_server_connection_args_acquire(struct server_connection_args *args) {
+ if (args != NULL) {
+ aws_ref_count_acquire(&args->ref_count);
+ }
+
+ return args;
+}
+
+static void s_server_connection_args_destroy(struct server_connection_args *args) {
+ if (args == NULL) {
+ return;
+ }
+
+ /* fire the destroy callback */
+ if (args->destroy_callback) {
+ args->destroy_callback(args->bootstrap, args->user_data);
+ }
+
+ struct aws_allocator *allocator = args->bootstrap->allocator;
+ aws_server_bootstrap_release(args->bootstrap);
+ if (args->use_tls) {
+ aws_tls_connection_options_clean_up(&args->tls_options);
+ }
+
+ aws_mem_release(allocator, args);
+}
+
+static void s_server_connection_args_release(struct server_connection_args *args) {
+ if (args != NULL) {
+ aws_ref_count_release(&args->ref_count);
+ }
+}
+
+static void s_server_incoming_callback(
+ struct server_channel_data *channel_data,
+ int error_code,
+ struct aws_channel *channel) {
+ /* incoming_callback is always called exactly once for each channel */
+ AWS_ASSERT(!channel_data->incoming_called);
+ struct server_connection_args *args = channel_data->server_connection_args;
+ args->incoming_callback(args->bootstrap, error_code, channel, args->user_data);
+ channel_data->incoming_called = true;
+}
+
+static void s_tls_server_on_negotiation_result(
+ struct aws_channel_handler *handler,
+ struct aws_channel_slot *slot,
+ int err_code,
+ void *user_data) {
+ struct server_channel_data *channel_data = user_data;
+ struct server_connection_args *connection_args = channel_data->server_connection_args;
+
+ if (connection_args->user_on_negotiation_result) {
+ connection_args->user_on_negotiation_result(handler, slot, err_code, connection_args->tls_user_data);
+ }
+
+ AWS_LOGF_DEBUG(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: tls negotiation result %d on channel %p",
+ (void *)connection_args->bootstrap,
+ err_code,
+ (void *)slot->channel);
+
+ struct aws_channel *channel = slot->channel;
+ if (err_code) {
+ /* shut down the channel */
+ aws_channel_shutdown(channel, err_code);
+ } else {
+ s_server_incoming_callback(channel_data, err_code, channel);
+ }
+}
+
+/* in the context of a channel bootstrap, we don't care about these, but since we're hooking into these APIs we have to
+ * provide a proxy for the user actually receiving their callbacks. */
+static void s_tls_server_on_data_read(
+ struct aws_channel_handler *handler,
+ struct aws_channel_slot *slot,
+ struct aws_byte_buf *buffer,
+ void *user_data) {
+ struct server_connection_args *connection_args = user_data;
+
+ if (connection_args->user_on_data_read) {
+ connection_args->user_on_data_read(handler, slot, buffer, connection_args->tls_user_data);
+ }
+}
+
+/* in the context of a channel bootstrap, we don't care about these, but since we're hooking into these APIs we have to
+ * provide a proxy for the user actually receiving their callbacks. */
+static void s_tls_server_on_error(
+ struct aws_channel_handler *handler,
+ struct aws_channel_slot *slot,
+ int err,
+ const char *message,
+ void *user_data) {
+ struct server_connection_args *connection_args = user_data;
+
+ if (connection_args->user_on_error) {
+ connection_args->user_on_error(handler, slot, err, message, connection_args->tls_user_data);
+ }
+}
+
+static inline int s_setup_server_tls(struct server_channel_data *channel_data, struct aws_channel *channel) {
+ struct aws_channel_slot *tls_slot = NULL;
+ struct aws_channel_handler *tls_handler = NULL;
+ struct server_connection_args *connection_args = channel_data->server_connection_args;
+
+ /* as far as cleanup goes here, since we're adding things to a channel, if a slot is ever successfully
+ added to the channel, we leave it there. The caller will clean up the channel and it will clean this memory
+ up as well. */
+ tls_slot = aws_channel_slot_new(channel);
+
+ if (!tls_slot) {
+ return AWS_OP_ERR;
+ }
+
+ /* Shallow-copy tls_options so we can override the user_data, making it specific to this channel */
+ struct aws_tls_connection_options tls_options = connection_args->tls_options;
+ tls_options.user_data = channel_data;
+ tls_handler = aws_tls_server_handler_new(connection_args->bootstrap->allocator, &tls_options, tls_slot);
+
+ if (!tls_handler) {
+ aws_mem_release(connection_args->bootstrap->allocator, tls_slot);
+ return AWS_OP_ERR;
+ }
+
+ AWS_LOGF_TRACE(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: Setting up server TLS on channel %p with handler %p on slot %p",
+ (void *)connection_args->bootstrap,
+ (void *)channel,
+ (void *)tls_handler,
+ (void *)tls_slot);
+
+ aws_channel_slot_insert_end(channel, tls_slot);
+
+ if (aws_channel_slot_set_handler(tls_slot, tls_handler)) {
+ return AWS_OP_ERR;
+ }
+
+ if (connection_args->on_protocol_negotiated) {
+ struct aws_channel_slot *alpn_slot = NULL;
+ struct aws_channel_handler *alpn_handler = NULL;
+ alpn_slot = aws_channel_slot_new(channel);
+
+ if (!alpn_slot) {
+ return AWS_OP_ERR;
+ }
+
+ alpn_handler = aws_tls_alpn_handler_new(
+ connection_args->bootstrap->allocator, connection_args->on_protocol_negotiated, connection_args->user_data);
+
+ if (!alpn_handler) {
+ aws_channel_slot_remove(alpn_slot);
+ return AWS_OP_ERR;
+ }
+
+ AWS_LOGF_TRACE(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: Setting up ALPN handler on channel "
+ "%p with handler %p on slot %p",
+ (void *)connection_args->bootstrap,
+ (void *)channel,
+ (void *)alpn_handler,
+ (void *)alpn_slot);
+
+ aws_channel_slot_insert_right(tls_slot, alpn_slot);
+
+ if (aws_channel_slot_set_handler(alpn_slot, alpn_handler)) {
+ return AWS_OP_ERR;
+ }
+ }
+
+ return AWS_OP_SUCCESS;
+}
+
+static void s_on_server_channel_on_setup_completed(struct aws_channel *channel, int error_code, void *user_data) {
+ struct server_channel_data *channel_data = user_data;
+
+ int err_code = error_code;
+ if (err_code) {
+ /* channel fail to set up no destroy callback will fire */
+ AWS_LOGF_ERROR(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: channel %p setup failed with error %d.",
+ (void *)channel_data->server_connection_args->bootstrap,
+ (void *)channel,
+ err_code);
+
+ aws_channel_destroy(channel);
+ struct aws_allocator *allocator = channel_data->socket->allocator;
+ aws_socket_clean_up(channel_data->socket);
+ aws_mem_release(allocator, (void *)channel_data->socket);
+ s_server_incoming_callback(channel_data, err_code, NULL);
+ aws_mem_release(channel_data->server_connection_args->bootstrap->allocator, channel_data);
+ /* no shutdown call back will be fired, we release the ref_count of connection arg here */
+ s_server_connection_args_release(channel_data->server_connection_args);
+ return;
+ }
+
+ AWS_LOGF_DEBUG(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: channel %p setup succeeded: bootstrapping.",
+ (void *)channel_data->server_connection_args->bootstrap,
+ (void *)channel);
+
+ struct aws_channel_slot *socket_slot = aws_channel_slot_new(channel);
+
+ if (!socket_slot) {
+ err_code = aws_last_error();
+ goto error;
+ }
+
+ struct aws_channel_handler *socket_channel_handler = aws_socket_handler_new(
+ channel_data->server_connection_args->bootstrap->allocator,
+ channel_data->socket,
+ socket_slot,
+ g_aws_channel_max_fragment_size);
+
+ if (!socket_channel_handler) {
+ err_code = aws_last_error();
+ aws_channel_slot_remove(socket_slot);
+ socket_slot = NULL;
+ goto error;
+ }
+
+ AWS_LOGF_TRACE(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: Setting up socket handler on channel "
+ "%p with handler %p on slot %p.",
+ (void *)channel_data->server_connection_args->bootstrap,
+ (void *)channel,
+ (void *)socket_channel_handler,
+ (void *)socket_slot);
+
+ if (aws_channel_slot_set_handler(socket_slot, socket_channel_handler)) {
+ err_code = aws_last_error();
+ goto error;
+ }
+
+ if (channel_data->server_connection_args->use_tls) {
+ /* incoming callback will be invoked upon the negotiation completion so don't do it
+ * here. */
+ if (s_setup_server_tls(channel_data, channel)) {
+ err_code = aws_last_error();
+ goto error;
+ }
+ } else {
+ s_server_incoming_callback(channel_data, AWS_OP_SUCCESS, channel);
+ }
+ return;
+
+error:
+ /* shut down the channel */
+ aws_channel_shutdown(channel, err_code);
+}
+
+static void s_on_server_channel_on_shutdown(struct aws_channel *channel, int error_code, void *user_data) {
+ struct server_channel_data *channel_data = user_data;
+ struct server_connection_args *args = channel_data->server_connection_args;
+ AWS_LOGF_DEBUG(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: channel %p shutdown with error %d.",
+ (void *)args->bootstrap,
+ (void *)channel,
+ error_code);
+
+ void *server_shutdown_user_data = args->user_data;
+ struct aws_server_bootstrap *server_bootstrap = args->bootstrap;
+ struct aws_allocator *allocator = server_bootstrap->allocator;
+
+ if (!channel_data->incoming_called) {
+ error_code = (error_code) ? error_code : AWS_ERROR_UNKNOWN;
+ s_server_incoming_callback(channel_data, error_code, NULL);
+ } else {
+ args->shutdown_callback(server_bootstrap, error_code, channel, server_shutdown_user_data);
+ }
+
+ aws_channel_destroy(channel);
+ aws_socket_clean_up(channel_data->socket);
+ aws_mem_release(allocator, channel_data->socket);
+ s_server_connection_args_release(channel_data->server_connection_args);
+
+ aws_mem_release(allocator, channel_data);
+}
+
+void s_on_server_connection_result(
+ struct aws_socket *socket,
+ int error_code,
+ struct aws_socket *new_socket,
+ void *user_data) {
+ (void)socket;
+ struct server_connection_args *connection_args = user_data;
+
+ s_server_connection_args_acquire(connection_args);
+ AWS_LOGF_DEBUG(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: server connection on socket %p completed with error %d.",
+ (void *)connection_args->bootstrap,
+ (void *)socket,
+ error_code);
+
+ if (!error_code) {
+ AWS_LOGF_TRACE(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: creating a new channel for incoming "
+ "connection using socket %p.",
+ (void *)connection_args->bootstrap,
+ (void *)socket);
+ struct server_channel_data *channel_data =
+ aws_mem_calloc(connection_args->bootstrap->allocator, 1, sizeof(struct server_channel_data));
+ if (!channel_data) {
+ goto error_cleanup;
+ }
+ channel_data->incoming_called = false;
+ channel_data->socket = new_socket;
+ channel_data->server_connection_args = connection_args;
+
+ struct aws_event_loop *event_loop =
+ aws_event_loop_group_get_next_loop(connection_args->bootstrap->event_loop_group);
+
+ struct aws_channel_options channel_args = {
+ .on_setup_completed = s_on_server_channel_on_setup_completed,
+ .setup_user_data = channel_data,
+ .shutdown_user_data = channel_data,
+ .on_shutdown_completed = s_on_server_channel_on_shutdown,
+ };
+
+ channel_args.event_loop = event_loop;
+ channel_args.enable_read_back_pressure = channel_data->server_connection_args->enable_read_back_pressure;
+
+ if (aws_socket_assign_to_event_loop(new_socket, event_loop)) {
+ aws_mem_release(connection_args->bootstrap->allocator, (void *)channel_data);
+ goto error_cleanup;
+ }
+
+ channel_data->channel = aws_channel_new(connection_args->bootstrap->allocator, &channel_args);
+
+ if (!channel_data->channel) {
+ aws_mem_release(connection_args->bootstrap->allocator, (void *)channel_data);
+ goto error_cleanup;
+ }
+ } else {
+ /* no channel is created */
+ connection_args->incoming_callback(connection_args->bootstrap, error_code, NULL, connection_args->user_data);
+ s_server_connection_args_release(connection_args);
+ }
+
+ return;
+
+error_cleanup:
+ /* no channel is created */
+ connection_args->incoming_callback(connection_args->bootstrap, aws_last_error(), NULL, connection_args->user_data);
+
+ struct aws_allocator *allocator = new_socket->allocator;
+ aws_socket_clean_up(new_socket);
+ aws_mem_release(allocator, (void *)new_socket);
+ s_server_connection_args_release(connection_args);
+}
+
+static void s_listener_destroy_task(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)status;
+ (void)task;
+ struct server_connection_args *server_connection_args = arg;
+
+ aws_socket_stop_accept(&server_connection_args->listener);
+ aws_socket_clean_up(&server_connection_args->listener);
+ s_server_connection_args_release(server_connection_args);
+}
+
+struct aws_socket *aws_server_bootstrap_new_socket_listener(
+ const struct aws_server_socket_channel_bootstrap_options *bootstrap_options) {
+ AWS_PRECONDITION(bootstrap_options);
+ AWS_PRECONDITION(bootstrap_options->bootstrap);
+ AWS_PRECONDITION(bootstrap_options->incoming_callback)
+ AWS_PRECONDITION(bootstrap_options->shutdown_callback)
+
+ struct server_connection_args *server_connection_args =
+ aws_mem_calloc(bootstrap_options->bootstrap->allocator, 1, sizeof(struct server_connection_args));
+ if (!server_connection_args) {
+ return NULL;
+ }
+
+ AWS_LOGF_INFO(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP,
+ "id=%p: attempting to initialize a new "
+ "server socket listener for %s:%d",
+ (void *)server_connection_args->bootstrap,
+ bootstrap_options->host_name,
+ (int)bootstrap_options->port);
+
+ aws_ref_count_init(
+ &server_connection_args->ref_count,
+ server_connection_args,
+ (aws_simple_completion_callback *)s_server_connection_args_destroy);
+ server_connection_args->user_data = bootstrap_options->user_data;
+ server_connection_args->bootstrap = aws_server_bootstrap_acquire(bootstrap_options->bootstrap);
+ server_connection_args->shutdown_callback = bootstrap_options->shutdown_callback;
+ server_connection_args->incoming_callback = bootstrap_options->incoming_callback;
+ server_connection_args->destroy_callback = bootstrap_options->destroy_callback;
+ server_connection_args->on_protocol_negotiated = bootstrap_options->bootstrap->on_protocol_negotiated;
+ server_connection_args->enable_read_back_pressure = bootstrap_options->enable_read_back_pressure;
+
+ aws_task_init(
+ &server_connection_args->listener_destroy_task,
+ s_listener_destroy_task,
+ server_connection_args,
+ "listener socket destroy");
+
+ if (bootstrap_options->tls_options) {
+ AWS_LOGF_INFO(
+ AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: using tls on listener", (void *)bootstrap_options->tls_options);
+ if (aws_tls_connection_options_copy(&server_connection_args->tls_options, bootstrap_options->tls_options)) {
+ goto cleanup_server_connection_args;
+ }
+
+ server_connection_args->use_tls = true;
+
+ server_connection_args->tls_user_data = bootstrap_options->tls_options->user_data;
+
+ /* in order to honor any callbacks a user may have installed on their tls_connection_options,
+ * we need to wrap them if they were set.*/
+ if (bootstrap_options->bootstrap->on_protocol_negotiated) {
+ server_connection_args->tls_options.advertise_alpn_message = true;
+ }
+
+ if (bootstrap_options->tls_options->on_data_read) {
+ server_connection_args->user_on_data_read = bootstrap_options->tls_options->on_data_read;
+ server_connection_args->tls_options.on_data_read = s_tls_server_on_data_read;
+ }
+
+ if (bootstrap_options->tls_options->on_error) {
+ server_connection_args->user_on_error = bootstrap_options->tls_options->on_error;
+ server_connection_args->tls_options.on_error = s_tls_server_on_error;
+ }
+
+ if (bootstrap_options->tls_options->on_negotiation_result) {
+ server_connection_args->user_on_negotiation_result = bootstrap_options->tls_options->on_negotiation_result;
+ }
+
+ server_connection_args->tls_options.on_negotiation_result = s_tls_server_on_negotiation_result;
+ server_connection_args->tls_options.user_data = server_connection_args;
+ }
+
+ struct aws_event_loop *connection_loop =
+ aws_event_loop_group_get_next_loop(bootstrap_options->bootstrap->event_loop_group);
+
+ if (aws_socket_init(
+ &server_connection_args->listener,
+ bootstrap_options->bootstrap->allocator,
+ bootstrap_options->socket_options)) {
+ goto cleanup_server_connection_args;
+ }
+
+ struct aws_socket_endpoint endpoint;
+ AWS_ZERO_STRUCT(endpoint);
+ size_t host_name_len = 0;
+ if (aws_secure_strlen(bootstrap_options->host_name, sizeof(endpoint.address), &host_name_len)) {
+ goto cleanup_server_connection_args;
+ }
+
+ memcpy(endpoint.address, bootstrap_options->host_name, host_name_len);
+ endpoint.port = bootstrap_options->port;
+
+ if (aws_socket_bind(&server_connection_args->listener, &endpoint)) {
+ goto cleanup_listener;
+ }
+
+ if (aws_socket_listen(&server_connection_args->listener, 1024)) {
+ goto cleanup_listener;
+ }
+
+ if (aws_socket_start_accept(
+ &server_connection_args->listener,
+ connection_loop,
+ s_on_server_connection_result,
+ server_connection_args)) {
+ goto cleanup_listener;
+ }
+
+ return &server_connection_args->listener;
+
+cleanup_listener:
+ aws_socket_clean_up(&server_connection_args->listener);
+
+cleanup_server_connection_args:
+ s_server_connection_args_release(server_connection_args);
+
+ return NULL;
+}
+
+void aws_server_bootstrap_destroy_socket_listener(struct aws_server_bootstrap *bootstrap, struct aws_socket *listener) {
+ struct server_connection_args *server_connection_args =
+ AWS_CONTAINER_OF(listener, struct server_connection_args, listener);
+
+ AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: releasing bootstrap reference", (void *)bootstrap);
+ aws_event_loop_schedule_task_now(listener->event_loop, &server_connection_args->listener_destroy_task);
+}
+
+int aws_server_bootstrap_set_alpn_callback(
+ struct aws_server_bootstrap *bootstrap,
+ aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated) {
+ AWS_ASSERT(on_protocol_negotiated);
+ AWS_LOGF_DEBUG(AWS_LS_IO_CHANNEL_BOOTSTRAP, "id=%p: Setting ALPN callback", (void *)bootstrap);
+ bootstrap->on_protocol_negotiated = on_protocol_negotiated;
+ return AWS_OP_SUCCESS;
+}