Lesson 35 - Get Compute Auth Token Working

This commit is contained in:
Norman Lansing
2026-02-28 12:32:28 -05:00
parent 1d477ee42a
commit 4fde462bce
7743 changed files with 1397833 additions and 18 deletions

View File

@@ -0,0 +1,117 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#ifndef AWS_IO_ASYNC_STREAM_H
#define AWS_IO_ASYNC_STREAM_H
/**
* THIS IS AN EXPERIMENTAL AND UNSTABLE API
* TODO: logging
* TODO: modify API to return byte-bufs, instead of filling in the provided byte-buf?
* this would avoid a copy in the use-cases we know of, but it's more complex
* TODO: vtable acquire()/release()?
* TODO: protect against simultaneous reads?
* TODO: check results of vtable->read() (i.e. 0 byte reads not allowed)?
* this would require 1 or 2 additional allocations per read
*/
#include <aws/io/io.h>
#include <aws/common/ref_count.h>
AWS_PUSH_SANE_WARNING_LEVEL
struct aws_async_input_stream;
struct aws_byte_buf;
struct aws_future_bool;
struct aws_input_stream;
struct aws_async_input_stream {
const struct aws_async_input_stream_vtable *vtable;
struct aws_allocator *alloc;
struct aws_ref_count ref_count;
void *impl;
};
struct aws_async_input_stream_vtable {
/**
* Destroy the stream, its refcount has reached 0.
*/
void (*destroy)(struct aws_async_input_stream *stream);
/**
* Read once into the buffer.
* Complete the read when at least 1 byte is read, the buffer is full, or EOF is reached.
* Do not resize the buffer (do not use "aws_byte_buf_xyz_dynamic()" functions)
* Do not assume that buffer len starts at 0.
* You may assume that read() won't be called again until the current one completes.
* You may assume that the buffer has some space available.
* Return a future, which will contain an error code if something went wrong,
* or a result bool indicating whether EOF has been reached.
*/
struct aws_future_bool *(*read)(struct aws_async_input_stream *stream, struct aws_byte_buf *dest);
};
AWS_EXTERN_C_BEGIN
/**
* Initialize aws_async_input_stream "base class"
*/
AWS_IO_API
void aws_async_input_stream_init_base(
struct aws_async_input_stream *stream,
struct aws_allocator *alloc,
const struct aws_async_input_stream_vtable *vtable,
void *impl);
/**
* Increment reference count.
* You may pass in NULL (has no effect).
* Returns whatever pointer was passed in.
*/
AWS_IO_API
struct aws_async_input_stream *aws_async_input_stream_acquire(struct aws_async_input_stream *stream);
/**
* Decrement reference count.
* You may pass in NULL (has no effect).
* Always returns NULL.
*/
AWS_IO_API
struct aws_async_input_stream *aws_async_input_stream_release(struct aws_async_input_stream *stream);
/**
* Read once from the async stream into the buffer.
* The read completes when at least 1 byte is read, the buffer is full, or EOF is reached.
* Depending on implementation, the read could complete at any time.
* It may complete synchronously. It may complete on another thread.
* Returns a future, which will contain an error code if something went wrong,
* or a result bool indicating whether EOF has been reached.
*
* WARNING: The buffer must have space available.
* WARNING: Do not read again until the previous read is complete.
*/
AWS_IO_API
struct aws_future_bool *aws_async_input_stream_read(struct aws_async_input_stream *stream, struct aws_byte_buf *dest);
/**
* Read repeatedly from the async stream until the buffer is full, or EOF is reached.
* Depending on implementation, this could complete at any time.
* It may complete synchronously. It may complete on another thread.
* Returns a future, which will contain an error code if something went wrong,
* or a result bool indicating whether EOF has been reached.
*
* WARNING: The buffer must have space available.
* WARNING: Do not read again until the previous read is complete.
*/
AWS_IO_API
struct aws_future_bool *aws_async_input_stream_read_to_fill(
struct aws_async_input_stream *stream,
struct aws_byte_buf *dest);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_ASYNC_STREAM_H */

View File

@@ -0,0 +1,515 @@
#ifndef AWS_IO_CHANNEL_H
#define AWS_IO_CHANNEL_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/io/io.h>
#include <aws/common/statistics.h>
#include <aws/common/task_scheduler.h>
AWS_PUSH_SANE_WARNING_LEVEL
enum aws_channel_direction {
AWS_CHANNEL_DIR_READ,
AWS_CHANNEL_DIR_WRITE,
};
struct aws_channel;
struct aws_channel_slot;
struct aws_channel_handler;
struct aws_event_loop;
struct aws_event_loop_local_object;
typedef void(aws_channel_on_setup_completed_fn)(struct aws_channel *channel, int error_code, void *user_data);
/* Callback called when a channel is completely shutdown. error_code refers to the reason the channel was closed. */
typedef void(aws_channel_on_shutdown_completed_fn)(struct aws_channel *channel, int error_code, void *user_data);
struct aws_channel_slot {
struct aws_allocator *alloc;
struct aws_channel *channel;
struct aws_channel_slot *adj_left;
struct aws_channel_slot *adj_right;
struct aws_channel_handler *handler;
size_t window_size;
size_t upstream_message_overhead;
size_t current_window_update_batch_size;
};
struct aws_channel_task;
typedef void(aws_channel_task_fn)(struct aws_channel_task *channel_task, void *arg, enum aws_task_status status);
struct aws_channel_task {
struct aws_task wrapper_task;
aws_channel_task_fn *task_fn;
void *arg;
const char *type_tag;
struct aws_linked_list_node node;
};
struct aws_channel_handler_vtable {
/**
* Called by the channel when a message is available for processing in the read direction. It is your
* responsibility to call aws_mem_release(message->allocator, message); on message when you are finished with it.
* You must only call `aws_mem_release(message->allocator, message);` if the `process_read_message`
* returns AWS_OP_SUCCESS. In case of an error, you must not clean up the message and should just raise the error.
*
* Also keep in mind that your slot's internal window has been decremented. You'll want to call
* aws_channel_slot_increment_read_window() at some point in the future if you want to keep receiving data.
*/
int (*process_read_message)(
struct aws_channel_handler *handler,
struct aws_channel_slot *slot,
struct aws_io_message *message);
/**
* Called by the channel when a message is available for processing in the write direction. It is your
* responsibility to call aws_mem_release(message->allocator, message); on message when you are finished with it.
* You must only call `aws_mem_release(message->allocator, message);` if the `process_read_message`
* returns AWS_OP_SUCCESS. In case of an error, you must not clean up the message and should just raise the error.
*/
int (*process_write_message)(
struct aws_channel_handler *handler,
struct aws_channel_slot *slot,
struct aws_io_message *message);
/**
* Called by the channel when a downstream handler has issued a window increment. You'll want to update your
* internal state and likely propagate a window increment message of your own by calling
* 'aws_channel_slot_increment_read_window()'
*/
int (*increment_read_window)(struct aws_channel_handler *handler, struct aws_channel_slot *slot, size_t size);
/**
* The channel calls shutdown on all handlers twice, once to shut down reading, and once to shut down writing.
* Shutdown always begins with the left-most handler, and proceeds to the right with dir set to
* AWS_CHANNEL_DIR_READ. Then shutdown is called on handlers from right to left with dir set to
* AWS_CHANNEL_DIR_WRITE.
*
* The shutdown process does not need to complete immediately and may rely on scheduled tasks.
* The handler must call aws_channel_slot_on_handler_shutdown_complete() when it is finished,
* which propagates shutdown to the next handler. If 'free_scarce_resources_immediately' is true,
* then resources vulnerable to denial-of-service attacks (such as sockets and file handles)
* must be closed immediately before the shutdown() call returns.
*/
int (*shutdown)(
struct aws_channel_handler *handler,
struct aws_channel_slot *slot,
enum aws_channel_direction dir,
int error_code,
bool free_scarce_resources_immediately);
/**
* Called by the channel when the handler is added to a slot, to get the initial window size.
*/
size_t (*initial_window_size)(struct aws_channel_handler *handler);
/** Called by the channel anytime a handler is added or removed, provides a hint for downstream
* handlers to avoid message fragmentation due to message overhead. */
size_t (*message_overhead)(struct aws_channel_handler *handler);
/**
* Clean up any resources and deallocate yourself. The shutdown process will already be completed before this
* function is called.
*/
void (*destroy)(struct aws_channel_handler *handler);
/**
* Directs the channel handler to reset all of the internal statistics it tracks about itself.
*/
void (*reset_statistics)(struct aws_channel_handler *handler);
/**
* Adds a pointer to the handler's internal statistics (if they exist) to a list of statistics structures
* associated with the channel's handler chain.
*/
void (*gather_statistics)(struct aws_channel_handler *handler, struct aws_array_list *stats_list);
/*
* If this handler represents a source of data (like the socket_handler), then this will trigger a read
* from the data source.
*/
void (*trigger_read)(struct aws_channel_handler *handler);
};
struct aws_channel_handler {
struct aws_channel_handler_vtable *vtable;
struct aws_allocator *alloc;
struct aws_channel_slot *slot;
void *impl;
};
/**
* Args for creating a new channel.
* event_loop to use for IO and tasks. on_setup_completed will be invoked when
* the setup process is finished It will be executed in the event loop's thread.
* on_shutdown_completed will be executed upon channel shutdown.
*
* enable_read_back_pressure toggles whether or not back pressure will be applied in the channel.
* Leave this option off unless you're using something like reactive-streams, since it is a slight throughput
* penalty.
*
* Unless otherwise
* specified all functions for channels and channel slots must be executed within that channel's event-loop's thread.
**/
struct aws_channel_options {
struct aws_event_loop *event_loop;
aws_channel_on_setup_completed_fn *on_setup_completed;
aws_channel_on_shutdown_completed_fn *on_shutdown_completed;
void *setup_user_data;
void *shutdown_user_data;
bool enable_read_back_pressure;
};
AWS_EXTERN_C_BEGIN
extern AWS_IO_API size_t g_aws_channel_max_fragment_size;
/**
* Initializes channel_task for use.
*/
AWS_IO_API
void aws_channel_task_init(
struct aws_channel_task *channel_task,
aws_channel_task_fn *task_fn,
void *arg,
const char *type_tag);
/**
* Allocates new channel, Unless otherwise specified all functions for channels and channel slots must be executed
* within that channel's event-loop's thread. channel_options are copied.
*/
AWS_IO_API
struct aws_channel *aws_channel_new(struct aws_allocator *allocator, const struct aws_channel_options *creation_args);
/**
* Mark the channel, along with all slots and handlers, for destruction.
* Must be called after shutdown has completed.
* Can be called from any thread assuming 'aws_channel_shutdown()' has completed.
* Note that memory will not be freed until all users which acquired holds on the channel via
* aws_channel_acquire_hold(), release them via aws_channel_release_hold().
*/
AWS_IO_API
void aws_channel_destroy(struct aws_channel *channel);
/**
* Initiates shutdown of the channel. Shutdown will begin with the left-most slot. Each handler will invoke
* 'aws_channel_slot_on_handler_shutdown_complete' once they've finished their shutdown process for the read direction.
* Once the right-most slot has shutdown in the read direction, the process will start shutting down starting on the
* right-most slot. Once the left-most slot has shutdown in the write direction, 'callbacks->shutdown_completed' will be
* invoked in the event loop's thread.
*
* This function can be called from any thread.
*/
AWS_IO_API
int aws_channel_shutdown(struct aws_channel *channel, int error_code);
/**
* Prevent a channel's memory from being freed.
* Any number of users may acquire a hold to prevent a channel and its handlers from being unexpectedly freed.
* Any user which acquires a hold must release it via aws_channel_release_hold().
* Memory will be freed once all holds are released and aws_channel_destroy() has been called.
*/
AWS_IO_API
void aws_channel_acquire_hold(struct aws_channel *channel);
/**
* Release a hold on the channel's memory, allowing it to be freed.
* This may be called before or after aws_channel_destroy().
*/
AWS_IO_API
void aws_channel_release_hold(struct aws_channel *channel);
/**
* Allocates and initializes a new slot for use with the channel. If this is the first slot in the channel, it will
* automatically be added to the channel as the first slot. For all subsequent calls on a given channel, the slot will
* need to be added to the channel via. the aws_channel_slot_insert_right(), aws_channel_slot_insert_end(), and
* aws_channel_slot_insert_left() APIs.
*/
AWS_IO_API
struct aws_channel_slot *aws_channel_slot_new(struct aws_channel *channel);
/**
* Fetches the event loop the channel is a part of.
*/
AWS_IO_API
struct aws_event_loop *aws_channel_get_event_loop(struct aws_channel *channel);
/**
* Fetches the current timestamp from the event-loop's clock, in nanoseconds.
*/
AWS_IO_API
int aws_channel_current_clock_time(struct aws_channel *channel, uint64_t *time_nanos);
/**
* Retrieves an object by key from the event loop's local storage.
*/
AWS_IO_API
int aws_channel_fetch_local_object(
struct aws_channel *channel,
const void *key,
struct aws_event_loop_local_object *obj);
/**
* Stores an object by key in the event loop's local storage.
*/
AWS_IO_API
int aws_channel_put_local_object(
struct aws_channel *channel,
const void *key,
const struct aws_event_loop_local_object *obj);
/**
* Removes an object by key from the event loop's local storage.
*/
AWS_IO_API
int aws_channel_remove_local_object(
struct aws_channel *channel,
const void *key,
struct aws_event_loop_local_object *removed_obj);
/**
* Acquires a message from the event loop's message pool. size_hint is merely a hint, it may be smaller than you
* requested and you are responsible for checking the bounds of it. If the returned message is not large enough, you
* must send multiple messages. This cannot fail, it never returns NULL.
*/
AWS_IO_API
struct aws_io_message *aws_channel_acquire_message_from_pool(
struct aws_channel *channel,
enum aws_io_message_type message_type,
size_t size_hint);
/**
* Schedules a task to run on the event loop as soon as possible.
* This is the ideal way to move a task into the correct thread. It's also handy for context switches.
* This function is safe to call from any thread.
*
* If called from the channel's event loop, the task will get directly added to the run-now list.
* If called from outside the channel's event loop, the task will go into a cross-thread task queue.
*
* If tasks must be serialized relative to some source synchronization, you may not want to use this API
* because tasks submitted from the event loop thread can "jump ahead" of tasks submitted from external threads
* due to this optimization. If this is a problem, you can either refactor your submission logic or use
* the aws_channel_schedule_task_now_serialized variant which does not perform this optimization.
*
* The task should not be cleaned up or modified until its function is executed.
*/
AWS_IO_API
void aws_channel_schedule_task_now(struct aws_channel *channel, struct aws_channel_task *task);
/**
* Schedules a task to run on the event loop as soon as possible.
*
* This variant always uses the cross thread queue rather than conditionally skipping it when already in
* the destination event loop. While not "optimal", this allows us to serialize task execution no matter where
* the task was submitted from: if you are submitting tasks from a critical section, the serialized order that you
* submit is guaranteed to be the order that they execute on the event loop.
*
* The task should not be cleaned up or modified until its function is executed.
*/
AWS_IO_API
void aws_channel_schedule_task_now_serialized(struct aws_channel *channel, struct aws_channel_task *task);
/**
* Schedules a task to run on the event loop at the specified time.
* This is the ideal way to move a task into the correct thread. It's also handy for context switches.
* Use aws_channel_current_clock_time() to get the current time in nanoseconds.
* This function is safe to call from any thread.
*
* The task should not be cleaned up or modified until its function is executed.
*/
AWS_IO_API
void aws_channel_schedule_task_future(
struct aws_channel *channel,
struct aws_channel_task *task,
uint64_t run_at_nanos);
/**
* Instrument a channel with a statistics handler. While instrumented with a statistics handler, the channel
* will periodically report per-channel-handler-specific statistics about handler performance and state.
*
* Assigning a statistics handler to a channel is a transfer of ownership -- the channel will clean up
* the handler appropriately. Statistics handlers may be changed dynamically (for example, the upgrade
* from a vanilla http channel to a websocket channel), but this function may only be called from the
* event loop thread that the channel is a part of.
*
* The first possible hook to set a statistics handler is the channel's creation callback.
*/
AWS_IO_API
int aws_channel_set_statistics_handler(struct aws_channel *channel, struct aws_crt_statistics_handler *handler);
/**
* Returns true if the caller is on the event loop's thread. If false, you likely need to use
* aws_channel_schedule_task(). This function is safe to call from any thread.
*/
AWS_IO_API
bool aws_channel_thread_is_callers_thread(struct aws_channel *channel);
/**
* Sets the handler for a slot, the slot will also call get_current_window_size() and propagate a window update
* upstream.
*/
AWS_IO_API
int aws_channel_slot_set_handler(struct aws_channel_slot *slot, struct aws_channel_handler *handler);
/**
* Removes slot from the channel and deallocates the slot and its handler.
*/
AWS_IO_API
int aws_channel_slot_remove(struct aws_channel_slot *slot);
/**
* Replaces remove with new_slot. Deallocates remove and its handler.
*/
AWS_IO_API
int aws_channel_slot_replace(struct aws_channel_slot *remove, struct aws_channel_slot *new_slot);
/**
* inserts 'to_add' to the position immediately to the right of slot. Note that the first call to
* aws_channel_slot_new() adds it to the channel implicitly.
*/
AWS_IO_API
int aws_channel_slot_insert_right(struct aws_channel_slot *slot, struct aws_channel_slot *to_add);
/**
* Inserts to 'to_add' the end of the channel. Note that the first call to
* aws_channel_slot_new() adds it to the channel implicitly.
*/
AWS_IO_API
int aws_channel_slot_insert_end(struct aws_channel *channel, struct aws_channel_slot *to_add);
/**
* inserts 'to_add' to the position immediately to the left of slot. Note that the first call to
* aws_channel_slot_new() adds it to the channel implicitly.
*/
AWS_IO_API
int aws_channel_slot_insert_left(struct aws_channel_slot *slot, struct aws_channel_slot *to_add);
/**
* Sends a message to the adjacent slot in the channel based on dir. Also does window size checking.
*
* NOTE: if this function returns an error code, it is the caller's responsibility to release message
* back to the pool. If this function returns AWS_OP_SUCCESS, the recipient of the message has taken
* ownership of the message. So, for example, don't release a message to the pool and then return an error.
* If you encounter an error condition in this case, shutdown the channel with the appropriate error code.
*/
AWS_IO_API
int aws_channel_slot_send_message(
struct aws_channel_slot *slot,
struct aws_io_message *message,
enum aws_channel_direction dir);
/**
* Convenience function that invokes aws_channel_acquire_message_from_pool(),
* asking for the largest reasonable DATA message that can be sent in the write direction,
* with upstream overhead accounted for. This cannot fail, it never returns NULL.
*/
AWS_IO_API
struct aws_io_message *aws_channel_slot_acquire_max_message_for_write(struct aws_channel_slot *slot);
/**
* Issues a window update notification upstream (to the left.)
*/
AWS_IO_API
int aws_channel_slot_increment_read_window(struct aws_channel_slot *slot, size_t window);
/**
* Called by handlers once they have finished their shutdown in the 'dir' direction. Propagates the shutdown process
* to the next handler in the channel.
*/
AWS_IO_API
int aws_channel_slot_on_handler_shutdown_complete(
struct aws_channel_slot *slot,
enum aws_channel_direction dir,
int err_code,
bool free_scarce_resources_immediately);
/**
* Initiates shutdown on slot. callbacks->on_shutdown_completed will be called
* once the shutdown process is completed.
*/
AWS_IO_API
int aws_channel_slot_shutdown(
struct aws_channel_slot *slot,
enum aws_channel_direction dir,
int err_code,
bool free_scarce_resources_immediately);
/**
* Fetches the downstream read window. This gives you the information necessary to honor the read window. If you call
* send_message() and it exceeds this window, the message will be rejected.
*/
AWS_IO_API
size_t aws_channel_slot_downstream_read_window(struct aws_channel_slot *slot);
/** Fetches the current overhead of upstream handlers. This provides a hint to avoid fragmentation if you care. */
AWS_IO_API
size_t aws_channel_slot_upstream_message_overhead(struct aws_channel_slot *slot);
/**
* Calls destroy on handler's vtable
*/
AWS_IO_API
void aws_channel_handler_destroy(struct aws_channel_handler *handler);
/**
* Calls process_read_message on handler's vtable
*/
AWS_IO_API
int aws_channel_handler_process_read_message(
struct aws_channel_handler *handler,
struct aws_channel_slot *slot,
struct aws_io_message *message);
/**
* Calls process_write_message on handler's vtable.
*/
AWS_IO_API
int aws_channel_handler_process_write_message(
struct aws_channel_handler *handler,
struct aws_channel_slot *slot,
struct aws_io_message *message);
/**
* Calls on_window_update on handler's vtable.
*/
AWS_IO_API
int aws_channel_handler_increment_read_window(
struct aws_channel_handler *handler,
struct aws_channel_slot *slot,
size_t size);
/**
* calls shutdown_direction on handler's vtable.
*/
AWS_IO_API
int aws_channel_handler_shutdown(
struct aws_channel_handler *handler,
struct aws_channel_slot *slot,
enum aws_channel_direction dir,
int error_code,
bool free_scarce_resources_immediately);
/**
* Calls initial_window_size on handler's vtable.
*/
AWS_IO_API
size_t aws_channel_handler_initial_window_size(struct aws_channel_handler *handler);
AWS_IO_API
struct aws_channel_slot *aws_channel_get_first_slot(struct aws_channel *channel);
/**
* A way for external processes to force a read by the data-source channel handler. Necessary in certain cases, like
* when a server channel finishes setting up its initial handlers, a read may have already been triggered on the
* socket (the client's CLIENT_HELLO tls payload, for example) and absent further data/notifications, this data
* would never get processed.
*/
AWS_IO_API
int aws_channel_trigger_read(struct aws_channel *channel);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_CHANNEL_H */

View File

@@ -0,0 +1,311 @@
#ifndef AWS_IO_CHANNEL_BOOTSTRAP_H
#define AWS_IO_CHANNEL_BOOTSTRAP_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/ref_count.h>
#include <aws/io/channel.h>
#include <aws/io/host_resolver.h>
AWS_PUSH_SANE_WARNING_LEVEL
struct aws_client_bootstrap;
struct aws_socket;
struct aws_socket_options;
struct aws_socket_endpoint;
/**
* Generic event function for channel lifecycle events.
*
* Callbacks are provided for:
* (1) Channel creation
* (2) Channel setup - If TLS is being used, this function is called once the socket has connected, the channel has
* been initialized, and TLS has been successfully negotiated. A TLS handler has already been added to the channel. If
* TLS negotiation fails, this function will be called with the corresponding error code. If TLS is not being used, this
* function is called once the socket has connected and the channel has been initialized.
* (3) Channel shutdown
*
* These callbacks are always invoked within the thread of the event-loop that the channel is assigned to.
*
* This function does NOT always imply "success" -- if error_code is AWS_OP_SUCCESS then everything was successful,
* otherwise an error condition occurred.
*/
typedef void(aws_client_bootstrap_on_channel_event_fn)(
struct aws_client_bootstrap *bootstrap,
int error_code,
struct aws_channel *channel,
void *user_data);
/**
* If ALPN is being used this function will be invoked by the channel once an ALPN message is received. The returned
* channel_handler will be added to, and managed by, the channel.
*/
typedef struct aws_channel_handler *(aws_channel_on_protocol_negotiated_fn)(struct aws_channel_slot *new_slot,
struct aws_byte_buf *protocol,
void *user_data);
struct aws_tls_connection_options;
struct aws_event_loop_group;
/**
* Called after client bootstrap has been completely cleaned up, after its last refcount is released.
*/
typedef void aws_client_bootstrap_shutdown_complete_fn(void *user_data);
/**
* aws_client_bootstrap handles creation and setup of channels that communicate via socket with a specific endpoint.
*/
struct aws_client_bootstrap {
struct aws_allocator *allocator;
struct aws_event_loop_group *event_loop_group;
struct aws_host_resolver *host_resolver;
struct aws_host_resolution_config host_resolver_config;
aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated;
struct aws_ref_count ref_count;
aws_client_bootstrap_shutdown_complete_fn *on_shutdown_complete;
void *user_data;
};
/**
* aws_client_bootstrap creation options.
*/
struct aws_client_bootstrap_options {
/* Required. Must outlive the client bootstrap. */
struct aws_event_loop_group *event_loop_group;
/* Required. Must outlive the client bootstrap. */
struct aws_host_resolver *host_resolver;
/* Optional. If none is provided then default settings are used.
* This object is deep-copied by bootstrap.
* */
const struct aws_host_resolution_config *host_resolution_config;
/* Optional. If provided, callback is invoked when client bootstrap has completely shut down. */
aws_client_bootstrap_shutdown_complete_fn *on_shutdown_complete;
/* Optional. Passed to callbacks */
void *user_data;
};
struct aws_server_bootstrap;
/**
* If TLS is being used, this function is called once the socket has received an incoming connection, the channel has
* been initialized, and TLS has been successfully negotiated. A TLS handler has already been added to the channel. If
* TLS negotiation fails, this function will be called with the corresponding error code.
*
* If TLS is not being used, this function is called once the socket has received an incoming connection and the channel
* has been initialized.
*
* This function is always called within the thread of the event-loop that the new channel is assigned to upon success.
*
* On failure, the channel might not be assigned to an event loop yet, and will thus be invoked on the listener's
* event-loop thread.
*
* This function does NOT mean "success", if error_code is AWS_OP_SUCCESS then everything was successful, otherwise an
* error condition occurred.
*
* If an error occurred, you do not need to shutdown the channel. The `aws_channel_client_shutdown_callback` will be
* invoked once the channel has finished shutting down.
*/
typedef void(aws_server_bootstrap_on_accept_channel_setup_fn)(
struct aws_server_bootstrap *bootstrap,
int error_code,
struct aws_channel *channel,
void *user_data);
/**
* Once the channel shuts down, this function will be invoked within the thread of
* the event-loop that the channel is assigned to.
*
* Note: this function is only invoked if the channel was successfully setup,
* e.g. aws_server_bootstrap_on_accept_channel_setup_fn() was invoked without an error code.
*/
typedef void(aws_server_bootstrap_on_accept_channel_shutdown_fn)(
struct aws_server_bootstrap *bootstrap,
int error_code,
struct aws_channel *channel,
void *user_data);
/**
* Once the server listener socket is finished destroying, and all the existing connections are closed, this fuction
* will be invoked.
*/
typedef void(
aws_server_bootstrap_on_server_listener_destroy_fn)(struct aws_server_bootstrap *bootstrap, void *user_data);
/**
* aws_server_bootstrap manages listening sockets, creating and setting up channels to handle each incoming connection.
*/
struct aws_server_bootstrap {
struct aws_allocator *allocator;
struct aws_event_loop_group *event_loop_group;
aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated;
struct aws_ref_count ref_count;
};
/**
* Socket-based channel creation options.
*
* bootstrap - configs name resolution and which event loop group the connection will be seated into
* host_name - host to connect to; if a dns address, will be resolved prior to connecting
* port - port to connect to
* socket_options - socket properties, including type (tcp vs. udp vs. unix domain) and connect timeout. TLS
* connections are currently restricted to tcp (AWS_SOCKET_STREAM) only.
* tls_options - (optional) tls context to apply after connection establishment. If NULL, the connection will
* not be protected by TLS.
* creation_callback - (optional) callback invoked when the channel is first created. This is always right after
* the connection was successfully established. *Does NOT* get called if the initial connect failed.
* setup_callback - callback invoked once the channel is ready for use and TLS has been negotiated or if an error
* is encountered
* shutdown_callback - callback invoked once the channel has shutdown.
* enable_read_back_pressure - controls whether or not back pressure will be applied in the channel
* user_data - arbitrary data to pass back to the various callbacks
* requested_event_loop - if set, the connection will be placed on the requested event loop rather than one
* chosen internally from the bootstrap's associated event loop group. It is an error to pass in an event loop
* that is not associated with the bootstrap's event loop group.
*
* Immediately after the `shutdown_callback` returns, the channel is cleaned up automatically. All callbacks are invoked
* in the thread of the event-loop that the new channel is assigned to.
*
*/
struct aws_socket_channel_bootstrap_options {
struct aws_client_bootstrap *bootstrap;
const char *host_name;
uint32_t port;
const struct aws_socket_options *socket_options;
const struct aws_tls_connection_options *tls_options;
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;
bool enable_read_back_pressure;
void *user_data;
struct aws_event_loop *requested_event_loop;
const struct aws_host_resolution_config *host_resolution_override_config;
};
/**
* Arguments to setup a server socket listener which will also negotiate and configure TLS.
* This creates a socket listener bound to `host` and 'port' using socket options `options`, and TLS options
* `tls_options`. `incoming_callback` will be invoked once an incoming channel is ready for use and TLS is
* finished negotiating, or if an error is encountered. `shutdown_callback` will be invoked once the channel has
* shutdown. `destroy_callback` will be invoked after the server socket listener is destroyed, and all associated
* connections and channels have finished shutting down. Immediately after the `shutdown_callback` returns, the channel
* is cleaned up automatically. All callbacks are invoked in the thread of the event-loop that listener is assigned to.
*
* Upon shutdown of your application, you'll want to call `aws_server_bootstrap_destroy_socket_listener` with the return
* value from this function.
*
* The socket type in `options` must be AWS_SOCKET_STREAM if tls_options is set.
* DTLS is not currently supported for tls.
*/
struct aws_server_socket_channel_bootstrap_options {
struct aws_server_bootstrap *bootstrap;
const char *host_name;
uint32_t port;
const struct aws_socket_options *socket_options;
const struct aws_tls_connection_options *tls_options;
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;
bool enable_read_back_pressure;
void *user_data;
};
AWS_EXTERN_C_BEGIN
/**
* Create the client bootstrap.
*/
AWS_IO_API struct aws_client_bootstrap *aws_client_bootstrap_new(
struct aws_allocator *allocator,
const struct aws_client_bootstrap_options *options);
/**
* Increments a client bootstrap's ref count, allowing the caller to take a reference to it.
*
* Returns the same client bootstrap passed in.
*/
AWS_IO_API struct aws_client_bootstrap *aws_client_bootstrap_acquire(struct aws_client_bootstrap *bootstrap);
/**
* Decrements a client bootstrap's ref count. When the ref count drops to zero, the bootstrap will be destroyed.
*/
AWS_IO_API void aws_client_bootstrap_release(struct aws_client_bootstrap *bootstrap);
/**
* When using TLS, if ALPN is used, this callback will be invoked from the channel. The returned handler will be added
* to the channel.
*/
AWS_IO_API int aws_client_bootstrap_set_alpn_callback(
struct aws_client_bootstrap *bootstrap,
aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated);
/**
* Sets up a client socket channel.
*/
AWS_IO_API int aws_client_bootstrap_new_socket_channel(struct aws_socket_channel_bootstrap_options *options);
/**
* Initializes the server bootstrap with `allocator` and `el_group`. This object manages listeners, server connections,
* and channels.
*/
AWS_IO_API struct aws_server_bootstrap *aws_server_bootstrap_new(
struct aws_allocator *allocator,
struct aws_event_loop_group *el_group);
/**
* Increments a server bootstrap's ref count, allowing the caller to take a reference to it.
*
* Returns the same server bootstrap passed in.
*/
AWS_IO_API struct aws_server_bootstrap *aws_server_bootstrap_acquire(struct aws_server_bootstrap *bootstrap);
/**
* Decrements a server bootstrap's ref count. When the ref count drops to zero, the bootstrap will be destroyed.
*/
AWS_IO_API void aws_server_bootstrap_release(struct aws_server_bootstrap *bootstrap);
/**
* When using TLS, if ALPN is used, this callback will be invoked from the channel. The returned handler will be added
* to the channel.
*/
AWS_IO_API int aws_server_bootstrap_set_alpn_callback(
struct aws_server_bootstrap *bootstrap,
aws_channel_on_protocol_negotiated_fn *on_protocol_negotiated);
/**
* Sets up a server socket listener. If you are planning on using TLS, use
* `aws_server_bootstrap_new_tls_socket_listener` instead. This creates a socket listener bound to `local_endpoint`
* using socket options `options`. `incoming_callback` will be invoked once an incoming channel is ready for use or if
* an error is encountered. `shutdown_callback` will be invoked once the channel has shutdown. `destroy_callback` will
* be invoked after the server socket listener is destroyed, and all associated connections and channels have finished
* shutting down. Immediately after the `shutdown_callback` returns, the channel is cleaned up automatically. All
* callbacks are invoked the thread of the event-loop that the listening socket is assigned to
*
* Upon shutdown of your application, you'll want to call `aws_server_bootstrap_destroy_socket_listener` with the return
* value from this function.
*
* bootstrap_options is copied.
*/
AWS_IO_API struct aws_socket *aws_server_bootstrap_new_socket_listener(
const struct aws_server_socket_channel_bootstrap_options *bootstrap_options);
/**
* Shuts down 'listener' and cleans up any resources associated with it. Any incoming channels on `listener` will still
* be active. `destroy_callback` will be invoked after the server socket listener is destroyed, and all associated
* connections and channels have finished shutting down.
*/
AWS_IO_API void aws_server_bootstrap_destroy_socket_listener(
struct aws_server_bootstrap *bootstrap,
struct aws_socket *listener);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_CHANNEL_BOOTSTRAP_H */

View File

@@ -0,0 +1,476 @@
#ifndef AWS_IO_EVENT_LOOP_H
#define AWS_IO_EVENT_LOOP_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/atomics.h>
#include <aws/common/hash_table.h>
#include <aws/common/ref_count.h>
#include <aws/io/io.h>
AWS_PUSH_SANE_WARNING_LEVEL
enum aws_io_event_type {
AWS_IO_EVENT_TYPE_READABLE = 1,
AWS_IO_EVENT_TYPE_WRITABLE = 2,
AWS_IO_EVENT_TYPE_REMOTE_HANG_UP = 4,
AWS_IO_EVENT_TYPE_CLOSED = 8,
AWS_IO_EVENT_TYPE_ERROR = 16,
};
struct aws_event_loop;
struct aws_task;
struct aws_thread_options;
#if AWS_USE_IO_COMPLETION_PORTS
struct aws_overlapped;
typedef void(aws_event_loop_on_completion_fn)(
struct aws_event_loop *event_loop,
struct aws_overlapped *overlapped,
int status_code,
size_t num_bytes_transferred);
/**
* The aws_win32_OVERLAPPED struct is layout-compatible with OVERLAPPED as defined in <Windows.h>. It is used
* here to avoid pulling in a dependency on <Windows.h> which would also bring along a lot of bad macros, such
* as redefinitions of GetMessage and GetObject. Note that the OVERLAPPED struct layout in the Windows SDK can
* never be altered without breaking binary compatibility for every existing third-party executable, so there
* is no need to worry about keeping this definition in sync.
*/
struct aws_win32_OVERLAPPED {
uintptr_t Internal;
uintptr_t InternalHigh;
union {
struct {
uint32_t Offset;
uint32_t OffsetHigh;
} s;
void *Pointer;
} u;
void *hEvent;
};
/**
* Use aws_overlapped when a handle connected to the event loop needs an OVERLAPPED struct.
* OVERLAPPED structs are needed to make OS-level async I/O calls.
* When the I/O completes, the assigned aws_event_loop_on_completion_fn is called from the event_loop's thread.
* While the I/O is pending, it is not safe to modify or delete aws_overlapped.
* Call aws_overlapped_init() before first use. If the aws_overlapped will be used multiple times, call
* aws_overlapped_reset() or aws_overlapped_init() between uses.
*/
struct aws_overlapped {
struct aws_win32_OVERLAPPED overlapped;
aws_event_loop_on_completion_fn *on_completion;
void *user_data;
};
#else /* !AWS_USE_IO_COMPLETION_PORTS */
typedef void(aws_event_loop_on_event_fn)(
struct aws_event_loop *event_loop,
struct aws_io_handle *handle,
int events,
void *user_data);
#endif /* AWS_USE_IO_COMPLETION_PORTS */
struct aws_event_loop_vtable {
void (*destroy)(struct aws_event_loop *event_loop);
int (*run)(struct aws_event_loop *event_loop);
int (*stop)(struct aws_event_loop *event_loop);
int (*wait_for_stop_completion)(struct aws_event_loop *event_loop);
void (*schedule_task_now)(struct aws_event_loop *event_loop, struct aws_task *task);
void (*schedule_task_future)(struct aws_event_loop *event_loop, struct aws_task *task, uint64_t run_at_nanos);
void (*cancel_task)(struct aws_event_loop *event_loop, struct aws_task *task);
#if AWS_USE_IO_COMPLETION_PORTS
int (*connect_to_io_completion_port)(struct aws_event_loop *event_loop, struct aws_io_handle *handle);
#else
int (*subscribe_to_io_events)(
struct aws_event_loop *event_loop,
struct aws_io_handle *handle,
int events,
aws_event_loop_on_event_fn *on_event,
void *user_data);
#endif
int (*unsubscribe_from_io_events)(struct aws_event_loop *event_loop, struct aws_io_handle *handle);
void (*free_io_event_resources)(void *user_data);
bool (*is_on_callers_thread)(struct aws_event_loop *event_loop);
};
struct aws_event_loop {
struct aws_event_loop_vtable *vtable;
struct aws_allocator *alloc;
aws_io_clock_fn *clock;
struct aws_hash_table local_data;
struct aws_atomic_var current_load_factor;
uint64_t latest_tick_start;
size_t current_tick_latency_sum;
struct aws_atomic_var next_flush_time;
void *impl_data;
};
struct aws_event_loop_local_object;
typedef void(aws_event_loop_on_local_object_removed_fn)(struct aws_event_loop_local_object *);
struct aws_event_loop_local_object {
const void *key;
void *object;
aws_event_loop_on_local_object_removed_fn *on_object_removed;
};
struct aws_event_loop_options {
aws_io_clock_fn *clock;
struct aws_thread_options *thread_options;
};
typedef struct aws_event_loop *(aws_new_event_loop_fn)(struct aws_allocator *alloc,
const struct aws_event_loop_options *options,
void *new_loop_user_data);
struct aws_event_loop_group {
struct aws_allocator *allocator;
struct aws_array_list event_loops;
struct aws_ref_count ref_count;
struct aws_shutdown_callback_options shutdown_options;
};
AWS_EXTERN_C_BEGIN
#ifdef AWS_USE_IO_COMPLETION_PORTS
/**
* Prepares aws_overlapped for use, and sets a function to call when the overlapped operation completes.
*/
AWS_IO_API
void aws_overlapped_init(
struct aws_overlapped *overlapped,
aws_event_loop_on_completion_fn *on_completion,
void *user_data);
/**
* Prepares aws_overlapped for re-use without changing the assigned aws_event_loop_on_completion_fn.
* Call aws_overlapped_init(), instead of aws_overlapped_reset(), to change the aws_event_loop_on_completion_fn.
*/
AWS_IO_API
void aws_overlapped_reset(struct aws_overlapped *overlapped);
/**
* Casts an aws_overlapped pointer for use as a LPOVERLAPPED parameter to Windows API functions
*/
AWS_IO_API
struct _OVERLAPPED *aws_overlapped_to_windows_overlapped(struct aws_overlapped *overlapped);
#endif /* AWS_USE_IO_COMPLETION_PORTS */
/**
* Creates an instance of the default event loop implementation for the current architecture and operating system.
*/
AWS_IO_API
struct aws_event_loop *aws_event_loop_new_default(struct aws_allocator *alloc, aws_io_clock_fn *clock);
/**
* Creates an instance of the default event loop implementation for the current architecture and operating system using
* extendable options.
*/
AWS_IO_API
struct aws_event_loop *aws_event_loop_new_default_with_options(
struct aws_allocator *alloc,
const struct aws_event_loop_options *options);
/**
* Invokes the destroy() fn for the event loop implementation.
* If the event loop is still in a running state, this function will block waiting on the event loop to shutdown.
* If you do not want this function to block, call aws_event_loop_stop() manually first.
* If the event loop is shared by multiple threads then destroy must be called by exactly one thread. All other threads
* must ensure their API calls to the event loop happen-before the call to destroy.
*/
AWS_IO_API
void aws_event_loop_destroy(struct aws_event_loop *event_loop);
/**
* Initializes common event-loop data structures.
* This is only called from the *new() function of event loop implementations.
*/
AWS_IO_API
int aws_event_loop_init_base(struct aws_event_loop *event_loop, struct aws_allocator *alloc, aws_io_clock_fn *clock);
/**
* Common cleanup code for all implementations.
* This is only called from the *destroy() function of event loop implementations.
*/
AWS_IO_API
void aws_event_loop_clean_up_base(struct aws_event_loop *event_loop);
/**
* Fetches an object from the event-loop's data store. Key will be taken as the memory address of the memory pointed to
* by key. This function is not thread safe and should be called inside the event-loop's thread.
*/
AWS_IO_API
int aws_event_loop_fetch_local_object(
struct aws_event_loop *event_loop,
void *key,
struct aws_event_loop_local_object *obj);
/**
* Puts an item object the event-loop's data store. Key will be taken as the memory address of the memory pointed to by
* key. The lifetime of item must live until remove or a put item overrides it. This function is not thread safe and
* should be called inside the event-loop's thread.
*/
AWS_IO_API
int aws_event_loop_put_local_object(struct aws_event_loop *event_loop, struct aws_event_loop_local_object *obj);
/**
* Removes an object from the event-loop's data store. Key will be taken as the memory address of the memory pointed to
* by key. If removed_item is not null, the removed item will be moved to it if it exists. Otherwise, the default
* deallocation strategy will be used. This function is not thread safe and should be called inside the event-loop's
* thread.
*/
AWS_IO_API
int aws_event_loop_remove_local_object(
struct aws_event_loop *event_loop,
void *key,
struct aws_event_loop_local_object *removed_obj);
/**
* Triggers the running of the event loop. This function must not block. The event loop is not active until this
* function is invoked. This function can be called again on an event loop after calling aws_event_loop_stop() and
* aws_event_loop_wait_for_stop_completion().
*/
AWS_IO_API
int aws_event_loop_run(struct aws_event_loop *event_loop);
/**
* Triggers the event loop to stop, but does not wait for the loop to stop completely.
* This function may be called from outside or inside the event loop thread. It is safe to call multiple times.
* This function is called from destroy().
*
* If you do not call destroy(), an event loop can be run again by calling stop(), wait_for_stop_completion(), run().
*/
AWS_IO_API
int aws_event_loop_stop(struct aws_event_loop *event_loop);
/**
* For event-loop implementations to use for providing metrics info to the base event-loop. This enables the
* event-loop load balancer to take into account load when vending another event-loop to a caller.
*
* Call this function at the beginning of your event-loop tick: after wake-up, but before processing any IO or tasks.
*/
AWS_IO_API
void aws_event_loop_register_tick_start(struct aws_event_loop *event_loop);
/**
* For event-loop implementations to use for providing metrics info to the base event-loop. This enables the
* event-loop load balancer to take into account load when vending another event-loop to a caller.
*
* Call this function at the end of your event-loop tick: after processing IO and tasks.
*/
AWS_IO_API
void aws_event_loop_register_tick_end(struct aws_event_loop *event_loop);
/**
* Returns the current load factor (however that may be calculated). If the event-loop is not invoking
* aws_event_loop_register_tick_start() and aws_event_loop_register_tick_end(), this value will always be 0.
*/
AWS_IO_API
size_t aws_event_loop_get_load_factor(struct aws_event_loop *event_loop);
/**
* Blocks until the event loop stops completely.
* If you want to call aws_event_loop_run() again, you must call this after aws_event_loop_stop().
* It is not safe to call this function from inside the event loop thread.
*/
AWS_IO_API
int aws_event_loop_wait_for_stop_completion(struct aws_event_loop *event_loop);
/**
* The event loop will schedule the task and run it on the event loop thread as soon as possible.
* Note that cancelled tasks may execute outside the event loop thread.
* This function may be called from outside or inside the event loop thread.
*
* The task should not be cleaned up or modified until its function is executed.
*/
AWS_IO_API
void aws_event_loop_schedule_task_now(struct aws_event_loop *event_loop, struct aws_task *task);
/**
* The event loop will schedule the task and run it at the specified time.
* Use aws_event_loop_current_clock_time() to query the current time in nanoseconds.
* Note that cancelled tasks may execute outside the event loop thread.
* This function may be called from outside or inside the event loop thread.
*
* The task should not be cleaned up or modified until its function is executed.
*/
AWS_IO_API
void aws_event_loop_schedule_task_future(
struct aws_event_loop *event_loop,
struct aws_task *task,
uint64_t run_at_nanos);
/**
* Cancels task.
* This function must be called from the event loop's thread, and is only guaranteed
* to work properly on tasks scheduled from within the event loop's thread.
* The task will be executed with the AWS_TASK_STATUS_CANCELED status inside this call.
*/
AWS_IO_API
void aws_event_loop_cancel_task(struct aws_event_loop *event_loop, struct aws_task *task);
#if AWS_USE_IO_COMPLETION_PORTS
/**
* Associates an aws_io_handle with the event loop's I/O Completion Port.
*
* The handle must use aws_overlapped for all async operations requiring an OVERLAPPED struct.
* When the operation completes, the aws_overlapped's completion function will run on the event loop thread.
* Note that completion functions will not be invoked while the event loop is stopped. Users should wait for all async
* operations on connected handles to complete before cleaning up or destroying the event loop.
*
* A handle may only be connected to one event loop in its lifetime.
*/
AWS_IO_API
int aws_event_loop_connect_handle_to_io_completion_port(
struct aws_event_loop *event_loop,
struct aws_io_handle *handle);
#else /* !AWS_USE_IO_COMPLETION_PORTS */
/**
* Subscribes on_event to events on the event-loop for handle. events is a bitwise concatenation of the events that were
* received. The definition for these values can be found in aws_io_event_type. Currently, only
* AWS_IO_EVENT_TYPE_READABLE and AWS_IO_EVENT_TYPE_WRITABLE are honored. You always are registered for error conditions
* and closure. This function may be called from outside or inside the event loop thread. However, the unsubscribe
* function must be called inside the event-loop's thread.
*/
AWS_IO_API
int aws_event_loop_subscribe_to_io_events(
struct aws_event_loop *event_loop,
struct aws_io_handle *handle,
int events,
aws_event_loop_on_event_fn *on_event,
void *user_data);
#endif /* AWS_USE_IO_COMPLETION_PORTS */
/**
* Unsubscribes handle from event-loop notifications.
* This function is not thread safe and should be called inside the event-loop's thread.
*
* NOTE: if you are using io completion ports, this is a risky call. We use it in places, but only when we're certain
* there's no pending events. If you want to use it, it's your job to make sure you don't have pending events before
* calling it.
*/
AWS_IO_API
int aws_event_loop_unsubscribe_from_io_events(struct aws_event_loop *event_loop, struct aws_io_handle *handle);
/**
* Cleans up resources (user_data) associated with the I/O eventing subsystem for a given handle. This should only
* ever be necessary in the case where you are cleaning up an event loop during shutdown and its thread has already
* been joined.
*/
AWS_IO_API
void aws_event_loop_free_io_event_resources(struct aws_event_loop *event_loop, struct aws_io_handle *handle);
/**
* Returns true if the event loop's thread is the same thread that called this function, otherwise false.
*/
AWS_IO_API
bool aws_event_loop_thread_is_callers_thread(struct aws_event_loop *event_loop);
/**
* Gets the current timestamp for the event loop's clock, in nanoseconds. This function is thread-safe.
*/
AWS_IO_API
int aws_event_loop_current_clock_time(struct aws_event_loop *event_loop, uint64_t *time_nanos);
/**
* Creates an event loop group, with clock, number of loops to manage, and the function to call for creating a new
* event loop.
*/
AWS_IO_API
struct aws_event_loop_group *aws_event_loop_group_new(
struct aws_allocator *alloc,
aws_io_clock_fn *clock,
uint16_t el_count,
aws_new_event_loop_fn *new_loop_fn,
void *new_loop_user_data,
const struct aws_shutdown_callback_options *shutdown_options);
/** Creates an event loop group, with clock, number of loops to manage, the function to call for creating a new
* event loop, and also pins all loops to hw threads on the same cpu_group (e.g. NUMA nodes). Note:
* If el_count exceeds the number of hw threads in the cpu_group it will be ignored on the assumption that if you
* care about NUMA, you don't want hyper-threads doing your IO and you especially don't want IO on a different node.
*/
AWS_IO_API
struct aws_event_loop_group *aws_event_loop_group_new_pinned_to_cpu_group(
struct aws_allocator *alloc,
aws_io_clock_fn *clock,
uint16_t el_count,
uint16_t cpu_group,
aws_new_event_loop_fn *new_loop_fn,
void *new_loop_user_data,
const struct aws_shutdown_callback_options *shutdown_options);
/**
* Initializes an event loop group with platform defaults. If max_threads == 0, then the
* loop count will be the number of available processors on the machine / 2 (to exclude hyper-threads).
* Otherwise, max_threads will be the number of event loops in the group.
*/
AWS_IO_API
struct aws_event_loop_group *aws_event_loop_group_new_default(
struct aws_allocator *alloc,
uint16_t max_threads,
const struct aws_shutdown_callback_options *shutdown_options);
/** Creates an event loop group, with clock, number of loops to manage, the function to call for creating a new
* event loop, and also pins all loops to hw threads on the same cpu_group (e.g. NUMA nodes). Note:
* If el_count exceeds the number of hw threads in the cpu_group it will be clamped to the number of hw threads
* on the assumption that if you care about NUMA, you don't want hyper-threads doing your IO and you especially
* don't want IO on a different node.
*
* If max_threads == 0, then the
* loop count will be the number of available processors in the cpu_group / 2 (to exclude hyper-threads)
*/
AWS_IO_API
struct aws_event_loop_group *aws_event_loop_group_new_default_pinned_to_cpu_group(
struct aws_allocator *alloc,
uint16_t max_threads,
uint16_t cpu_group,
const struct aws_shutdown_callback_options *shutdown_options);
/**
* Increments the reference count on the event loop group, allowing the caller to take a reference to it.
*
* Returns the same event loop group passed in.
*/
AWS_IO_API
struct aws_event_loop_group *aws_event_loop_group_acquire(struct aws_event_loop_group *el_group);
/**
* Decrements an event loop group's ref count. When the ref count drops to zero, the event loop group will be
* destroyed.
*/
AWS_IO_API
void aws_event_loop_group_release(struct aws_event_loop_group *el_group);
AWS_IO_API
struct aws_event_loop *aws_event_loop_group_get_loop_at(struct aws_event_loop_group *el_group, size_t index);
AWS_IO_API
size_t aws_event_loop_group_get_loop_count(struct aws_event_loop_group *el_group);
/**
* Fetches the next loop for use. The purpose is to enable load balancing across loops. You should not depend on how
* this load balancing is done as it is subject to change in the future. Currently it uses the "best-of-two" algorithm
* based on the load factor of each loop.
*/
AWS_IO_API
struct aws_event_loop *aws_event_loop_group_get_next_loop(struct aws_event_loop_group *el_group);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_EVENT_LOOP_H */

View File

@@ -0,0 +1,29 @@
#ifndef AWS_IO_EXPORTS_H
#define AWS_IO_EXPORTS_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#if defined(USE_WINDOWS_DLL_SEMANTICS) || defined(WIN32)
# ifdef AWS_IO_USE_IMPORT_EXPORT
# ifdef AWS_IO_EXPORTS
# define AWS_IO_API __declspec(dllexport)
# else
# define AWS_IO_API __declspec(dllimport)
# endif /* AWS_IO_EXPORTS */
# else
# define AWS_IO_API
# endif /* USE_IMPORT_EXPORT */
#else
# if ((__GNUC__ >= 4) || defined(__clang__)) && defined(AWS_IO_USE_IMPORT_EXPORT) && defined(AWS_IO_EXPORTS)
# define AWS_IO_API __attribute__((visibility("default")))
# else
# define AWS_IO_API
# endif /* __GNUC__ >= 4 || defined(__clang__) */
#endif /* defined(USE_WINDOWS_DLL_SEMANTICS) || defined(WIN32) */
#endif /* AWS_IO_EXPORTS_H */

View File

@@ -0,0 +1,12 @@
#ifndef AWS_IO_FILE_UTILS_H
#define AWS_IO_FILE_UTILS_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
/* Just shim the code that's in to common, maintain the public interface */
#include <aws/common/file.h>
#endif /* AWS_IO_FILE_UTILS_H */

View File

@@ -0,0 +1,627 @@
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#ifndef AWS_IO_FUTURE_H
#define AWS_IO_FUTURE_H
/*
// THIS IS AN EXPERIMENTAL AND UNSTABLE API
//
// An aws_future is used to deliver the result of an asynchronous function.
//
// When an async function is called, it creates a future and returns it to the caller.
// When the async work is finished, it completes the future by setting an error or result value.
// The caller waits until the future is done, checks for error, and then gets
// the result if everything was OK. Typically, the caller waits by registering
// a callback that the future invokes when it's done.
//
// If result type T has a "destructor" (clean_up(), destroy(), or release() function),
// then the future has set_result_by_move() and get_result_by_move() functions
// that explicitly transfer ownership to and from the future.
// If the future dies, and still "owns" the resource, it calls the destructor.
// If T has no destructor, then the future has set_result() and get_result()
// functions that simply copy T by value.
//
// Macros are used to define a type-safe API for each result type T,
// similar to C++ templates. This makes the API hard to browse, so functions
// are documented in comments below. The result setter/getter functions
// are mildly different based on T's destructor type, and are documented later.
//
// --- API (common to all aws_future<T>) ---
//
// Create a new future, with refcount of 1.
struct aws_future_T *aws_future_T_new(struct aws_allocator *alloc);
// Increment the refcount.
// You can pass NULL (has no effect).
// Returns the same pointer that was passed in.
struct aws_future_T *aws_future_T_acquire(struct aws_future_T *future);
// Decrement the refcount.
// You can pass NULL (has no effect).
// Always returns NULL.
struct aws_future_T *aws_future_T_release(struct aws_future_T *future);
// Set future as done, with an error_code.
// If the future is already done this call is ignored.
void aws_future_T_set_error(struct aws_future_T *future, int error_code);
// Return whether the future is done.
bool aws_future_T_is_done(const struct aws_future_T *future);
// Get the error-code of a completed future.
// If 0 is returned, then the future completed successfully,
// you may now get the result.
//
// WARNING: You MUST NOT call this until the future is done.
int aws_future_T_get_error(const struct aws_future_T *future);
// Register callback to be invoked when the future completes.
//
// If the future is already done, the callback runs synchronously on the calling thread.
// If the future isn't done yet, the callback is registered, and it
// will run synchronously on whatever thread completes the future.
//
// WARNING: You MUST NOT register more than one callback.
void aws_future_T_register_callback(struct aws_future_T *future, aws_future_callback_fn *on_done, void *user_data);
// If the future isn't done yet, then register the completion callback.
//
// Returns true if the callback was registered,
// or false if the future is already done.
//
// Use this when you can't risk the callback running synchronously.
// For example: If you're calling an async function repeatedly,
// and synchronous completion could lead to stack overflow due to recursion.
// Or if you are holding a non-recursive mutex, and the callback also
// needs the mutex, and an immediate callback would deadlock.
//
// WARNING: If a callback is registered, you MUST NOT call this again until
// the callback has been invoked.
bool aws_future_T_register_callback_if_not_done(
struct aws_future_T *future,
aws_future_callback_fn *on_done,
void *user_data);
// Register completion callback to run async on an event-loop thread.
//
// When the future completes, the callback is scheduled to run as an event-loop task.
//
// Use this when you want the callback to run on the event-loop's thread,
// or to ensure the callback runs async even if the future completed synchronously.
//
// WARNING: You MUST NOT register more than one callback.
void aws_future_T_register_event_loop_callback(
struct aws_future_T *future,
struct aws_event_loop *event_loop,
aws_future_callback_fn *on_done,
void *user_data);
// Register completion callback to run async on an aws_channel's thread.
//
// When the future completes, the callback is scheduled to run as a channel task.
//
// Use this when you want the callback to run on the channel's thread,
// or to ensure the callback runs async even if the future completed synchronously.
//
// WARNING: You MUST NOT register more than one callback.
void aws_future_T_register_channel_callback(
struct aws_future_T *future,
struct aws_channel *channel,
aws_future_callback_fn *on_done,
void *user_data);
// Wait (up to timeout_ns) for future to complete.
// Returns true if future completes in this time.
// This blocks the current thread, and is probably only useful for tests and sample programs.
bool aws_future_T_wait(struct aws_future_T *future, uint64_t timeout_ns);
//
// --- Defining new aws_future types ---
// TODO UPDATE THESE DOCS
// To define new types of aws_future<T>, add the appropriate macro to the appropriate header.
// The macros are:
//
// AWS_DECLARE_FUTURE_T_BY_VALUE(FUTURE, T)
// For T stored by value, with no destructor.
// Use with types like bool, size_t, etc
//
// AWS_DECLARE_FUTURE_T_BY_VALUE_WITH_CLEAN_UP(FUTURE, T, CLEAN_UP_FN)
// For T stored by value, with destructor like: void aws_T_clean_up(T*)
// Use with types like `struct aws_byte_buf`
//
// AWS_DECLARE_FUTURE_T_POINTER_WITH_DESTROY(FUTURE, T, DESTROY_FN)
// For T stored by pointer, with destructor like: void aws_T_destroy(T*)
// Use with types like `struct aws_string *`
//
// AWS_DECLARE_FUTURE_T_POINTER_WITH_RELEASE(FUTURE, T, RELEASE_FN)
// For T stored by pointer, with destructor like: T* aws_T_release(T*)
// Use with types like `struct aws_http_message *`
// Note: if T's release() function doesn't return a pointer, use _WITH_DESTROY instead of _WITH_RELEASE.
//
// This file declares several common types: aws_future<size_t>, aws_future<void>, etc.
// But new future types should be declared in the header where that type's API is declared.
// For example: AWS_DECLARE_FUTURE_T_POINTER_WITH_RELEASE(aws_future_http_message, struct aws_http_message)
// would go in: aws-c-http/include/aws/http/request_response.h
//
// The APIs generated by these macros are identical except for the "setter" and "getter" functions.
//
// --- Design (if you're curious) ---
//
// This class was developed to give the user more control over how the completion
// callback is invoked. In the past, we passed completion callbacks to the async
// function. But this could lead to issues when an async function "sometimes"
// completed synchronously and "sometimes" completed async. The async function
// would need to stress about how to schedule the callback so it was always async,
// or more typically just invoke it whenever and leave the caller to figure it out.
//
// This class is also an experiment with "templates/generics in C".
// In order to make the class type-safe, we use macros to define a unique
// API for each result type T we need to store in a future.
// If we refer to aws_future<struct aws_byte_buf>, we mean a struct named
// aws_future_byte_buf, which stores an aws_byte_buf by value.
// This could lead to code bloat, but the type-safety seems worth it.
//
// future is defined in aws-c-io, instead of aws-c-common, so it can
// easily integrate with aws_event_loop and aws_channel.
//
// It's legal to call set_error() or set_result() multiple times.
// If the future is already done, it ignores the call.
// If result T has a destructor, the new result is immediately freed instead of saved.
// This design lets us deal with ambiguity where it's not 100% certain whether a handoff occurred.
// For example: if we call from C->Java and an exception is thrown,
// it's not clear whether Java got the handoff. In this case, we can safely
// call set_error(), completing the future if necessary,
// or being ignored if the future was already done.
*/
#include <aws/io/io.h>
AWS_PUSH_SANE_WARNING_LEVEL
struct aws_channel;
struct aws_event_loop;
struct aws_future_impl;
/** Completion callback for aws_future<T> */
typedef void(aws_future_callback_fn)(void *user_data);
typedef void(aws_future_impl_result_clean_up_fn)(void *result_addr);
typedef void(aws_future_impl_result_destroy_fn)(void *result);
typedef void *(aws_future_impl_result_release_fn)(void *result);
AWS_EXTERN_C_BEGIN
AWS_IO_API
struct aws_future_impl *aws_future_impl_new_by_value(struct aws_allocator *alloc, size_t sizeof_result);
AWS_IO_API
struct aws_future_impl *aws_future_impl_new_by_value_with_clean_up(
struct aws_allocator *alloc,
size_t sizeof_result,
aws_future_impl_result_clean_up_fn *result_clean_up);
AWS_IO_API
struct aws_future_impl *aws_future_impl_new_pointer(struct aws_allocator *alloc);
AWS_IO_API
struct aws_future_impl *aws_future_impl_new_pointer_with_destroy(
struct aws_allocator *alloc,
aws_future_impl_result_destroy_fn *result_destroy);
AWS_IO_API
struct aws_future_impl *aws_future_impl_new_pointer_with_release(
struct aws_allocator *alloc,
aws_future_impl_result_release_fn *result_release);
AWS_IO_API
struct aws_future_impl *aws_future_impl_release(struct aws_future_impl *promise);
AWS_IO_API
struct aws_future_impl *aws_future_impl_acquire(struct aws_future_impl *promise);
AWS_IO_API
void aws_future_impl_set_error(struct aws_future_impl *promise, int error_code);
AWS_IO_API
void aws_future_impl_set_result_by_move(struct aws_future_impl *promise, void *src_address);
AWS_IO_API
bool aws_future_impl_is_done(const struct aws_future_impl *future);
AWS_IO_API
void aws_future_impl_register_callback(
struct aws_future_impl *future,
aws_future_callback_fn *on_done,
void *user_data);
AWS_IO_API
bool aws_future_impl_register_callback_if_not_done(
struct aws_future_impl *future,
aws_future_callback_fn *on_done,
void *user_data);
AWS_IO_API
void aws_future_impl_register_event_loop_callback(
struct aws_future_impl *future,
struct aws_event_loop *event_loop,
aws_future_callback_fn *on_done,
void *user_data);
AWS_IO_API
void aws_future_impl_register_channel_callback(
struct aws_future_impl *future,
struct aws_channel *channel,
aws_future_callback_fn *on_done,
void *user_data);
AWS_IO_API
bool aws_future_impl_wait(const struct aws_future_impl *future, uint64_t timeout_ns);
AWS_IO_API
int aws_future_impl_get_error(const struct aws_future_impl *future);
AWS_IO_API
void *aws_future_impl_get_result_address(const struct aws_future_impl *future);
AWS_IO_API
void aws_future_impl_get_result_by_move(struct aws_future_impl *future, void *dst_address);
/* Common beginning to all aws_future<T> declarations */
#define AWS_FUTURE_T_DECLARATION_BEGIN(FUTURE, API) struct FUTURE;
/* Common beginning to all aws_future<T> implementations */
#define AWS_FUTURE_T_IMPLEMENTATION_BEGIN(FUTURE)
/* Common end to all aws_future<T> declarations */
#define AWS_FUTURE_T_DECLARATION_END(FUTURE, API) \
API struct FUTURE *FUTURE##_acquire(struct FUTURE *future); \
API struct FUTURE *FUTURE##_release(struct FUTURE *future); \
API void FUTURE##_set_error(struct FUTURE *future, int error_code); \
API bool FUTURE##_is_done(const struct FUTURE *future); \
API int FUTURE##_get_error(const struct FUTURE *future); \
API void FUTURE##_register_callback(struct FUTURE *future, aws_future_callback_fn *on_done, void *user_data); \
API bool FUTURE##_register_callback_if_not_done( \
struct FUTURE *future, aws_future_callback_fn *on_done, void *user_data); \
API void FUTURE##_register_event_loop_callback( \
struct FUTURE *future, struct aws_event_loop *event_loop, aws_future_callback_fn *on_done, void *user_data); \
API void FUTURE##_register_channel_callback( \
struct FUTURE *future, struct aws_channel *channel, aws_future_callback_fn *on_done, void *user_data); \
API bool FUTURE##_wait(struct FUTURE *future, uint64_t timeout_ns);
/* Common end to all aws_future<T> implementations */
#define AWS_FUTURE_T_IMPLEMENTATION_END(FUTURE) \
struct FUTURE *FUTURE##_acquire(struct FUTURE *future) { \
return (struct FUTURE *)aws_future_impl_acquire((struct aws_future_impl *)future); \
} \
\
struct FUTURE *FUTURE##_release(struct FUTURE *future) { \
return (struct FUTURE *)aws_future_impl_release((struct aws_future_impl *)future); \
} \
\
void FUTURE##_set_error(struct FUTURE *future, int error_code) { \
aws_future_impl_set_error((struct aws_future_impl *)future, error_code); \
} \
\
bool FUTURE##_is_done(const struct FUTURE *future) { \
return aws_future_impl_is_done((const struct aws_future_impl *)future); \
} \
\
int FUTURE##_get_error(const struct FUTURE *future) { \
return aws_future_impl_get_error((const struct aws_future_impl *)future); \
} \
\
void FUTURE##_register_callback(struct FUTURE *future, aws_future_callback_fn *on_done, void *user_data) { \
aws_future_impl_register_callback((struct aws_future_impl *)future, on_done, user_data); \
} \
\
bool FUTURE##_register_callback_if_not_done( \
struct FUTURE *future, aws_future_callback_fn *on_done, void *user_data) { \
\
return aws_future_impl_register_callback_if_not_done((struct aws_future_impl *)future, on_done, user_data); \
} \
\
void FUTURE##_register_event_loop_callback( \
struct FUTURE *future, struct aws_event_loop *event_loop, aws_future_callback_fn *on_done, void *user_data) { \
\
aws_future_impl_register_event_loop_callback( \
(struct aws_future_impl *)future, event_loop, on_done, user_data); \
} \
\
void FUTURE##_register_channel_callback( \
struct FUTURE *future, struct aws_channel *channel, aws_future_callback_fn *on_done, void *user_data) { \
\
aws_future_impl_register_channel_callback((struct aws_future_impl *)future, channel, on_done, user_data); \
} \
\
bool FUTURE##_wait(struct FUTURE *future, uint64_t timeout_ns) { \
return aws_future_impl_wait((struct aws_future_impl *)future, timeout_ns); \
}
/**
* Declare a future that holds a simple T by value, that needs no destructor.
* Use with types like bool, size_t, etc.
*
* See top of future.h for most API docs.
* The result setters and getters are:
// Set the result.
//
// If the future is already done this call is ignored.
void aws_future_T_set_result(const struct aws_future_T *future, T result);
// Get the result of a completed future.
//
// WARNING: You MUST NOT call this until the future is done.
// WARNING: You MUST NOT call this unless get_error() returned 0.
T aws_future_T_get_result(const struct aws_future_T *future);
*/
#define AWS_FUTURE_T_BY_VALUE_DECLARATION(FUTURE, T, API) \
AWS_FUTURE_T_DECLARATION_BEGIN(FUTURE, API) \
API struct FUTURE *FUTURE##_new(struct aws_allocator *alloc); \
API void FUTURE##_set_result(struct FUTURE *future, T result); \
API T FUTURE##_get_result(const struct FUTURE *future); \
AWS_FUTURE_T_DECLARATION_END(FUTURE, API)
#define AWS_FUTURE_T_BY_VALUE_IMPLEMENTATION(FUTURE, T) \
AWS_FUTURE_T_IMPLEMENTATION_BEGIN(FUTURE) \
struct FUTURE *FUTURE##_new(struct aws_allocator *alloc) { \
return (struct FUTURE *)aws_future_impl_new_by_value(alloc, sizeof(T)); \
} \
\
void FUTURE##_set_result(struct FUTURE *future, T result) { \
aws_future_impl_set_result_by_move((struct aws_future_impl *)future, &result); \
} \
\
T FUTURE##_get_result(const struct FUTURE *future) { \
return *(T *)aws_future_impl_get_result_address((const struct aws_future_impl *)future); \
} \
AWS_FUTURE_T_IMPLEMENTATION_END(FUTURE)
/**
* Declares a future that holds T by value, with destructor like: void aws_T_clean_up(T*)
* Use with types like aws_byte_buf.
*
* See top of future.h for most API docs.
* The result setters and getters are:
// Set the result, transferring ownership.
//
// The memory at `value_address` is memcpy'd into the future,
// and then zeroed out to help prevent accidental reuse.
// It is safe to call this multiple times. If the future is already done,
// the new result is destroyed instead of saved.
void aws_future_T_set_result_by_move(struct aws_future_T *future, T *value_address);
// Get the result, transferring ownership.
//
// WARNING: You MUST NOT call this until the future is done.
// WARNING: You MUST NOT call this unless get_error() returned 0.
// WARNING: You MUST NOT call this multiple times.
T aws_future_T_get_result_by_move(struct aws_future_T *future);
// Get the result, without transferring ownership.
//
// WARNING: You MUST NOT call this until the future is done.
// WARNING: You MUST NOT call this unless get_error() returned 0.
// WARNING: You MUST NOT call this multiple times.
T* aws_future_T_peek_result(const struct aws_future_T *future);
*/
#define AWS_FUTURE_T_BY_VALUE_WITH_CLEAN_UP_DECLARATION(FUTURE, T, API) \
AWS_FUTURE_T_DECLARATION_BEGIN(FUTURE, API) \
API struct FUTURE *FUTURE##_new(struct aws_allocator *alloc); \
API void FUTURE##_set_result_by_move(struct FUTURE *future, T *value_address); \
API T *FUTURE##_peek_result(const struct FUTURE *future); \
API T FUTURE##_get_result_by_move(struct FUTURE *future); \
AWS_FUTURE_T_DECLARATION_END(FUTURE, API)
#define AWS_FUTURE_T_BY_VALUE_WITH_CLEAN_UP_IMPLEMENTATION(FUTURE, T, CLEAN_UP_FN) \
AWS_FUTURE_T_IMPLEMENTATION_BEGIN(FUTURE) \
\
struct FUTURE *FUTURE##_new(struct aws_allocator *alloc) { \
void (*clean_up_fn)(T *) = CLEAN_UP_FN; /* check clean_up() function signature */ \
return (struct FUTURE *)aws_future_impl_new_by_value_with_clean_up( \
alloc, sizeof(T), (aws_future_impl_result_clean_up_fn)clean_up_fn); \
} \
\
void FUTURE##_set_result_by_move(struct FUTURE *future, T *value_address) { \
aws_future_impl_set_result_by_move((struct aws_future_impl *)future, value_address); \
} \
\
T *FUTURE##_peek_result(const struct FUTURE *future) { \
return aws_future_impl_get_result_address((const struct aws_future_impl *)future); \
} \
\
T FUTURE##_get_result_by_move(struct FUTURE *future) { \
T value; \
aws_future_impl_get_result_by_move((struct aws_future_impl *)future, &value); \
return value; \
} \
\
AWS_FUTURE_T_IMPLEMENTATION_END(FUTURE)
/**
* Declares a future that holds T*, with no destructor.
*/
#define AWS_FUTURE_T_POINTER_DECLARATION(FUTURE, T, API) \
AWS_FUTURE_T_DECLARATION_BEGIN(FUTURE, API) \
API struct FUTURE *FUTURE##_new(struct aws_allocator *alloc); \
API void FUTURE##_set_result(struct FUTURE *future, T *result); \
API T *FUTURE##_get_result(const struct FUTURE *future); \
AWS_FUTURE_T_DECLARATION_END(FUTURE, API)
#define AWS_FUTURE_T_POINTER_IMPLEMENTATION(FUTURE, T) \
AWS_FUTURE_T_IMPLEMENTATION_BEGIN(FUTURE) \
\
struct FUTURE *FUTURE##_new(struct aws_allocator *alloc) { \
return (struct FUTURE *)aws_future_impl_new_pointer(alloc); \
} \
\
void FUTURE##_set_result(struct FUTURE *future, T *result) { \
aws_future_impl_set_result_by_move((struct aws_future_impl *)future, &result); \
} \
\
T *FUTURE##_get_result(const struct FUTURE *future) { \
return *(T **)aws_future_impl_get_result_address((const struct aws_future_impl *)future); \
} \
\
AWS_FUTURE_T_IMPLEMENTATION_END(FUTURE)
/**
* Declares a future that holds T*, with destructor like: void aws_T_destroy(T*)
* Use with types like aws_string.
*
* See top of future.h for most API docs.
* The result setters and getters are:
// Set the result, transferring ownership.
//
// The value at `pointer_address` is copied into the future,
// and then set NULL to prevent accidental reuse.
// If the future is already done, this new result is destroyed instead of saved.
void aws_future_T_set_result_by_move(struct aws_future_T *future, T **pointer_address);
// Get the result, transferring ownership.
//
// WARNING: You MUST NOT call this until the future is done.
// WARNING: You MUST NOT call this unless get_error() returned 0.
// WARNING: You MUST NOT call this multiple times.
T* aws_future_T_get_result_by_move(struct aws_future_T *future);
// Get the result, without transferring ownership.
//
// WARNING: You MUST NOT call this until the future is done.
// WARNING: You MUST NOT call this unless get_error() returned 0.
// WARNING: You MUST NOT call this multiple times.
T* aws_future_T_peek_result(const struct aws_future_T *future);
*/
#define AWS_FUTURE_T_POINTER_WITH_DESTROY_DECLARATION(FUTURE, T, API) \
AWS_FUTURE_T_DECLARATION_BEGIN(FUTURE, API) \
API struct FUTURE *FUTURE##_new(struct aws_allocator *alloc); \
API void FUTURE##_set_result_by_move(struct FUTURE *future, T **pointer_address); \
API T *FUTURE##_get_result_by_move(struct FUTURE *future); \
API T *FUTURE##_peek_result(const struct FUTURE *future); \
AWS_FUTURE_T_DECLARATION_END(FUTURE, API)
#define AWS_FUTURE_T_POINTER_WITH_DESTROY_IMPLEMENTATION(FUTURE, T, DESTROY_FN) \
AWS_FUTURE_T_IMPLEMENTATION_BEGIN(FUTURE) \
\
struct FUTURE *FUTURE##_new(struct aws_allocator *alloc) { \
void (*destroy_fn)(T *) = DESTROY_FN; /* check destroy() function signature */ \
return (struct FUTURE *)aws_future_impl_new_pointer_with_destroy( \
alloc, (aws_future_impl_result_destroy_fn *)destroy_fn); \
} \
\
void FUTURE##_set_result_by_move(struct FUTURE *future, T **pointer_address) { \
aws_future_impl_set_result_by_move((struct aws_future_impl *)future, pointer_address); \
} \
\
T *FUTURE##_get_result_by_move(struct FUTURE *future) { \
T *pointer; \
aws_future_impl_get_result_by_move((struct aws_future_impl *)future, &pointer); \
return pointer; \
} \
\
T *FUTURE##_peek_result(const struct FUTURE *future) { \
return *(T **)aws_future_impl_get_result_address((const struct aws_future_impl *)future); \
} \
\
AWS_FUTURE_T_IMPLEMENTATION_END(FUTURE)
/**
* Declares a future that holds T*, with destructor like: T* aws_T_release(T*)
* Use with types like aws_http_message
*
* See top of future.h for most API docs.
* The result setters and getters are:
// Set the result, transferring ownership.
//
// The value at `pointer_address` is copied into the future,
// and then set NULL to prevent accidental reuse.
// If the future is already done, this new result is destroyed instead of saved.
void aws_future_T_set_result_by_move(struct aws_future_T *future, T **pointer_address);
// Get the result, transferring ownership.
//
// WARNING: You MUST NOT call this until the future is done.
// WARNING: You MUST NOT call this unless get_error() returned 0.
// WARNING: You MUST NOT call this multiple times.
T* aws_future_T_get_result_by_move(struct aws_future_T *future);
// Get the result, without transferring ownership.
//
// WARNING: You MUST NOT call this until the future is done.
// WARNING: You MUST NOT call this unless get_error() returned 0.
// WARNING: You MUST NOT call this multiple times.
T* aws_future_T_peek_result(const struct aws_future_T *future);
*/
#define AWS_FUTURE_T_POINTER_WITH_RELEASE_DECLARATION(FUTURE, T, API) \
AWS_FUTURE_T_DECLARATION_BEGIN(FUTURE, API) \
API struct FUTURE *FUTURE##_new(struct aws_allocator *alloc); \
API void FUTURE##_set_result_by_move(struct FUTURE *future, T **pointer_address); \
API T *FUTURE##_get_result_by_move(struct FUTURE *future); \
API T *FUTURE##_peek_result(const struct FUTURE *future); \
AWS_FUTURE_T_DECLARATION_END(FUTURE, API)
#define AWS_FUTURE_T_POINTER_WITH_RELEASE_IMPLEMENTATION(FUTURE, T, RELEASE_FN) \
AWS_FUTURE_T_IMPLEMENTATION_BEGIN(FUTURE) \
\
struct FUTURE *FUTURE##_new(struct aws_allocator *alloc) { \
T *(*release_fn)(T *) = RELEASE_FN; /* check release() function signature */ \
return (struct FUTURE *)aws_future_impl_new_pointer_with_release( \
alloc, (aws_future_impl_result_release_fn *)release_fn); \
} \
\
void FUTURE##_set_result_by_move(struct FUTURE *future, T **pointer_address) { \
aws_future_impl_set_result_by_move((struct aws_future_impl *)future, pointer_address); \
} \
\
T *FUTURE##_get_result_by_move(struct FUTURE *future) { \
T *pointer; \
aws_future_impl_get_result_by_move((struct aws_future_impl *)future, &pointer); \
return pointer; \
} \
\
T *FUTURE##_peek_result(const struct FUTURE *future) { \
return *(T **)aws_future_impl_get_result_address((const struct aws_future_impl *)future); \
} \
\
AWS_FUTURE_T_IMPLEMENTATION_END(FUTURE)
/**
* aws_future<size_t>
*/
AWS_FUTURE_T_BY_VALUE_DECLARATION(aws_future_size, size_t, AWS_IO_API)
/**
* aws_future<bool>
*/
AWS_FUTURE_T_BY_VALUE_DECLARATION(aws_future_bool, bool, AWS_IO_API)
/**
* aws_future<void>
*/
AWS_FUTURE_T_DECLARATION_BEGIN(aws_future_void, AWS_IO_API)
AWS_IO_API struct aws_future_void *aws_future_void_new(struct aws_allocator *alloc);
AWS_IO_API void aws_future_void_set_result(struct aws_future_void *future);
AWS_FUTURE_T_DECLARATION_END(aws_future_void, AWS_IO_API)
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_FUTURE_H */

View File

@@ -0,0 +1,279 @@
#ifndef AWS_IO_HOST_RESOLVER_H
#define AWS_IO_HOST_RESOLVER_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/ref_count.h>
#include <aws/io/io.h>
AWS_PUSH_SANE_WARNING_LEVEL
struct aws_event_loop_group;
enum aws_address_record_type {
/* ipv4 address. */
AWS_ADDRESS_RECORD_TYPE_A,
/* ipv6 address. */
AWS_ADDRESS_RECORD_TYPE_AAAA
};
enum aws_get_host_address_flags {
/* get number of ipv4 addresses. */
AWS_GET_HOST_ADDRESS_COUNT_RECORD_TYPE_A = 0x00000001,
/* get number of ipv6 addresses. */
AWS_GET_HOST_ADDRESS_COUNT_RECORD_TYPE_AAAA = 0x00000002
};
struct aws_string;
struct aws_host_address {
struct aws_allocator *allocator;
const struct aws_string *host;
const struct aws_string *address;
enum aws_address_record_type record_type;
uint64_t expiry;
/* This next section is strictly for mitigating the impact of sticky hosts that aren't performing well. */
/*for use in DNS-based load balancing.*/
size_t use_count;
/* give a hint on when to remove a bad host from service. */
size_t connection_failure_count;
/* we don't implement this yet, but we will asap. */
uint8_t weight;
};
struct aws_host_resolver;
/**
* Invoked once an address has been resolved for host. The type in host_addresses is struct aws_host_address (by-value).
* The caller does not own this memory and you must copy the host address before returning from this function if you
* plan to use it later. For convenience, we've provided the aws_host_address_copy() and aws_host_address_clean_up()
* functions.
*/
typedef void(aws_on_host_resolved_result_fn)(
struct aws_host_resolver *resolver,
const struct aws_string *host_name,
int err_code,
const struct aws_array_list *host_addresses,
void *user_data);
/**
* Function signature for configuring your own resolver (the default just uses getaddrinfo()). The type in
* output_addresses is struct aws_host_address (by-value). We assume this function blocks, hence this absurdly
* complicated design.
*/
typedef int(aws_resolve_host_implementation_fn)(
struct aws_allocator *allocator,
const struct aws_string *host_name,
struct aws_array_list *output_addresses,
void *user_data);
struct aws_host_resolution_config {
aws_resolve_host_implementation_fn *impl;
size_t max_ttl;
void *impl_data;
uint64_t resolve_frequency_ns; /* 0 defaults to 1 second interval */
};
struct aws_host_listener;
struct aws_host_listener_options;
struct aws_host_resolver_purge_host_options {
/* the host to purge the cache for */
const struct aws_string *host;
/* Callback to invoke when the purge is complete */
aws_simple_completion_callback *on_host_purge_complete_callback;
/* user_data will be passed as it is in the callback. */
void *user_data;
};
/** should you absolutely disdain the default implementation, feel free to implement your own. */
struct aws_host_resolver_vtable {
/** clean up everything you allocated, but not resolver itself. */
void (*destroy)(struct aws_host_resolver *resolver);
/** resolve the host by host_name, the user owns host_name, so it needs to be copied if you persist it,
* invoke res with the result. This function should never block. */
int (*resolve_host)(
struct aws_host_resolver *resolver,
const struct aws_string *host_name,
aws_on_host_resolved_result_fn *res,
const struct aws_host_resolution_config *config,
void *user_data);
/** gives your implementation a hint that an address has some failed connections occuring. Do whatever you want (or
* nothing) about it.
*/
int (*record_connection_failure)(struct aws_host_resolver *resolver, const struct aws_host_address *address);
/**
* @Deprecated Use purge_cache_with_callback instead
* wipe out anything you have cached. */
int (*purge_cache)(struct aws_host_resolver *resolver);
/** wipe out anything you have cached. */
int (*purge_cache_with_callback)(
struct aws_host_resolver *resolver,
aws_simple_completion_callback *on_purge_cache_complete_callback,
void *user_data);
/** wipe out anything cached for a specific host */
int (*purge_host_cache)(
struct aws_host_resolver *resolver,
const struct aws_host_resolver_purge_host_options *options);
/** get number of addresses for a given host. */
size_t (*get_host_address_count)(
struct aws_host_resolver *resolver,
const struct aws_string *host_name,
uint32_t flags);
};
struct aws_host_resolver {
struct aws_allocator *allocator;
void *impl;
struct aws_host_resolver_vtable *vtable;
struct aws_ref_count ref_count;
struct aws_shutdown_callback_options shutdown_options;
};
struct aws_host_resolver_default_options {
size_t max_entries;
struct aws_event_loop_group *el_group;
const struct aws_shutdown_callback_options *shutdown_options;
aws_io_clock_fn *system_clock_override_fn;
};
AWS_EXTERN_C_BEGIN
/**
* Copies `from` to `to`.
*/
AWS_IO_API int aws_host_address_copy(const struct aws_host_address *from, struct aws_host_address *to);
/**
* Moves `from` to `to`. After this call, from is no longer usable. Though, it could be resused for another
* move or copy operation.
*/
AWS_IO_API void aws_host_address_move(struct aws_host_address *from, struct aws_host_address *to);
/**
* Cleans up the memory for `address`
*/
AWS_IO_API void aws_host_address_clean_up(struct aws_host_address *address);
/** WARNING! do not call this function directly (getaddrinfo()): it blocks. Provide a pointer to this function for other
* resolution functions. */
AWS_IO_API int aws_default_dns_resolve(
struct aws_allocator *allocator,
const struct aws_string *host_name,
struct aws_array_list *output_addresses,
void *user_data);
/**
* Creates a host resolver with the default behavior. Here's the behavior:
*
* Since there's not a reliable way to do non-blocking DNS without a ton of risky work that would need years of testing
* on every Unix system in existence, we work around it by doing a threaded implementation.
*
* When you request an address, it checks the cache. If the entry isn't in the cache it creates a new one.
* Each entry has a potentially short lived back-ground thread based on ttl for the records. Once we've populated the
* cache and you keep the resolver active, the resolution callback will be invoked immediately. When it's idle, it will
* take a little while in the background thread to fetch more, evaluate TTLs etc... In that case your callback will be
* invoked from the background thread.
*
* --------------------------------------------------------------------------------------------------------------------
*
* A few things to note about TTLs and connection failures.
*
* We attempt to honor your max ttl but will not honor it if dns queries are failing or all of your connections are
* marked as failed. Once we are able to query dns again, we will re-evaluate the TTLs.
*
* Upon notification connection failures, we move them to a separate list. Eventually we retry them when it's likely
* that the endpoint is healthy again or we don't really have another choice, but we try to keep them out of your
* hot path.
*
* ---------------------------------------------------------------------------------------------------------------------
*
* Finally, this entire design attempts to prevent problems where developers have to choose between large TTLs and thus
* sticky hosts or short TTLs and good fleet utilization but now higher latencies. In this design, we resolve every
* second in the background (only while you're actually using the record), but we do not expire the earlier resolved
* addresses until max ttl has passed.
*
* This for example, should enable you to hit thousands of hosts in the Amazon S3 fleet instead of just one or two.
*/
AWS_IO_API struct aws_host_resolver *aws_host_resolver_new_default(
struct aws_allocator *allocator,
const struct aws_host_resolver_default_options *options);
/**
* Increments the reference count on the host resolver, allowing the caller to take a reference to it.
*
* Returns the same host resolver passed in.
*/
AWS_IO_API struct aws_host_resolver *aws_host_resolver_acquire(struct aws_host_resolver *resolver);
/**
* Decrements a host resolver's ref count. When the ref count drops to zero, the resolver will be destroyed.
*/
AWS_IO_API void aws_host_resolver_release(struct aws_host_resolver *resolver);
/**
* calls resolve_host on the vtable. config will be copied.
*/
AWS_IO_API int aws_host_resolver_resolve_host(
struct aws_host_resolver *resolver,
const struct aws_string *host_name,
aws_on_host_resolved_result_fn *res,
const struct aws_host_resolution_config *config,
void *user_data);
/**
* calls record_connection_failure on the vtable.
*/
AWS_IO_API int aws_host_resolver_record_connection_failure(
struct aws_host_resolver *resolver,
const struct aws_host_address *address);
/**
* @Deprecated Use purge_cache_with_callback instead
* calls purge_cache on the vtable.
*/
AWS_IO_API int aws_host_resolver_purge_cache(struct aws_host_resolver *resolver);
/**
* Calls aws_host_resolver_purge_cache_with_callback on the vtable which will wipe out everything host resolver has
* cached.
*/
AWS_IO_API int aws_host_resolver_purge_cache_with_callback(
struct aws_host_resolver *resolver,
aws_simple_completion_callback *on_purge_cache_complete_callback,
void *user_data);
/**
* Removes the cache for a host asynchronously.
*/
AWS_IO_API int aws_host_resolver_purge_host_cache(
struct aws_host_resolver *resolver,
const struct aws_host_resolver_purge_host_options *options);
/**
* get number of addresses for a given host.
*/
AWS_IO_API size_t aws_host_resolver_get_host_address_count(
struct aws_host_resolver *resolver,
const struct aws_string *host_name,
uint32_t flags);
/**
* Returns the default host resolution config used internally if none specified.
*
* @return default host resolution config
*/
AWS_IO_API struct aws_host_resolution_config aws_host_resolver_init_default_resolution_config(void);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_HOST_RESOLVER_H */

View File

@@ -0,0 +1,282 @@
#ifndef AWS_IO_IO_H
#define AWS_IO_IO_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/byte_buf.h>
#include <aws/common/common.h>
#include <aws/common/linked_list.h>
#include <aws/io/exports.h>
AWS_PUSH_SANE_WARNING_LEVEL
#define AWS_C_IO_PACKAGE_ID 1
struct aws_io_handle {
union {
int fd;
void *handle;
} data;
void *additional_data;
};
enum aws_io_message_type {
AWS_IO_MESSAGE_APPLICATION_DATA,
};
struct aws_io_message;
struct aws_channel;
typedef void(aws_channel_on_message_write_completed_fn)(
struct aws_channel *channel,
struct aws_io_message *message,
int err_code,
void *user_data);
struct aws_io_message {
/**
* Allocator used for the message and message data. If this is null, the message belongs to a pool or some other
* message manager.
*/
struct aws_allocator *allocator;
/**
* Buffer containing the data for message
*/
struct aws_byte_buf message_data;
/**
* type of the message. This is used for framework control messages. Currently the only type is
* AWS_IO_MESSAGE_APPLICATION_DATA
*/
enum aws_io_message_type message_type;
/**
* Conveys information about the contents of message_data (e.g. cast the ptr to some type). If 0, it's just opaque
* data.
*/
int message_tag;
/**
* In order to avoid excess allocations/copies, on a partial read or write, the copy mark is set to indicate how
* much of this message has already been processed or copied.
*/
size_t copy_mark;
/**
* The channel that the message is bound to.
*/
struct aws_channel *owning_channel;
/**
* Invoked by the channel once the entire message has been written to the data sink.
*/
aws_channel_on_message_write_completed_fn *on_completion;
/**
* arbitrary user data for the on_completion callback
*/
void *user_data;
/** it's incredibly likely something is going to need to queue this,
* go ahead and make sure the list info is part of the original allocation.
*/
struct aws_linked_list_node queueing_handle;
};
typedef int(aws_io_clock_fn)(uint64_t *timestamp);
enum aws_io_errors {
AWS_IO_CHANNEL_ERROR_ERROR_CANT_ACCEPT_INPUT = AWS_ERROR_ENUM_BEGIN_RANGE(AWS_C_IO_PACKAGE_ID),
AWS_IO_CHANNEL_UNKNOWN_MESSAGE_TYPE,
AWS_IO_CHANNEL_READ_WOULD_EXCEED_WINDOW,
AWS_IO_EVENT_LOOP_ALREADY_ASSIGNED,
AWS_IO_EVENT_LOOP_SHUTDOWN,
AWS_IO_TLS_ERROR_NEGOTIATION_FAILURE,
AWS_IO_TLS_ERROR_NOT_NEGOTIATED,
AWS_IO_TLS_ERROR_WRITE_FAILURE,
AWS_IO_TLS_ERROR_ALERT_RECEIVED,
AWS_IO_TLS_CTX_ERROR,
AWS_IO_TLS_VERSION_UNSUPPORTED,
AWS_IO_TLS_CIPHER_PREF_UNSUPPORTED,
AWS_IO_MISSING_ALPN_MESSAGE,
AWS_IO_UNHANDLED_ALPN_PROTOCOL_MESSAGE,
AWS_IO_FILE_VALIDATION_FAILURE,
AWS_ERROR_IO_EVENT_LOOP_THREAD_ONLY,
AWS_ERROR_IO_ALREADY_SUBSCRIBED,
AWS_ERROR_IO_NOT_SUBSCRIBED,
AWS_ERROR_IO_OPERATION_CANCELLED,
AWS_IO_READ_WOULD_BLOCK,
AWS_IO_BROKEN_PIPE,
AWS_IO_SOCKET_UNSUPPORTED_ADDRESS_FAMILY,
AWS_IO_SOCKET_INVALID_OPERATION_FOR_TYPE,
AWS_IO_SOCKET_CONNECTION_REFUSED,
AWS_IO_SOCKET_TIMEOUT,
AWS_IO_SOCKET_NO_ROUTE_TO_HOST,
AWS_IO_SOCKET_NETWORK_DOWN,
AWS_IO_SOCKET_CLOSED,
AWS_IO_SOCKET_NOT_CONNECTED,
AWS_IO_SOCKET_INVALID_OPTIONS,
AWS_IO_SOCKET_ADDRESS_IN_USE,
AWS_IO_SOCKET_INVALID_ADDRESS,
AWS_IO_SOCKET_ILLEGAL_OPERATION_FOR_STATE,
AWS_IO_SOCKET_CONNECT_ABORTED,
AWS_IO_DNS_QUERY_FAILED,
AWS_IO_DNS_INVALID_NAME,
AWS_IO_DNS_NO_ADDRESS_FOR_HOST,
AWS_IO_DNS_HOST_REMOVED_FROM_CACHE,
AWS_IO_STREAM_INVALID_SEEK_POSITION,
AWS_IO_STREAM_READ_FAILED,
DEPRECATED_AWS_IO_INVALID_FILE_HANDLE,
AWS_IO_SHARED_LIBRARY_LOAD_FAILURE,
AWS_IO_SHARED_LIBRARY_FIND_SYMBOL_FAILURE,
AWS_IO_TLS_NEGOTIATION_TIMEOUT,
AWS_IO_TLS_ALERT_NOT_GRACEFUL,
AWS_IO_MAX_RETRIES_EXCEEDED,
AWS_IO_RETRY_PERMISSION_DENIED,
AWS_IO_TLS_DIGEST_ALGORITHM_UNSUPPORTED,
AWS_IO_TLS_SIGNATURE_ALGORITHM_UNSUPPORTED,
AWS_ERROR_PKCS11_VERSION_UNSUPPORTED,
AWS_ERROR_PKCS11_TOKEN_NOT_FOUND,
AWS_ERROR_PKCS11_KEY_NOT_FOUND,
AWS_ERROR_PKCS11_KEY_TYPE_UNSUPPORTED,
AWS_ERROR_PKCS11_UNKNOWN_CRYPTOKI_RETURN_VALUE,
/* PKCS#11 "CKR_" (Cryptoki Return Value) as AWS error-codes */
AWS_ERROR_PKCS11_CKR_CANCEL,
AWS_ERROR_PKCS11_CKR_HOST_MEMORY,
AWS_ERROR_PKCS11_CKR_SLOT_ID_INVALID,
AWS_ERROR_PKCS11_CKR_GENERAL_ERROR,
AWS_ERROR_PKCS11_CKR_FUNCTION_FAILED,
AWS_ERROR_PKCS11_CKR_ARGUMENTS_BAD,
AWS_ERROR_PKCS11_CKR_NO_EVENT,
AWS_ERROR_PKCS11_CKR_NEED_TO_CREATE_THREADS,
AWS_ERROR_PKCS11_CKR_CANT_LOCK,
AWS_ERROR_PKCS11_CKR_ATTRIBUTE_READ_ONLY,
AWS_ERROR_PKCS11_CKR_ATTRIBUTE_SENSITIVE,
AWS_ERROR_PKCS11_CKR_ATTRIBUTE_TYPE_INVALID,
AWS_ERROR_PKCS11_CKR_ATTRIBUTE_VALUE_INVALID,
AWS_ERROR_PKCS11_CKR_ACTION_PROHIBITED,
AWS_ERROR_PKCS11_CKR_DATA_INVALID,
AWS_ERROR_PKCS11_CKR_DATA_LEN_RANGE,
AWS_ERROR_PKCS11_CKR_DEVICE_ERROR,
AWS_ERROR_PKCS11_CKR_DEVICE_MEMORY,
AWS_ERROR_PKCS11_CKR_DEVICE_REMOVED,
AWS_ERROR_PKCS11_CKR_ENCRYPTED_DATA_INVALID,
AWS_ERROR_PKCS11_CKR_ENCRYPTED_DATA_LEN_RANGE,
AWS_ERROR_PKCS11_CKR_FUNCTION_CANCELED,
AWS_ERROR_PKCS11_CKR_FUNCTION_NOT_PARALLEL,
AWS_ERROR_PKCS11_CKR_FUNCTION_NOT_SUPPORTED,
AWS_ERROR_PKCS11_CKR_KEY_HANDLE_INVALID,
AWS_ERROR_PKCS11_CKR_KEY_SIZE_RANGE,
AWS_ERROR_PKCS11_CKR_KEY_TYPE_INCONSISTENT,
AWS_ERROR_PKCS11_CKR_KEY_NOT_NEEDED,
AWS_ERROR_PKCS11_CKR_KEY_CHANGED,
AWS_ERROR_PKCS11_CKR_KEY_NEEDED,
AWS_ERROR_PKCS11_CKR_KEY_INDIGESTIBLE,
AWS_ERROR_PKCS11_CKR_KEY_FUNCTION_NOT_PERMITTED,
AWS_ERROR_PKCS11_CKR_KEY_NOT_WRAPPABLE,
AWS_ERROR_PKCS11_CKR_KEY_UNEXTRACTABLE,
AWS_ERROR_PKCS11_CKR_MECHANISM_INVALID,
AWS_ERROR_PKCS11_CKR_MECHANISM_PARAM_INVALID,
AWS_ERROR_PKCS11_CKR_OBJECT_HANDLE_INVALID,
AWS_ERROR_PKCS11_CKR_OPERATION_ACTIVE,
AWS_ERROR_PKCS11_CKR_OPERATION_NOT_INITIALIZED,
AWS_ERROR_PKCS11_CKR_PIN_INCORRECT,
AWS_ERROR_PKCS11_CKR_PIN_INVALID,
AWS_ERROR_PKCS11_CKR_PIN_LEN_RANGE,
AWS_ERROR_PKCS11_CKR_PIN_EXPIRED,
AWS_ERROR_PKCS11_CKR_PIN_LOCKED,
AWS_ERROR_PKCS11_CKR_SESSION_CLOSED,
AWS_ERROR_PKCS11_CKR_SESSION_COUNT,
AWS_ERROR_PKCS11_CKR_SESSION_HANDLE_INVALID,
AWS_ERROR_PKCS11_CKR_SESSION_PARALLEL_NOT_SUPPORTED,
AWS_ERROR_PKCS11_CKR_SESSION_READ_ONLY,
AWS_ERROR_PKCS11_CKR_SESSION_EXISTS,
AWS_ERROR_PKCS11_CKR_SESSION_READ_ONLY_EXISTS,
AWS_ERROR_PKCS11_CKR_SESSION_READ_WRITE_SO_EXISTS,
AWS_ERROR_PKCS11_CKR_SIGNATURE_INVALID,
AWS_ERROR_PKCS11_CKR_SIGNATURE_LEN_RANGE,
AWS_ERROR_PKCS11_CKR_TEMPLATE_INCOMPLETE,
AWS_ERROR_PKCS11_CKR_TEMPLATE_INCONSISTENT,
AWS_ERROR_PKCS11_CKR_TOKEN_NOT_PRESENT,
AWS_ERROR_PKCS11_CKR_TOKEN_NOT_RECOGNIZED,
AWS_ERROR_PKCS11_CKR_TOKEN_WRITE_PROTECTED,
AWS_ERROR_PKCS11_CKR_UNWRAPPING_KEY_HANDLE_INVALID,
AWS_ERROR_PKCS11_CKR_UNWRAPPING_KEY_SIZE_RANGE,
AWS_ERROR_PKCS11_CKR_UNWRAPPING_KEY_TYPE_INCONSISTENT,
AWS_ERROR_PKCS11_CKR_USER_ALREADY_LOGGED_IN,
AWS_ERROR_PKCS11_CKR_USER_NOT_LOGGED_IN,
AWS_ERROR_PKCS11_CKR_USER_PIN_NOT_INITIALIZED,
AWS_ERROR_PKCS11_CKR_USER_TYPE_INVALID,
AWS_ERROR_PKCS11_CKR_USER_ANOTHER_ALREADY_LOGGED_IN,
AWS_ERROR_PKCS11_CKR_USER_TOO_MANY_TYPES,
AWS_ERROR_PKCS11_CKR_WRAPPED_KEY_INVALID,
AWS_ERROR_PKCS11_CKR_WRAPPED_KEY_LEN_RANGE,
AWS_ERROR_PKCS11_CKR_WRAPPING_KEY_HANDLE_INVALID,
AWS_ERROR_PKCS11_CKR_WRAPPING_KEY_SIZE_RANGE,
AWS_ERROR_PKCS11_CKR_WRAPPING_KEY_TYPE_INCONSISTENT,
AWS_ERROR_PKCS11_CKR_RANDOM_SEED_NOT_SUPPORTED,
AWS_ERROR_PKCS11_CKR_RANDOM_NO_RNG,
AWS_ERROR_PKCS11_CKR_DOMAIN_PARAMS_INVALID,
AWS_ERROR_PKCS11_CKR_CURVE_NOT_SUPPORTED,
AWS_ERROR_PKCS11_CKR_BUFFER_TOO_SMALL,
AWS_ERROR_PKCS11_CKR_SAVED_STATE_INVALID,
AWS_ERROR_PKCS11_CKR_INFORMATION_SENSITIVE,
AWS_ERROR_PKCS11_CKR_STATE_UNSAVEABLE,
AWS_ERROR_PKCS11_CKR_CRYPTOKI_NOT_INITIALIZED,
AWS_ERROR_PKCS11_CKR_CRYPTOKI_ALREADY_INITIALIZED,
AWS_ERROR_PKCS11_CKR_MUTEX_BAD,
AWS_ERROR_PKCS11_CKR_MUTEX_NOT_LOCKED,
AWS_ERROR_PKCS11_CKR_NEW_PIN_MODE,
AWS_ERROR_PKCS11_CKR_NEXT_OTP,
AWS_ERROR_PKCS11_CKR_EXCEEDED_MAX_ITERATIONS,
AWS_ERROR_PKCS11_CKR_FIPS_SELF_TEST_FAILED,
AWS_ERROR_PKCS11_CKR_LIBRARY_LOAD_FAILED,
AWS_ERROR_PKCS11_CKR_PIN_TOO_WEAK,
AWS_ERROR_PKCS11_CKR_PUBLIC_KEY_INVALID,
AWS_ERROR_PKCS11_CKR_FUNCTION_REJECTED,
AWS_ERROR_IO_PINNED_EVENT_LOOP_MISMATCH,
AWS_ERROR_PKCS11_ENCODING_ERROR,
AWS_IO_TLS_ERROR_DEFAULT_TRUST_STORE_NOT_FOUND,
AWS_IO_STREAM_SEEK_FAILED,
AWS_IO_STREAM_GET_LENGTH_FAILED,
AWS_IO_STREAM_SEEK_UNSUPPORTED,
AWS_IO_STREAM_GET_LENGTH_UNSUPPORTED,
AWS_IO_TLS_ERROR_READ_FAILURE,
AWS_ERROR_PEM_MALFORMED,
AWS_IO_ERROR_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_IO_PACKAGE_ID),
AWS_IO_INVALID_FILE_HANDLE = AWS_ERROR_INVALID_FILE_HANDLE,
};
AWS_EXTERN_C_BEGIN
/**
* Initializes internal datastructures used by aws-c-io.
* Must be called before using any functionality in aws-c-io.
*/
AWS_IO_API
void aws_io_library_init(struct aws_allocator *allocator);
/**
* Shuts down the internal datastructures used by aws-c-io.
*/
AWS_IO_API
void aws_io_library_clean_up(void);
AWS_IO_API
void aws_io_fatal_assert_library_initialized(void);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_IO_H */

View File

@@ -0,0 +1,40 @@
#ifndef AWS_IO_LOGGING_H
#define AWS_IO_LOGGING_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/io/io.h>
#include <aws/common/logging.h>
AWS_PUSH_SANE_WARNING_LEVEL
struct aws_log_channel;
struct aws_log_formatter;
struct aws_log_writer;
enum aws_io_log_subject {
AWS_LS_IO_GENERAL = AWS_LOG_SUBJECT_BEGIN_RANGE(AWS_C_IO_PACKAGE_ID),
AWS_LS_IO_EVENT_LOOP,
AWS_LS_IO_SOCKET,
AWS_LS_IO_SOCKET_HANDLER,
AWS_LS_IO_TLS,
AWS_LS_IO_ALPN,
AWS_LS_IO_DNS,
AWS_LS_IO_PKI,
AWS_LS_IO_CHANNEL,
AWS_LS_IO_CHANNEL_BOOTSTRAP,
AWS_LS_IO_FILE_UTILS,
AWS_LS_IO_SHARED_LIBRARY,
AWS_LS_IO_EXPONENTIAL_BACKOFF_RETRY_STRATEGY,
AWS_LS_IO_STANDARD_RETRY_STRATEGY,
AWS_LS_IO_PKCS11,
AWS_LS_IO_PEM,
AWS_IO_LS_LAST = AWS_LOG_SUBJECT_END_RANGE(AWS_C_IO_PACKAGE_ID)
};
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_LOGGING_H */

View File

@@ -0,0 +1,89 @@
#ifndef AWS_IO_MESSAGE_POOL_H
#define AWS_IO_MESSAGE_POOL_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/array_list.h>
#include <aws/io/io.h>
AWS_PUSH_SANE_WARNING_LEVEL
struct aws_memory_pool {
struct aws_allocator *alloc;
struct aws_array_list stack;
uint16_t ideal_segment_count;
size_t segment_size;
void *data_ptr;
};
struct aws_message_pool {
struct aws_allocator *alloc;
struct aws_memory_pool application_data_pool;
struct aws_memory_pool small_block_pool;
};
struct aws_message_pool_creation_args {
size_t application_data_msg_data_size;
uint8_t application_data_msg_count;
size_t small_block_msg_data_size;
uint8_t small_block_msg_count;
};
AWS_EXTERN_C_BEGIN
AWS_IO_API
int aws_memory_pool_init(
struct aws_memory_pool *mempool,
struct aws_allocator *alloc,
uint16_t ideal_segment_count,
size_t segment_size);
AWS_IO_API
void aws_memory_pool_clean_up(struct aws_memory_pool *mempool);
/**
* Acquires memory from the pool if available, otherwise, it attempts to allocate and returns the result.
*/
AWS_IO_API
void *aws_memory_pool_acquire(struct aws_memory_pool *mempool);
/**
* Releases memory to the pool if space is available, otherwise frees `to_release`
*/
AWS_IO_API
void aws_memory_pool_release(struct aws_memory_pool *mempool, void *to_release);
/**
* Initializes message pool using 'msg_pool' as the backing pool, 'args' is copied.
*/
AWS_IO_API
int aws_message_pool_init(
struct aws_message_pool *msg_pool,
struct aws_allocator *alloc,
struct aws_message_pool_creation_args *args);
AWS_IO_API
void aws_message_pool_clean_up(struct aws_message_pool *msg_pool);
/**
* Acquires a message from the pool if available, otherwise, it attempts to allocate. If a message is acquired,
* note that size_hint is just a hint. the return value's capacity will be set to the actual buffer size.
*/
AWS_IO_API
struct aws_io_message *aws_message_pool_acquire(
struct aws_message_pool *msg_pool,
enum aws_io_message_type message_type,
size_t size_hint);
/**
* Releases message to the pool if space is available, otherwise frees `message`
* @param message
*/
AWS_IO_API
void aws_message_pool_release(struct aws_message_pool *msg_pool, struct aws_io_message *message);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_MESSAGE_POOL_H */

View File

@@ -0,0 +1,99 @@
#ifndef AWS_IO_PEM_H
#define AWS_IO_PEM_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/io/io.h>
AWS_EXTERN_C_BEGIN
/*
* Naming follows OpenSSL convention for PEM types.
* Refer to comment after each enum value for the type string it represents.
*/
enum aws_pem_object_type {
AWS_PEM_TYPE_UNKNOWN = 0,
AWS_PEM_TYPE_X509_OLD, /* X509 CERTIFICATE */
AWS_PEM_TYPE_X509, /* CERTIFICATE */
AWS_PEM_TYPE_X509_TRUSTED, /* TRUSTED CERTIFICATE */
AWS_PEM_TYPE_X509_REQ_OLD, /* NEW CERTIFICATE REQUEST */
AWS_PEM_TYPE_X509_REQ, /* CERTIFICATE REQUEST */
AWS_PEM_TYPE_X509_CRL, /* X509 CRL */
AWS_PEM_TYPE_EVP_PKEY, /* ANY PRIVATE KEY */
AWS_PEM_TYPE_PUBLIC_PKCS8, /* PUBLIC KEY */
AWS_PEM_TYPE_PRIVATE_RSA_PKCS1, /* RSA PRIVATE KEY */
AWS_PEM_TYPE_PUBLIC_RSA_PKCS1, /* RSA PUBLIC KEY */
AWS_PEM_TYPE_PRIVATE_DSA_PKCS1, /* RSA PRIVATE KEY */
AWS_PEM_TYPE_PUBLIC_DSA_PKCS1, /* RSA PUBLIC KEY */
AWS_PEM_TYPE_PKCS7, /* PKCS7 */
AWS_PEM_TYPE_PKCS7_SIGNED_DATA, /* PKCS #7 SIGNED DATA */
AWS_PEM_TYPE_PRIVATE_PKCS8_ENCRYPTED, /* ENCRYPTED PRIVATE KEY */
AWS_PEM_TYPE_PRIVATE_PKCS8, /* PRIVATE KEY */
AWS_PEM_TYPE_DH_PARAMETERS, /* X9.42 DH PARAMETERS */
AWS_PEM_TYPE_DH_PARAMETERS_X942, /* X9.42 DH PARAMETERS */
AWS_PEM_TYPE_SSL_SESSION_PARAMETERS, /* SSL SESSION PARAMETERS */
AWS_PEM_TYPE_DSA_PARAMETERS, /* DSA PARAMETERS */
AWS_PEM_TYPE_ECDSA_PUBLIC, /* ECDSA PUBLIC KEY */
AWS_PEM_TYPE_EC_PARAMETERS, /* EC PARAMETERS */
AWS_PEM_TYPE_EC_PRIVATE, /* EC PRIVATE KEY */
AWS_PEM_TYPE_PARAMETERS, /* PARAMETERS */
AWS_PEM_TYPE_CMS, /* CMS */
AWS_PEM_TYPE_SM2_PARAMETERS /* SM2 PARAMETERS */
};
/*
* Describes PEM object decoded from file.
* data points to raw data bytes of object (decoding will do additional base 64
* decoding for each object).
* type will be set to object type or to AWS_PEM_TYPE_UNKNOWN if it could not
* figure out type.
* type_string is the string between -----BEGIN and -----
*/
struct aws_pem_object {
enum aws_pem_object_type type;
struct aws_string *type_string;
struct aws_byte_buf data;
};
/**
* Cleans up elements of pem_objects list 'aws_pem_objects_init_from_file_contents()'
* and 'aws_pem_objects_init_from_file_path()'.
*/
AWS_IO_API void aws_pem_objects_clean_up(struct aws_array_list *pem_objects);
/**
* Decodes PEM data and reads objects sequentially adding them to pem_objects.
* If it comes across an object it cannot read, list of all object read until
* that point is returned.
* If no objects can be read from PEM or objects could not be base 64 decoded,
* AWS_ERROR_PEM_MALFORMED is raised.
* out_pem_objects stores aws_pem_object struct by value.
* Function will initialize pem_objects list.
* This code is slow, and it allocates, so please try
* not to call this in the middle of something that needs to be fast or resource sensitive.
*/
AWS_IO_API int aws_pem_objects_init_from_file_contents(
struct aws_array_list *pem_objects,
struct aws_allocator *alloc,
struct aws_byte_cursor pem_cursor);
/**
* Decodes PEM data from file and reads objects sequentially adding them to pem_objects.
* If it comes across an object it cannot read, list of all object read until
* that point is returned.
* If no objects can be read from PEM or objects could not be base 64 decoded,
* AWS_ERROR_PEM_MALFORMED is raised.
* out_pem_objects stores aws_pem_object struct by value.
* Function will initialize pem_objects list.
* This code is slow, and it allocates, so please try
* not to call this in the middle of something that needs to be fast or resource sensitive.
*/
AWS_IO_API int aws_pem_objects_init_from_file_path(
struct aws_array_list *pem_objects,
struct aws_allocator *allocator,
const char *filename);
AWS_EXTERN_C_END
#endif /* AWS_IO_PEM_H */

View File

@@ -0,0 +1,151 @@
#ifndef AWS_IO_PIPE_H
#define AWS_IO_PIPE_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/byte_buf.h>
#include <aws/io/io.h>
AWS_PUSH_SANE_WARNING_LEVEL
struct aws_event_loop;
struct aws_pipe_read_end {
void *impl_data;
};
struct aws_pipe_write_end {
void *impl_data;
};
/**
* Callback for when the pipe is readable (edge-triggered), or an error has occurred.
* Afer subscribing, the callback is invoked when the pipe has data to read, or the pipe has an error.
* The readable callback is invoked again any time the user reads all data, and then more data arrives.
* Note that it will not be invoked again if the pipe still has unread data when more data arrives.
* `error_code` of AWS_ERROR_SUCCESS indicates a readable event, and otherwise contains the value of the error.
* `user_data` corresponds to the `user_data` passed into aws_pipe_subscribe_to_read_events().
* This callback is always invoked on the read-end's event-loop thread.
*/
typedef void(aws_pipe_on_readable_fn)(struct aws_pipe_read_end *read_end, int error_code, void *user_data);
/**
* Callback for when the asynchronous aws_pipe_write() operation has either completed or failed.
* `write_end` will be NULL if this callback is invoked after the the write-end has been cleaned up,
* this does not necessarily mean that the write operation failed.
* `error_code` will be AWS_ERROR_SUCCESS if all data was written, or a code corresponding to the error.
* `src_buffer` corresponds to the buffer passed into aws_pipe_write()
* `user_data` corresponds to the `user_data` passed into aws_pipe_write().
* This callback is always invoked on the write-end's event-loop thread.
*/
typedef void(aws_pipe_on_write_completed_fn)(
struct aws_pipe_write_end *write_end,
int error_code,
struct aws_byte_cursor src_buffer,
void *user_data);
AWS_EXTERN_C_BEGIN
/**
* Opens an OS specific bidirectional pipe.
* The read direction is stored in read_end. Write direction is stored in write_end.
* Each end must be connected to an event-loop, and further calls to each end must happen on that event-loop's thread.
*/
AWS_IO_API
int aws_pipe_init(
struct aws_pipe_read_end *read_end,
struct aws_event_loop *read_end_event_loop,
struct aws_pipe_write_end *write_end,
struct aws_event_loop *write_end_event_loop,
struct aws_allocator *allocator);
/**
* Clean up the read-end of the pipe.
* This must be called on the thread of the connected event-loop.
*/
AWS_IO_API
int aws_pipe_clean_up_read_end(struct aws_pipe_read_end *read_end);
/**
* Clean up the write-end of the pipe.
* This must be called on the thread of the connected event-loop.
*/
AWS_IO_API
int aws_pipe_clean_up_write_end(struct aws_pipe_write_end *write_end);
/**
* Get the event-loop connected to the read-end of the pipe.
* This may be called on any thread.
*/
AWS_IO_API
struct aws_event_loop *aws_pipe_get_read_end_event_loop(const struct aws_pipe_read_end *read_end);
/**
* Get the event-loop connected to the write-end of the pipe.
* This may be called on any thread.
*/
AWS_IO_API
struct aws_event_loop *aws_pipe_get_write_end_event_loop(const struct aws_pipe_write_end *write_end);
/**
* Initiates an asynchrous write from the source buffer to the pipe.
* The data referenced by `src_buffer` must remain in memory until the operation completes.
* `on_complete` is called on the event-loop thread when the operation has either completed or failed.
* The callback's pipe argument will be NULL if the callback is invoked after the pipe has been cleaned up.
* This must be called on the thread of the connected event-loop.
*/
AWS_IO_API
int aws_pipe_write(
struct aws_pipe_write_end *write_end,
struct aws_byte_cursor src_buffer,
aws_pipe_on_write_completed_fn *on_completed,
void *user_data);
/**
* Read data from the pipe into the destination buffer.
* Attempts to read enough to fill all remaining space in the buffer, from `dst_buffer->len` to `dst_buffer->capacity`.
* `dst_buffer->len` is updated to reflect the buffer's new length.
* `num_bytes_read` (optional) is set to the total number of bytes read.
* This function never blocks. If no bytes could be read without blocking, then AWS_OP_ERR is returned and
* aws_last_error() code will be AWS_IO_READ_WOULD_BLOCK.
* This must be called on the thread of the connected event-loop.
*/
AWS_IO_API
int aws_pipe_read(struct aws_pipe_read_end *read_end, struct aws_byte_buf *dst_buffer, size_t *num_bytes_read);
/**
* Subscribe to be notified when the pipe becomes readable (edge-triggered), or an error occurs.
* `on_readable` is invoked on the event-loop's thread when the pipe has data to read, or the pipe has an error.
* `on_readable` is invoked again any time the user reads all data, and then more data arrives.
* Note that it will not be invoked again if the pipe still has unread data when more data arrives.
* This must be called on the thread of the connected event-loop.
*/
AWS_IO_API
int aws_pipe_subscribe_to_readable_events(
struct aws_pipe_read_end *read_end,
aws_pipe_on_readable_fn *on_readable,
void *user_data);
/**
* Stop receiving notifications about events on the read-end of the pipe.
* This must be called on the thread of the connected event-loop.
*/
AWS_IO_API
int aws_pipe_unsubscribe_from_readable_events(struct aws_pipe_read_end *read_end);
#if defined(_WIN32)
/**
* Generate a unique pipe name.
* The suggested dst_size is 256.
*/
AWS_IO_API
int aws_pipe_get_unique_name(char *dst, size_t dst_size);
#endif
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_PIPE_H */

View File

@@ -0,0 +1,97 @@
#ifndef AWS_IO_PKCS11_H
#define AWS_IO_PKCS11_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/io/io.h>
AWS_PUSH_SANE_WARNING_LEVEL
struct aws_allocator;
/**
* Handle to a loaded PKCS#11 library.
*/
struct aws_pkcs11_lib;
/**
* Controls how aws_pkcs11_lib calls C_Initialize() and C_Finalize() on the PKCS#11 library.
*/
enum aws_pkcs11_lib_behavior {
/**
* Default behavior that accommodates most use cases.
* C_Initialize() is called on creation, and "already-initialized" errors are ignored.
* C_Finalize() is never called, just in case another part of your
* application is still using the PKCS#11 library.
*/
AWS_PKCS11_LIB_DEFAULT_BEHAVIOR,
/**
* Skip calling C_Initialize() and C_Finalize().
* Use this if your application has already initialized the PKCS#11 library,
* and you do not want C_Initialize() called again.
*/
AWS_PKCS11_LIB_OMIT_INITIALIZE,
/**
* C_Initialize() is called on creation and C_Finalize() is called on cleanup.
* If C_Initialize() reports that's it's already initialized, this is treated as an error.
* Use this if you need perfect cleanup (ex: running valgrind with --leak-check).
*/
AWS_PKCS11_LIB_STRICT_INITIALIZE_FINALIZE,
};
/* The enum above was misspelled, and later got fixed (pcks11 -> pkcs11).
* This macro maintain backwards compatibility with the old spelling */
#define aws_pcks11_lib_behavior aws_pkcs11_lib_behavior
/**
* Options for aws_pkcs11_lib_new()
*/
struct aws_pkcs11_lib_options {
/**
* Name of PKCS#11 library file to load (UTF-8).
* Zero out if your application is compiled with PKCS#11 symbols linked in.
*/
struct aws_byte_cursor filename;
/**
* Behavior for calling C_Initialize() and C_Finalize() on the PKCS#11 library.
*/
enum aws_pkcs11_lib_behavior initialize_finalize_behavior;
};
AWS_EXTERN_C_BEGIN
/**
* Load and initialize a PKCS#11 library.
* See `aws_pkcs11_lib_options` for options.
*
* If successful a valid pointer is returned. You must call aws_pkcs11_lib_release() when you are done with it.
* If unsuccessful, NULL is returned and an error is set.
*/
AWS_IO_API
struct aws_pkcs11_lib *aws_pkcs11_lib_new(
struct aws_allocator *allocator,
const struct aws_pkcs11_lib_options *options);
/**
* Acquire a reference to a PKCS#11 library, preventing it from being cleaned up.
* You must call aws_pkcs11_lib_release() when you are done with it.
* This function returns whatever was passed in. It cannot fail.
*/
AWS_IO_API
struct aws_pkcs11_lib *aws_pkcs11_lib_acquire(struct aws_pkcs11_lib *pkcs11_lib);
/**
* Release a reference to the PKCS#11 library.
* When the last reference is released, the library is cleaned up.
*/
AWS_IO_API
void aws_pkcs11_lib_release(struct aws_pkcs11_lib *pkcs11_lib);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_PKCS11_H */

View File

@@ -0,0 +1,241 @@
#ifndef AWS_IO_CLIENT_RETRY_STRATEGY_H
#define AWS_IO_CLIENT_RETRY_STRATEGY_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/io/exports.h>
#include <aws/common/atomics.h>
#include <aws/common/byte_buf.h>
AWS_PUSH_SANE_WARNING_LEVEL
struct aws_retry_strategy;
struct aws_retry_token;
struct aws_event_loop_group;
/**
* Invoked upon the acquisition, or failure to acquire a retry token. This function will always be invoked if and only
* if aws_retry_strategy_acquire_retry_token() returns AWS_OP_SUCCESS. It will never be invoked synchronously from
* aws_retry_strategy_acquire_retry_token(). Token will always be NULL if error_code is non-zero, and vice-versa. If
* token is non-null, it will have a reference count of 1, and you must call aws_retry_token_release() on it later. See
* the comments for aws_retry_strategy_on_retry_ready_fn for more info.
*/
typedef void(aws_retry_strategy_on_retry_token_acquired_fn)(
struct aws_retry_strategy *retry_strategy,
int error_code,
struct aws_retry_token *token,
void *user_data);
/**
* Invoked after a successful call to aws_retry_strategy_schedule_retry(). This function will always be invoked if and
* only if aws_retry_strategy_schedule_retry() returns AWS_OP_SUCCESS. It will never be invoked synchronously from
* aws_retry_strategy_schedule_retry(). After attempting the operation, either call aws_retry_strategy_schedule_retry()
* with an aws_retry_error_type or call aws_retry_token_record_success() and then release the token via.
* aws_retry_token_release().
*/
typedef void(aws_retry_strategy_on_retry_ready_fn)(struct aws_retry_token *token, int error_code, void *user_data);
/**
* Optional function to supply your own generate random implementation
*/
typedef uint64_t(aws_generate_random_fn)(void *user_data);
enum aws_retry_error_type {
/** This is a connection level error such as a socket timeout, socket connect error, tls negotiation timeout etc...
* Typically these should never be applied for non-idempotent request types since in this scenario, it's impossible
* to know whether the operation had a side effect on the server. */
AWS_RETRY_ERROR_TYPE_TRANSIENT,
/** This is an error where the server explicitly told the client to back off, such as a 429 or 503 Http error. */
AWS_RETRY_ERROR_TYPE_THROTTLING,
/** This is a server error that isn't explicitly throttling but is considered by the client
* to be something that should be retried. */
AWS_RETRY_ERROR_TYPE_SERVER_ERROR,
/** Doesn't count against any budgets. This could be something like a 401 challenge in Http. */
AWS_RETRY_ERROR_TYPE_CLIENT_ERROR,
};
struct aws_retry_strategy_vtable {
void (*destroy)(struct aws_retry_strategy *retry_strategy);
int (*acquire_token)(
struct aws_retry_strategy *retry_strategy,
const struct aws_byte_cursor *partition_id,
aws_retry_strategy_on_retry_token_acquired_fn *on_acquired,
void *user_data,
uint64_t timeout_ms);
int (*schedule_retry)(
struct aws_retry_token *token,
enum aws_retry_error_type error_type,
aws_retry_strategy_on_retry_ready_fn *retry_ready,
void *user_data);
int (*record_success)(struct aws_retry_token *token);
void (*release_token)(struct aws_retry_token *token);
};
struct aws_retry_strategy {
struct aws_allocator *allocator;
struct aws_retry_strategy_vtable *vtable;
struct aws_atomic_var ref_count;
void *impl;
};
struct aws_retry_token {
struct aws_allocator *allocator;
struct aws_retry_strategy *retry_strategy;
struct aws_atomic_var ref_count;
void *impl;
};
/**
* Jitter mode for exponential backoff.
*
* For a great writeup on these options see:
* https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/
*/
enum aws_exponential_backoff_jitter_mode {
/* Uses AWS_EXPONENTIAL_BACKOFF_JITTER_FULL */
AWS_EXPONENTIAL_BACKOFF_JITTER_DEFAULT,
AWS_EXPONENTIAL_BACKOFF_JITTER_NONE,
AWS_EXPONENTIAL_BACKOFF_JITTER_FULL,
AWS_EXPONENTIAL_BACKOFF_JITTER_DECORRELATED,
};
/**
* Options for exponential backoff retry strategy. el_group must be set, any other option, if set to 0 will signify
* "use defaults"
*/
struct aws_exponential_backoff_retry_options {
/** Event loop group to use for scheduling tasks. */
struct aws_event_loop_group *el_group;
/** Max retries to allow. The default value is 10 */
size_t max_retries;
/** Scaling factor to add for the backoff. Default is 500ms */
uint32_t backoff_scale_factor_ms;
/** Max retry backoff in seconds. Default is 20 seconds */
uint32_t max_backoff_secs;
/** Jitter mode to use, see comments for aws_exponential_backoff_jitter_mode.
* Default is AWS_EXPONENTIAL_BACKOFF_JITTER_DEFAULT */
enum aws_exponential_backoff_jitter_mode jitter_mode;
/** Deprecated. Use generate_random_impl instead
* By default this will be set to use aws_device_random. If you want something else, set it here.
* */
uint64_t (*generate_random)(void);
/*
* By default this will be set to use aws_device_random. If you want something else, set it here.
*/
aws_generate_random_fn *generate_random_impl;
/**
* Optional user data for the generate random generate_random_impl.
*/
void *generate_random_user_data;
/**
* Optional shutdown callback that gets invoked, with appropriate user data,
* when the resources used by the retry_strategy are no longer in use.
*/
const struct aws_shutdown_callback_options *shutdown_options;
};
struct aws_standard_retry_options {
struct aws_exponential_backoff_retry_options backoff_retry_options;
/** capacity for partitions. Defaults to 500 */
size_t initial_bucket_capacity;
};
AWS_EXTERN_C_BEGIN
/**
* Acquire a reference count on retry_strategy.
*/
AWS_IO_API void aws_retry_strategy_acquire(struct aws_retry_strategy *retry_strategy);
/**
* Releases a reference count on retry_strategy.
*/
AWS_IO_API void aws_retry_strategy_release(struct aws_retry_strategy *retry_strategy);
/**
* Attempts to acquire a retry token for use with retries. On success, on_acquired will be invoked when a token is
* available, or an error will be returned if the timeout expires. partition_id identifies operations that should be
* grouped together. This allows for more sophisticated strategies such as AIMD and circuit breaker patterns. Pass NULL
* to use the global partition.
*/
AWS_IO_API int aws_retry_strategy_acquire_retry_token(
struct aws_retry_strategy *retry_strategy,
const struct aws_byte_cursor *partition_id,
aws_retry_strategy_on_retry_token_acquired_fn *on_acquired,
void *user_data,
uint64_t timeout_ms);
/**
* Schedules a retry based on the backoff and token based strategies. retry_ready is invoked when the retry is either
* ready for execution or if it has been canceled due to application shutdown.
*
* This function can return an error to reject the retry attempt if, for example, a circuit breaker has opened. If this
* occurs users should fail their calls back to their callers.
*
* error_type is used for book keeping. See the comments above for aws_retry_error_type.
*/
AWS_IO_API int aws_retry_strategy_schedule_retry(
struct aws_retry_token *token,
enum aws_retry_error_type error_type,
aws_retry_strategy_on_retry_ready_fn *retry_ready,
void *user_data);
/**
* Records a successful retry. This is used for making future decisions to open up token buckets, AIMD breakers etc...
* some strategies such as exponential backoff will ignore this, but you should always call it after a successful
* operation or your system will never recover during an outage.
*/
AWS_IO_API int aws_retry_token_record_success(struct aws_retry_token *token);
/**
* Increments reference count for token. This should be called any time you seat the token to a pointer you own.
*/
AWS_IO_API void aws_retry_token_acquire(struct aws_retry_token *token);
/**
* Releases the reference count for token. This should always be invoked after either calling
* aws_retry_strategy_schedule_retry() and failing, or after calling aws_retry_token_record_success().
*/
AWS_IO_API void aws_retry_token_release(struct aws_retry_token *token);
/**
* Creates a retry strategy using exponential backoff. This strategy does not perform any bookkeeping on error types and
* success. There is no circuit breaker functionality in here. See the comments above for
* aws_exponential_backoff_retry_options.
*/
AWS_IO_API struct aws_retry_strategy *aws_retry_strategy_new_exponential_backoff(
struct aws_allocator *allocator,
const struct aws_exponential_backoff_retry_options *config);
/**
* This is a retry implementation that cuts off traffic if it's
* detected that an endpoint partition is having availability
* problems. This is necessary to keep from making outages worse
* by scheduling work that's unlikely to succeed yet increases
* load on an already ailing system.
*
* We do this by creating a bucket for each partition. A partition
* is an arbitrary specifier. It can be anything: a region, a service,
* a combination of region and service, a literal dns name.... doesn't matter.
*
* Each bucket has a budget for maximum allowed retries. Different types of events
* carry different weights. Things that indicate an unhealthy partition such as
* transient errors (timeouts, unhealthy connection etc...) cost more.
* A retry for any other reason (service sending a 5xx response code) cost a bit less.
* When a retry is attempted this capacity is leased out to the retry. On success it is
* released back to the capacity pool. On failure, it remains leased.
* Operations that succeed without a retry slowly restore the capacity pool.
*
* If a partition runs out of capacity it is assumed unhealthy and retries will be blocked
* until capacity returns to the pool. To prevent a partition from staying unhealthy after
* an outage has recovered, new requests that succeed without a retry will increase the capacity
* slowly ( a new request gets a payback lease of 1, but the lease is never actually deducted from the capacity pool).
*/
AWS_IO_API struct aws_retry_strategy *aws_retry_strategy_new_standard(
struct aws_allocator *allocator,
const struct aws_standard_retry_options *config);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_CLIENT_RETRY_STRATEGY_H */

View File

@@ -0,0 +1,51 @@
#ifndef AWS_COMMON_SHARED_LIBRARY_H
#define AWS_COMMON_SHARED_LIBRARY_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/io/io.h>
AWS_PUSH_SANE_WARNING_LEVEL
struct aws_byte_cursor;
/*
* A simple platform wrapper for dynamically loading and examining shared libraries
*/
struct aws_shared_library {
void *library_handle;
};
typedef void (*aws_generic_function)(void);
AWS_EXTERN_C_BEGIN
/*
* Initializes a dynamically-loaded shared library from its file path location
*/
AWS_IO_API
int aws_shared_library_init(struct aws_shared_library *library, const char *library_path);
/*
* Closes a dynamically-loaded shared library
*/
AWS_IO_API
void aws_shared_library_clean_up(struct aws_shared_library *library);
/*
* Finds a function symbol within a shared library. function_address may be
* safely cast into any other function type as appropriate.
*/
AWS_IO_API
int aws_shared_library_find_function(
struct aws_shared_library *library,
const char *symbol_name,
aws_generic_function *function_address);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_COMMON_SHARED_LIBRARY_H */

View File

@@ -0,0 +1,348 @@
#ifndef AWS_IO_SOCKET_H
#define AWS_IO_SOCKET_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/io/channel.h>
#include <aws/io/io.h>
AWS_PUSH_SANE_WARNING_LEVEL
enum aws_socket_domain {
AWS_SOCKET_IPV4,
AWS_SOCKET_IPV6,
/* Unix domain sockets (or at least something like them) */
AWS_SOCKET_LOCAL,
/* VSOCK used in inter-VM communication */
AWS_SOCKET_VSOCK,
};
enum aws_socket_type {
/* A streaming socket sends reliable messages over a two-way connection.
* This means TCP when used with IPV4/6, and Unix domain sockets, when used with
* AWS_SOCKET_LOCAL*/
AWS_SOCKET_STREAM,
/* A datagram socket is connectionless and sends unreliable messages.
* This means UDP when used with IPV4/6.
* LOCAL and VSOCK sockets are not compatible with DGRAM.*/
AWS_SOCKET_DGRAM,
};
#define AWS_NETWORK_INTERFACE_NAME_MAX 16
struct aws_socket_options {
enum aws_socket_type type;
enum aws_socket_domain domain;
uint32_t connect_timeout_ms;
/* Keepalive properties are TCP only.
* Set keepalive true to periodically transmit messages for detecting a disconnected peer.
* If interval or timeout are zero, then default values are used. */
uint16_t keep_alive_interval_sec;
uint16_t keep_alive_timeout_sec;
/* If set, sets the number of keep alive probes allowed to fail before the connection is considered
* lost. If zero OS defaults are used. On Windows, this option is meaningless until Windows 10 1703.*/
uint16_t keep_alive_max_failed_probes;
bool keepalive;
/**
* THIS IS AN EXPERIMENTAL AND UNSTABLE API
* (Optional)
* This property is used to bind the socket to a particular network interface by name, such as eth0 and ens32.
* If this is empty, the socket will not be bound to any interface and will use OS defaults. If the provided name
* is invalid, `aws_socket_init()` will error out with AWS_IO_SOCKET_INVALID_OPTIONS. This option is only
* supported on Linux, macOS, and platforms that have either SO_BINDTODEVICE or IP_BOUND_IF. It is not supported on
* Windows. `AWS_ERROR_PLATFORM_NOT_SUPPORTED` will be raised on unsupported platforms.
*/
char network_interface_name[AWS_NETWORK_INTERFACE_NAME_MAX];
};
struct aws_socket;
struct aws_event_loop;
/**
* Called in client mode when an outgoing connection has succeeded or an error has occurred.
* If the connection was successful error_code will be AWS_ERROR_SUCCESS and the socket has already been assigned
* to the event loop specified in aws_socket_connect().
*
* If an error occurred error_code will be non-zero.
*/
typedef void(aws_socket_on_connection_result_fn)(struct aws_socket *socket, int error_code, void *user_data);
/**
* Called by a listening socket when either an incoming connection has been received or an error occurred.
*
* In the normal use-case, this function will be called multiple times over the lifetime of a single listening socket.
* new_socket is already connected and initialized, and is using the same options and allocator as the listening socket.
* A user may want to call aws_socket_set_options() on the new socket if different options are desired.
*
* new_socket is not yet assigned to an event-loop. The user should call aws_socket_assign_to_event_loop() before
* performing IO operations.
*
* When error_code is AWS_ERROR_SUCCESS, new_socket is the recently accepted connection.
* If error_code is non-zero, an error occurred and you should aws_socket_close() the socket.
*
* Do not call aws_socket_clean_up() from this callback.
*/
typedef void(aws_socket_on_accept_result_fn)(
struct aws_socket *socket,
int error_code,
struct aws_socket *new_socket,
void *user_data);
/**
* Callback for when the data passed to a call to aws_socket_write() has either completed or failed.
* On success, error_code will be AWS_ERROR_SUCCESS.
*/
typedef void(
aws_socket_on_write_completed_fn)(struct aws_socket *socket, int error_code, size_t bytes_written, void *user_data);
/**
* Callback for when socket is either readable (edge-triggered) or when an error has occurred. If the socket is
* readable, error_code will be AWS_ERROR_SUCCESS.
*/
typedef void(aws_socket_on_readable_fn)(struct aws_socket *socket, int error_code, void *user_data);
#ifdef _WIN32
# define AWS_ADDRESS_MAX_LEN 256
#else
# include <sys/un.h>
# define AWS_ADDRESS_MAX_LEN sizeof(((struct sockaddr_un *)0)->sun_path)
#endif
struct aws_socket_endpoint {
char address[AWS_ADDRESS_MAX_LEN];
uint32_t port;
};
struct aws_socket {
struct aws_allocator *allocator;
struct aws_socket_endpoint local_endpoint;
struct aws_socket_endpoint remote_endpoint;
struct aws_socket_options options;
struct aws_io_handle io_handle;
struct aws_event_loop *event_loop;
struct aws_channel_handler *handler;
int state;
aws_socket_on_readable_fn *readable_fn;
void *readable_user_data;
aws_socket_on_connection_result_fn *connection_result_fn;
aws_socket_on_accept_result_fn *accept_result_fn;
void *connect_accept_user_data;
void *impl;
};
struct aws_byte_buf;
struct aws_byte_cursor;
/* These are hacks for working around headers and functions we need for IO work but aren't directly includable or
linkable. these are purposely not exported. These functions only get called internally. The awkward aws_ prefixes are
just in case someone includes this header somewhere they were able to get these definitions included. */
#ifdef _WIN32
typedef void (*aws_ms_fn_ptr)(void);
void aws_check_and_init_winsock(void);
aws_ms_fn_ptr aws_winsock_get_connectex_fn(void);
aws_ms_fn_ptr aws_winsock_get_acceptex_fn(void);
#endif
AWS_EXTERN_C_BEGIN
/**
* Initializes a socket object with socket options. options will be copied.
*/
AWS_IO_API int aws_socket_init(
struct aws_socket *socket,
struct aws_allocator *alloc,
const struct aws_socket_options *options);
/**
* Shuts down any pending operations on the socket, and cleans up state. The socket object can be re-initialized after
* this operation. This function calls aws_socket_close. If you have not already called aws_socket_close() on the
* socket, all of the rules for aws_socket_close() apply here. In this case it will not fail if you use the function
* improperly, but on some platforms you will certainly leak memory.
*
* If the socket has already been closed, you can safely, call this from any thread.
*/
AWS_IO_API void aws_socket_clean_up(struct aws_socket *socket);
/**
* Connects to a remote endpoint. In UDP, this simply binds the socket to a remote address for use with
* `aws_socket_write()`, and if the operation is successful, the socket can immediately be used for write operations.
*
* In TCP, LOCAL and VSOCK this function will not block. If the return value is successful, then you must wait on the
* `on_connection_result()` callback to be invoked before using the socket.
*
* If an event_loop is provided for UDP sockets, a notification will be sent on
* on_connection_result in the event-loop's thread. Upon completion, the socket will already be assigned
* an event loop. If NULL is passed for UDP, it will immediately return upon success, but you must call
* aws_socket_assign_to_event_loop before use.
*/
AWS_IO_API int aws_socket_connect(
struct aws_socket *socket,
const struct aws_socket_endpoint *remote_endpoint,
struct aws_event_loop *event_loop,
aws_socket_on_connection_result_fn *on_connection_result,
void *user_data);
/**
* Binds the socket to a local address. In UDP mode, the socket is ready for `aws_socket_read()` operations. In
* connection oriented modes, you still must call `aws_socket_listen()` and `aws_socket_start_accept()` before using the
* socket. local_endpoint is copied.
*/
AWS_IO_API int aws_socket_bind(struct aws_socket *socket, const struct aws_socket_endpoint *local_endpoint);
/**
* Get the local address which the socket is bound to.
* Raises an error if no address is bound.
*/
AWS_IO_API int aws_socket_get_bound_address(const struct aws_socket *socket, struct aws_socket_endpoint *out_address);
/**
* TCP, LOCAL and VSOCK only. Sets up the socket to listen on the address bound to in `aws_socket_bind()`.
*/
AWS_IO_API int aws_socket_listen(struct aws_socket *socket, int backlog_size);
/**
* TCP, LOCAL and VSOCK only. The socket will begin accepting new connections. This is an asynchronous operation. New
* connections or errors will arrive via the `on_accept_result` callback.
*
* aws_socket_bind() and aws_socket_listen() must be called before calling this function.
*/
AWS_IO_API int aws_socket_start_accept(
struct aws_socket *socket,
struct aws_event_loop *accept_loop,
aws_socket_on_accept_result_fn *on_accept_result,
void *user_data);
/**
* TCP, LOCAL and VSOCK only. The listening socket will stop accepting new connections.
* It is safe to call `aws_socket_start_accept()` again after
* this operation. This can be called from any thread but be aware,
* on some platforms, if you call this from outside of the current event loop's thread, it will block
* until the event loop finishes processing the request for unsubscribe in it's own thread.
*/
AWS_IO_API int aws_socket_stop_accept(struct aws_socket *socket);
/**
* Calls `close()` on the socket and unregisters all io operations from the event loop. This function must be called
* from the event-loop's thread unless this is a listening socket. If it's a listening socket it can be called from any
* non-event-loop thread or the event-loop the socket is currently assigned to. If called from outside the event-loop,
* this function will block waiting on the socket to close. If this is called from an event-loop thread other than
* the one it's assigned to, it presents the possibility of a deadlock, so don't do it.
*/
AWS_IO_API int aws_socket_close(struct aws_socket *socket);
/**
* Calls `shutdown()` on the socket based on direction.
*/
AWS_IO_API int aws_socket_shutdown_dir(struct aws_socket *socket, enum aws_channel_direction dir);
/**
* Sets new socket options on the underlying socket. This is mainly useful in context of accepting a new connection via:
* `on_incoming_connection()`. options is copied.
*/
AWS_IO_API int aws_socket_set_options(struct aws_socket *socket, const struct aws_socket_options *options);
/**
* Assigns the socket to the event-loop. The socket will begin receiving read/write/error notifications after this call.
*
* Note: If you called connect for TCP or Unix Domain Sockets and received a connection_success callback, this has
* already happened. You only need to call this function when:
*
* a.) This socket is a server socket (e.g. a result of a call to start_accept())
* b.) This socket is a UDP socket.
*/
AWS_IO_API int aws_socket_assign_to_event_loop(struct aws_socket *socket, struct aws_event_loop *event_loop);
/**
* Gets the event-loop the socket is assigned to.
*/
AWS_IO_API struct aws_event_loop *aws_socket_get_event_loop(struct aws_socket *socket);
/**
* Subscribes on_readable to notifications when the socket goes readable (edge-triggered). Errors will also be recieved
* in the callback.
*
* Note! This function is technically not thread safe, but we do not enforce which thread you call from.
* It's your responsibility to either call this in safely (e.g. just don't call it in parallel from multiple threads) or
* schedule a task to call it. If you call it before your first call to read, it will be fine.
*/
AWS_IO_API int aws_socket_subscribe_to_readable_events(
struct aws_socket *socket,
aws_socket_on_readable_fn *on_readable,
void *user_data);
/**
* Reads from the socket. This call is non-blocking and will return `AWS_IO_SOCKET_READ_WOULD_BLOCK` if no data is
* available. `read` is the amount of data read into `buffer`.
*
* Attempts to read enough to fill all remaining space in the buffer, from `buffer->len` to `buffer->capacity`.
* `buffer->len` is updated to reflect the buffer's new length.
*
*
* Use aws_socket_subscribe_to_readable_events() to receive notifications of when the socket goes readable.
*
* NOTE! This function must be called from the event-loop used in aws_socket_assign_to_event_loop
*/
AWS_IO_API int aws_socket_read(struct aws_socket *socket, struct aws_byte_buf *buffer, size_t *amount_read);
/**
* Writes to the socket. This call is non-blocking and will attempt to write as much as it can, but will queue any
* remaining portion of the data for write when available. written_fn will be invoked once the entire cursor has been
* written, or the write failed or was cancelled.
*
* NOTE! This function must be called from the event-loop used in aws_socket_assign_to_event_loop
*
* For client sockets, connect() and aws_socket_assign_to_event_loop() must be called before calling this.
*
* For incoming sockets from a listener, aws_socket_assign_to_event_loop() must be called first.
*/
AWS_IO_API int aws_socket_write(
struct aws_socket *socket,
const struct aws_byte_cursor *cursor,
aws_socket_on_write_completed_fn *written_fn,
void *user_data);
/**
* Gets the latest error from the socket. If no error has occurred AWS_OP_SUCCESS will be returned. This function does
* not raise any errors to the installed error handlers.
*/
AWS_IO_API int aws_socket_get_error(struct aws_socket *socket);
/**
* Returns true if the socket is still open (doesn't mean connected or listening, only that it hasn't had close()
* called.
*/
AWS_IO_API bool aws_socket_is_open(struct aws_socket *socket);
/**
* Raises AWS_IO_SOCKET_INVALID_ADDRESS and logs an error if connecting to this port is illegal.
* For example, port must be in range 1-65535 to connect with IPv4.
* These port values would fail eventually in aws_socket_connect(),
* but you can use this function to validate earlier.
*/
AWS_IO_API int aws_socket_validate_port_for_connect(uint32_t port, enum aws_socket_domain domain);
/**
* Raises AWS_IO_SOCKET_INVALID_ADDRESS and logs an error if binding to this port is illegal.
* For example, port must in range 0-65535 to bind with IPv4.
* These port values would fail eventually in aws_socket_bind(),
* but you can use this function to validate earlier.
*/
AWS_IO_API int aws_socket_validate_port_for_bind(uint32_t port, enum aws_socket_domain domain);
/**
* Assigns a random address (UUID) for use with AWS_SOCKET_LOCAL (Unix Domain Sockets).
* For use in internal tests only.
*/
AWS_IO_API void aws_socket_endpoint_init_local_address_for_test(struct aws_socket_endpoint *endpoint);
/**
* Validates whether the network interface name is valid. On Windows, it will always return false since we don't support
* network_interface_name on Windows */
AWS_IO_API bool aws_is_network_interface_name_valid(const char *interface_name);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_SOCKET_H */

View File

@@ -0,0 +1,35 @@
#ifndef AWS_IO_SOCKET_CHANNEL_HANDLER_H
#define AWS_IO_SOCKET_CHANNEL_HANDLER_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/io/io.h>
AWS_PUSH_SANE_WARNING_LEVEL
struct aws_socket;
struct aws_channel_handler;
struct aws_channel_slot;
struct aws_event_loop;
AWS_EXTERN_C_BEGIN
/**
* Socket handlers should be the first slot/handler in a channel. It interacts directly with the channel's event loop
* for read and write notifications. max_read_size is the maximum amount of data it will read from the socket
* before a context switch (a continuation task will be scheduled).
*/
AWS_IO_API struct aws_channel_handler *aws_socket_handler_new(
struct aws_allocator *allocator,
struct aws_socket *socket,
struct aws_channel_slot *slot,
size_t max_read_size);
/* Get aws_socket from socket channel handler */
AWS_IO_API const struct aws_socket *aws_socket_handler_get_socket(const struct aws_channel_handler *handler);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_SOCKET_CHANNEL_HANDLER_H */

View File

@@ -0,0 +1,81 @@
#ifndef AWS_IO_STATISTICS_H
#define AWS_IO_STATISTICS_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/io/io.h>
#include <aws/common/statistics.h>
#include <aws/io/tls_channel_handler.h>
AWS_PUSH_SANE_WARNING_LEVEL
enum aws_crt_io_statistics_category {
AWSCRT_STAT_CAT_SOCKET = AWS_CRT_STATISTICS_CATEGORY_BEGIN_RANGE(AWS_C_IO_PACKAGE_ID),
AWSCRT_STAT_CAT_TLS,
};
/**
* Socket channel handler statistics record
*/
struct aws_crt_statistics_socket {
aws_crt_statistics_category_t category;
uint64_t bytes_read;
uint64_t bytes_written;
};
/**
* Tls channel handler statistics record
*/
struct aws_crt_statistics_tls {
aws_crt_statistics_category_t category;
uint64_t handshake_start_ns;
uint64_t handshake_end_ns;
enum aws_tls_negotiation_status handshake_status;
};
AWS_EXTERN_C_BEGIN
/**
* Initializes socket channel handler statistics
*/
AWS_IO_API
int aws_crt_statistics_socket_init(struct aws_crt_statistics_socket *stats);
/**
* Cleans up socket channel handler statistics
*/
AWS_IO_API
void aws_crt_statistics_socket_cleanup(struct aws_crt_statistics_socket *stats);
/**
* Resets socket channel handler statistics for the next gather interval. Calculate-once results are left alone.
*/
AWS_IO_API
void aws_crt_statistics_socket_reset(struct aws_crt_statistics_socket *stats);
/**
* Initializes tls channel handler statistics
*/
AWS_IO_API
int aws_crt_statistics_tls_init(struct aws_crt_statistics_tls *stats);
/**
* Cleans up tls channel handler statistics
*/
AWS_IO_API
void aws_crt_statistics_tls_cleanup(struct aws_crt_statistics_tls *stats);
/**
* Resets tls channel handler statistics for the next gather interval. Calculate-once results are left alone.
*/
AWS_IO_API
void aws_crt_statistics_tls_reset(struct aws_crt_statistics_tls *stats);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_STATISTICS_H */

View File

@@ -0,0 +1,136 @@
#ifndef AWS_IO_STREAM_H
#define AWS_IO_STREAM_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/ref_count.h>
#include <aws/io/io.h>
AWS_PUSH_SANE_WARNING_LEVEL
struct aws_input_stream;
struct aws_byte_buf;
/*
* For seek calls, where in the stream to seek from.
* CUR support can come later
* Intentionally mirror libc constants
*/
enum aws_stream_seek_basis { AWS_SSB_BEGIN = 0, AWS_SSB_END = 2 };
struct aws_stream_status {
bool is_end_of_stream;
bool is_valid;
};
struct aws_input_stream_vtable {
int (*seek)(struct aws_input_stream *stream, int64_t offset, enum aws_stream_seek_basis basis);
/**
* Stream as much data as will fit into the destination buffer and update its length.
* The destination buffer's capacity MUST NOT be changed.
*
* Return AWS_OP_SUCCESS if the read is successful.
* If AWS_OP_ERR is returned, the stream is assumed to be invalid and any data written to the buffer is ignored.
*
* If no more data is currently available, or the end of the stream has been reached, simply return AWS_OP_SUCCESS
* without touching the destination buffer.
*/
int (*read)(struct aws_input_stream *stream, struct aws_byte_buf *dest);
int (*get_status)(struct aws_input_stream *stream, struct aws_stream_status *status);
int (*get_length)(struct aws_input_stream *stream, int64_t *out_length);
/**
* Optional.
* If not set, the default aws_ref_count_acquire/release will be used.
* Set for high level language binding that has its own refcounting implementation and needs to be kept alive from
* C.
* If set, ref_count member will not be used.
*/
void (*acquire)(struct aws_input_stream *stream);
void (*release)(struct aws_input_stream *stream);
};
/**
* Base class for input streams.
* Note: when you implement one input stream, the ref_count needs to be initialized to clean up the resource when
* reaches to zero.
*/
struct aws_input_stream {
/* point to the impl only set if needed. */
void *impl;
const struct aws_input_stream_vtable *vtable;
struct aws_ref_count ref_count;
};
AWS_EXTERN_C_BEGIN
/**
* Increments the reference count on the input stream, allowing the caller to take a reference to it.
*
* Returns the same input stream passed in.
*/
AWS_IO_API struct aws_input_stream *aws_input_stream_acquire(struct aws_input_stream *stream);
/**
* Decrements a input stream's ref count. When the ref count drops to zero, the input stream will be destroyed.
*
* Returns NULL always.
*/
AWS_IO_API struct aws_input_stream *aws_input_stream_release(struct aws_input_stream *stream);
/*
* Seek to a position within a stream; analagous to fseek() and its relatives
*/
AWS_IO_API int aws_input_stream_seek(struct aws_input_stream *stream, int64_t offset, enum aws_stream_seek_basis basis);
/*
* Read data from a stream. If data is available, will read up to the (capacity - len) open bytes
* in the destination buffer. If AWS_OP_ERR is returned, the destination buffer will be unchanged.
*/
AWS_IO_API int aws_input_stream_read(struct aws_input_stream *stream, struct aws_byte_buf *dest);
/*
* Queries miscellaneous properties of the stream
*/
AWS_IO_API int aws_input_stream_get_status(struct aws_input_stream *stream, struct aws_stream_status *status);
/*
* Returns the total stream length, if able, regardless of current stream position. Under certain conditions,
* a valid stream may return an error instead when there is not a good answer (socket stream, for example).
*
*/
AWS_IO_API int aws_input_stream_get_length(struct aws_input_stream *stream, int64_t *out_length);
/* DEPRECATED
* Tears down the stream. Equivalent to aws_input_stream_release()
*/
AWS_IO_API void aws_input_stream_destroy(struct aws_input_stream *stream);
/*
* Creates a stream that operates on a range of bytes
*/
AWS_IO_API struct aws_input_stream *aws_input_stream_new_from_cursor(
struct aws_allocator *allocator,
const struct aws_byte_cursor *cursor);
/*
* Creates a stream that operates on a (not-yet-opened) file.
* Destruction closes the file.
*/
AWS_IO_API struct aws_input_stream *aws_input_stream_new_from_file(
struct aws_allocator *allocator,
const char *file_name);
/*
* Creates an input stream that reads from an already opened file.
* Destruction does not close the file.
*/
AWS_IO_API struct aws_input_stream *aws_input_stream_new_from_open_file(struct aws_allocator *allocator, FILE *file);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_STREAM_H */

View File

@@ -0,0 +1,909 @@
#ifndef AWS_IO_TLS_CHANNEL_HANDLER_H
#define AWS_IO_TLS_CHANNEL_HANDLER_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/common/byte_buf.h>
#include <aws/common/ref_count.h>
#include <aws/io/io.h>
AWS_PUSH_SANE_WARNING_LEVEL
#define AWS_TLS_NEGOTIATED_PROTOCOL_MESSAGE 0x01
struct aws_channel_slot;
struct aws_channel_handler;
struct aws_pkcs11_session;
struct aws_string;
enum aws_tls_versions {
AWS_IO_SSLv3,
AWS_IO_TLSv1,
AWS_IO_TLSv1_1,
AWS_IO_TLSv1_2,
AWS_IO_TLSv1_3,
AWS_IO_TLS_VER_SYS_DEFAULTS = 128,
};
enum aws_tls_cipher_pref {
AWS_IO_TLS_CIPHER_PREF_SYSTEM_DEFAULT = 0,
/* Deprecated */ AWS_IO_TLS_CIPHER_PREF_KMS_PQ_TLSv1_0_2019_06 = 1,
/* Deprecated */ AWS_IO_TLS_CIPHER_PREF_KMS_PQ_SIKE_TLSv1_0_2019_11 = 2,
/* Deprecated */ AWS_IO_TLS_CIPHER_PREF_KMS_PQ_TLSv1_0_2020_02 = 3,
/* Deprecated */ AWS_IO_TLS_CIPHER_PREF_KMS_PQ_SIKE_TLSv1_0_2020_02 = 4,
/* Deprecated */ AWS_IO_TLS_CIPHER_PREF_KMS_PQ_TLSv1_0_2020_07 = 5,
/*
* This TLS cipher preference list contains post-quantum key exchange algorithms that have been submitted to NIST
* for potential future standardization. Support for this preference list, or PQ algorithms present in it, may be
* removed at any time in the future. PQ algorithms in this preference list will be used in hybrid mode, and always
* combined with a classical ECDHE key exchange.
*/
AWS_IO_TLS_CIPHER_PREF_PQ_TLSv1_0_2021_05 = 6,
AWS_IO_TLS_CIPHER_PREF_END_RANGE = 0xFFFF
};
/**
* The hash algorithm of a TLS private key operation. Any custom private key operation handlers are expected to perform
* operations on the input TLS data using the correct hash algorithm or fail the operation.
*/
enum aws_tls_hash_algorithm {
AWS_TLS_HASH_UNKNOWN,
AWS_TLS_HASH_SHA1,
AWS_TLS_HASH_SHA224,
AWS_TLS_HASH_SHA256,
AWS_TLS_HASH_SHA384,
AWS_TLS_HASH_SHA512,
};
/**
* The signature of a TLS private key operation. Any custom private key operation handlers are expected to perform
* operations on the input TLS data using the correct signature algorithm or fail the operation.
*/
enum aws_tls_signature_algorithm {
AWS_TLS_SIGNATURE_UNKNOWN,
AWS_TLS_SIGNATURE_RSA,
AWS_TLS_SIGNATURE_ECDSA,
};
/**
* The TLS private key operation that needs to be performed by a custom private key operation handler when making
* a connection using mutual TLS.
*/
enum aws_tls_key_operation_type {
AWS_TLS_KEY_OPERATION_UNKNOWN,
AWS_TLS_KEY_OPERATION_SIGN,
AWS_TLS_KEY_OPERATION_DECRYPT,
};
struct aws_tls_ctx {
struct aws_allocator *alloc;
void *impl;
struct aws_ref_count ref_count;
};
/**
* Invoked upon completion of the TLS handshake. If successful error_code will be AWS_OP_SUCCESS, otherwise
* the negotiation failed and immediately after this function is invoked, the channel will be shutting down.
*/
typedef void(aws_tls_on_negotiation_result_fn)(
struct aws_channel_handler *handler,
struct aws_channel_slot *slot,
int error_code,
void *user_data);
/**
* Only used if the TLS handler is the last handler in the channel. This allows you to read any data that
* was read and decrypted by the handler. If you have application protocol channel handlers, this function
* is not necessary and certainly not recommended.
*/
typedef void(aws_tls_on_data_read_fn)(
struct aws_channel_handler *handler,
struct aws_channel_slot *slot,
struct aws_byte_buf *buffer,
void *user_data);
/**
* Invoked when an error occurs in the TLS state machine AFTER the handshake has completed. This function should only
* be used in conjunction with the rules of aws_tls_on_data_read_fn.
*/
typedef void(aws_tls_on_error_fn)(
struct aws_channel_handler *handler,
struct aws_channel_slot *slot,
int err,
const char *message,
void *user_data);
struct aws_tls_connection_options {
/** semi-colon delimited list of protocols. Example:
* h2;http/1.1
*/
struct aws_string *alpn_list;
/**
* Serves two purposes. If SNI is supported (hint... it is),
* this sets the SNI extension.
*
* For X.509 validation this also sets the name that will be used
* for verifying the subj alt name and common name of the peer's certificate.
*/
struct aws_string *server_name;
aws_tls_on_negotiation_result_fn *on_negotiation_result;
aws_tls_on_data_read_fn *on_data_read;
aws_tls_on_error_fn *on_error;
void *user_data;
struct aws_tls_ctx *ctx;
bool advertise_alpn_message;
uint32_t timeout_ms;
};
/**
* A struct containing all of the data needed for a private key operation when
* making a mutual TLS connection. This struct contains the data that needs
* to be operated on, like performing a sign operation or a decrypt operation.
*/
struct aws_tls_key_operation;
struct aws_tls_ctx_options {
struct aws_allocator *allocator;
/**
* minimum tls version to use. If you just want us to use the
* system defaults, you can set: AWS_IO_TLS_VER_SYS_DEFAULTS. This
* has the added benefit of automatically picking up new TLS versions
* as your OS or distribution adds support.
*/
enum aws_tls_versions minimum_tls_version;
/**
* The Cipher Preference List to use
*/
enum aws_tls_cipher_pref cipher_pref;
/**
* A PEM armored PKCS#7 collection of CAs you want to trust as a string.
* Only use this if it's a CA not currently installed on your system.
*/
struct aws_byte_buf ca_file;
/**
* Only used on Unix systems using an openssl style trust API.
* this is typically something like /etc/pki/tls/certs/"
*/
struct aws_string *ca_path;
/**
* Sets ctx wide alpn string. This is most useful for servers.
* This is a semi-colon delimited list. example:
* h2;http/1.1
*/
struct aws_string *alpn_list;
/**
* A PEM armored PKCS#7 certificate as a string.
* It is supported on every operating system.
*/
struct aws_byte_buf certificate;
#ifdef _WIN32
/** The path to a system
* installed certficate/private key pair. Example:
* CurrentUser\\MY\\<thumprint>
*/
const char *system_certificate_path;
#endif
/**
* A PEM armored PKCS#7 private key as a string.
*
* On windows, this field should be NULL only if you are
* using a system installed certficate.
*/
struct aws_byte_buf private_key;
#ifdef __APPLE__
/**
* Apple Only!
*
* On Apple OS you can also use a pkcs#12 for your certificate
* and private key. This is the contents the certificate.
*/
struct aws_byte_buf pkcs12;
/**
* Password for the pkcs12 data in pkcs12.
*/
struct aws_byte_buf pkcs12_password;
# if !defined(AWS_OS_IOS)
/**
* On Apple OS you can also use a custom keychain instead of
* the default keychain of the account.
*/
struct aws_string *keychain_path;
# endif
#endif
/** max tls fragment size. Default is the value of g_aws_channel_max_fragment_size. */
size_t max_fragment_size;
/**
* default is true for clients and false for servers.
* You should not change this default for clients unless
* you're testing and don't want to fool around with CA trust stores.
* Before you release to production, you'll want to turn this back on
* and add your custom CA to the aws_tls_ctx_options.
*
* If you set this in server mode, it enforces client authentication.
*/
bool verify_peer;
/**
* For use when adding BYO_CRYPTO implementations. You can set extra data in here for use with your TLS
* implementation.
*/
void *ctx_options_extension;
/**
* Set if using custom private key operations.
* See aws_custom_key_op_handler for more details
*
* Note: Custom key operations (and PKCS#11 integration) hasn't been tested with TLS 1.3, so don't use
* cipher preferences that allow TLS 1.3. If this is set, we will always use non TLS 1.3 preferences.
*/
struct aws_custom_key_op_handler *custom_key_op_handler;
};
struct aws_tls_negotiated_protocol_message {
struct aws_byte_buf protocol;
};
typedef struct aws_channel_handler *(
*aws_tls_on_protocol_negotiated)(struct aws_channel_slot *new_slot, struct aws_byte_buf *protocol, void *user_data);
/**
* An enum for the current state of tls negotiation within a tls channel handler
*/
enum aws_tls_negotiation_status {
AWS_TLS_NEGOTIATION_STATUS_NONE,
AWS_TLS_NEGOTIATION_STATUS_ONGOING,
AWS_TLS_NEGOTIATION_STATUS_SUCCESS,
AWS_TLS_NEGOTIATION_STATUS_FAILURE
};
#ifdef BYO_CRYPTO
/**
* Callback for creating a TLS handler. If you're using this you're using BYO_CRYPTO. This function should return
* a fully implemented aws_channel_handler instance for TLS. Note: the aws_tls_options passed to your
* aws_tls_handler_new_fn contains multiple callbacks. Namely: aws_tls_on_negotiation_result_fn. You are responsible for
* invoking this function when TLs session negotiation has completed.
*/
typedef struct aws_channel_handler *(aws_tls_handler_new_fn)(struct aws_allocator *allocator,
struct aws_tls_connection_options *options,
struct aws_channel_slot *slot,
void *user_data);
/**
* Invoked when it's time to start TLS negotiation. Note: the aws_tls_options passed to your aws_tls_handler_new_fn
* contains multiple callbacks. Namely: aws_tls_on_negotiation_result_fn. You are responsible for invoking this function
* when TLS session negotiation has completed.
*/
typedef int(aws_tls_client_handler_start_negotiation_fn)(struct aws_channel_handler *handler, void *user_data);
struct aws_tls_byo_crypto_setup_options {
aws_tls_handler_new_fn *new_handler_fn;
/* ignored for server implementations, required for clients. */
aws_tls_client_handler_start_negotiation_fn *start_negotiation_fn;
void *user_data;
};
#endif /* BYO_CRYPTO */
AWS_EXTERN_C_BEGIN
/******************************** tls options init stuff ***********************/
/**
* Initializes options with default client options
*/
AWS_IO_API void aws_tls_ctx_options_init_default_client(
struct aws_tls_ctx_options *options,
struct aws_allocator *allocator);
/**
* Cleans up resources allocated by init_* functions
*/
AWS_IO_API void aws_tls_ctx_options_clean_up(struct aws_tls_ctx_options *options);
/**
* Initializes options for use with mutual tls in client mode.
* cert_path and pkey_path are paths to files on disk. cert_path
* and pkey_path are treated as PKCS#7 PEM armored. They are loaded
* from disk and stored in buffers internally.
*
* NOTE: This is unsupported on iOS.
*/
AWS_IO_API int aws_tls_ctx_options_init_client_mtls_from_path(
struct aws_tls_ctx_options *options,
struct aws_allocator *allocator,
const char *cert_path,
const char *pkey_path);
/**
* Initializes options for use with mutual tls in client mode.
* cert and pkey are copied. cert and pkey are treated as PKCS#7 PEM
* armored.
*
* NOTE: This is unsupported on iOS.
*/
AWS_IO_API int aws_tls_ctx_options_init_client_mtls(
struct aws_tls_ctx_options *options,
struct aws_allocator *allocator,
const struct aws_byte_cursor *cert,
const struct aws_byte_cursor *pkey);
/**
* vtable for aws_custom_key_op_handler.
*/
struct aws_custom_key_op_handler_vtable {
/**
* Called when the a TLS handshake has an operation it needs the custom key operation handler to perform.
* NOTE: You must call aws_tls_key_operation_complete() or aws_tls_key_operation_complete_with_error()
* otherwise the TLS handshake will stall the TLS connection indefinitely and leak memory.
*/
void (*on_key_operation)(struct aws_custom_key_op_handler *key_op_handler, struct aws_tls_key_operation *operation);
};
/**
* The custom key operation that is used when performing a mutual TLS handshake. This can
* be extended to provide custom private key operations, like PKCS11 or similar.
*/
struct aws_custom_key_op_handler {
/**
* A void* intended to be populated with a reference to whatever class is extending this class. For example,
* if you have extended aws_custom_key_op_handler with a custom struct, you would put a pointer to this struct
* to *impl so you can retrieve it back in the vtable functions.
*/
void *impl;
/**
* A vtable containing all of the functions the aws_custom_key_op_handler implements. Is intended to be extended.
* NOTE: Use "aws_custom_key_op_handler_<func>" to access vtable functions.
*/
const struct aws_custom_key_op_handler_vtable *vtable;
/**
* A reference count for handling memory usage.
* Use aws_custom_key_op_handler_acquire and aws_custom_key_op_handler_release to increase/decrease count.
*/
struct aws_ref_count ref_count;
};
/**
* Increases the reference count for the passed-in aws_custom_key_op_handler and returns it.
*/
AWS_IO_API struct aws_custom_key_op_handler *aws_custom_key_op_handler_acquire(
struct aws_custom_key_op_handler *key_op_handler);
/**
* Decreases the reference count for the passed-in aws_custom_key_op_handler and returns NULL.
*/
AWS_IO_API struct aws_custom_key_op_handler *aws_custom_key_op_handler_release(
struct aws_custom_key_op_handler *key_op_handler);
/**
* Calls the on_key_operation vtable function. See aws_custom_key_op_handler_vtable for function details.
*/
AWS_IO_API void aws_custom_key_op_handler_perform_operation(
struct aws_custom_key_op_handler *key_op_handler,
struct aws_tls_key_operation *operation);
/**
* Initializes options for use with mutual TLS in client mode,
* where private key operations are handled by custom code.
*
* Note: cert_file_contents will be copied into a new buffer after this
* function is called, so you do not need to keep that data alive
* after calling this function.
*
* @param options aws_tls_ctx_options to be initialized.
* @param allocator Allocator to use.
* @param custom Options for custom key operations.
* @param cert_file_contents The contents of a certificate file.
*/
AWS_IO_API int aws_tls_ctx_options_init_client_mtls_with_custom_key_operations(
struct aws_tls_ctx_options *options,
struct aws_allocator *allocator,
struct aws_custom_key_op_handler *custom,
const struct aws_byte_cursor *cert_file_contents);
/**
* This struct exists as a graceful way to pass many arguments when
* calling init-with-pkcs11 functions on aws_tls_ctx_options (this also makes
* it easy to introduce optional arguments in the future).
* Instances of this struct should only exist briefly on the stack.
*
* Instructions for binding this to high-level languages:
* - Python: The members of this struct should be the keyword args to the init-with-pkcs11 functions.
* - JavaScript: This should be an options map passed to init-with-pkcs11 functions.
* - Java: This should be an options class passed to init-with-pkcs11 functions.
* - C++: Same as Java
*
* Notes on integer types:
* PKCS#11 uses `unsigned long` for IDs, handles, etc but we expose them as `uint64_t` in public APIs.
* We do this because sizeof(long) is inconsistent across platform/arch/language
* (ex: always 64bit in Java, always 32bit in C on Windows, matches CPU in C on Linux and Apple).
* By using uint64_t in our public API, we can keep the careful bounds-checking all in one
* place, instead of expecting each high-level language binding to get it just right.
*/
struct aws_tls_ctx_pkcs11_options {
/**
* The PKCS#11 library to use.
* This field is required.
*/
struct aws_pkcs11_lib *pkcs11_lib;
/**
* User PIN, for logging into the PKCS#11 token (UTF-8).
* Zero out to log into a token with a "protected authentication path".
*/
struct aws_byte_cursor user_pin;
/**
* ID of slot containing PKCS#11 token.
* If set to NULL, the token will be chosen based on other criteria
* (such as token label).
*/
const uint64_t *slot_id;
/**
* Label of PKCS#11 token to use.
* If zeroed out, the token will be chosen based on other criteria
* (such as slot ID).
*/
struct aws_byte_cursor token_label;
/**
* Label of private key object on PKCS#11 token (UTF-8).
* If zeroed out, the private key will be chosen based on other criteria
* (such as being the only available private key on the token).
*/
struct aws_byte_cursor private_key_object_label;
/**
* Certificate's file path on disk (UTF-8).
* The certificate must be PEM formatted and UTF-8 encoded.
* Zero out if passing in certificate by some other means (such as file contents).
*/
struct aws_byte_cursor cert_file_path;
/**
* Certificate's file contents (UTF-8).
* The certificate must be PEM formatted and UTF-8 encoded.
* Zero out if passing in certificate by some other means (such as file path).
*/
struct aws_byte_cursor cert_file_contents;
};
/**
* Initializes options for use with mutual TLS in client mode,
* where a PKCS#11 library provides access to the private key.
*
* NOTE: This only works on Unix devices.
*
* @param options aws_tls_ctx_options to be initialized.
* @param allocator Allocator to use.
* @param pkcs11_options Options for using PKCS#11 (contents are copied)
*/
AWS_IO_API int aws_tls_ctx_options_init_client_mtls_with_pkcs11(
struct aws_tls_ctx_options *options,
struct aws_allocator *allocator,
const struct aws_tls_ctx_pkcs11_options *pkcs11_options);
/**
* @Deprecated
*
* Sets a custom keychain path for storing the cert and pkey with mutual tls in client mode.
*
* NOTE: This only works on MacOS.
*/
AWS_IO_API int aws_tls_ctx_options_set_keychain_path(
struct aws_tls_ctx_options *options,
struct aws_byte_cursor *keychain_path_cursor);
/**
* Initializes options for use with in server mode.
* cert_path and pkey_path are paths to files on disk. cert_path
* and pkey_path are treated as PKCS#7 PEM armored. They are loaded
* from disk and stored in buffers internally.
*/
AWS_IO_API int aws_tls_ctx_options_init_default_server_from_path(
struct aws_tls_ctx_options *options,
struct aws_allocator *allocator,
const char *cert_path,
const char *pkey_path);
/**
* Initializes options for use with in server mode.
* cert and pkey are copied. cert and pkey are treated as PKCS#7 PEM
* armored.
*/
AWS_IO_API int aws_tls_ctx_options_init_default_server(
struct aws_tls_ctx_options *options,
struct aws_allocator *allocator,
struct aws_byte_cursor *cert,
struct aws_byte_cursor *pkey);
/**
* Initializes options for use with mutual tls in client mode.
* cert_reg_path is the path to a system
* installed certficate/private key pair. Example:
* CurrentUser\\MY\\<thumprint>
*
* NOTE: This only works on Windows.
*/
AWS_IO_API int aws_tls_ctx_options_init_client_mtls_from_system_path(
struct aws_tls_ctx_options *options,
struct aws_allocator *allocator,
const char *cert_reg_path);
/**
* Initializes options for use with server mode.
* cert_reg_path is the path to a system
* installed certficate/private key pair. Example:
* CurrentUser\\MY\\<thumprint>
*
* NOTE: This only works on Windows.
*/
AWS_IO_API int aws_tls_ctx_options_init_default_server_from_system_path(
struct aws_tls_ctx_options *options,
struct aws_allocator *allocator,
const char *cert_reg_path);
/**
* Initializes options for use with mutual tls in client mode.
* pkcs12_path is a path to a file on disk containing a pkcs#12 file. The file is loaded
* into an internal buffer. pkcs_pwd is the corresponding password for the pkcs#12 file; it is copied.
*
* NOTE: This only works on Apple devices.
*/
AWS_IO_API int aws_tls_ctx_options_init_client_mtls_pkcs12_from_path(
struct aws_tls_ctx_options *options,
struct aws_allocator *allocator,
const char *pkcs12_path,
const struct aws_byte_cursor *pkcs_pwd);
/**
* Initializes options for use with mutual tls in client mode.
* pkcs12 is a buffer containing a pkcs#12 certificate and private key; it is copied.
* pkcs_pwd is the corresponding password for the pkcs#12 buffer; it is copied.
*
* NOTE: This only works on Apple devices.
*/
AWS_IO_API int aws_tls_ctx_options_init_client_mtls_pkcs12(
struct aws_tls_ctx_options *options,
struct aws_allocator *allocator,
struct aws_byte_cursor *pkcs12,
struct aws_byte_cursor *pkcs_pwd);
/**
* Initializes options for use in server mode.
* pkcs12_path is a path to a file on disk containing a pkcs#12 file. The file is loaded
* into an internal buffer. pkcs_pwd is the corresponding password for the pkcs#12 file; it is copied.
*
* NOTE: This only works on Apple devices.
*/
AWS_IO_API int aws_tls_ctx_options_init_server_pkcs12_from_path(
struct aws_tls_ctx_options *options,
struct aws_allocator *allocator,
const char *pkcs12_path,
struct aws_byte_cursor *pkcs_password);
/**
* Initializes options for use in server mode.
* pkcs12 is a buffer containing a pkcs#12 certificate and private key; it is copied.
* pkcs_pwd is the corresponding password for the pkcs#12 buffer; it is copied.
*
* NOTE: This only works on Apple devices.
*/
AWS_IO_API int aws_tls_ctx_options_init_server_pkcs12(
struct aws_tls_ctx_options *options,
struct aws_allocator *allocator,
struct aws_byte_cursor *pkcs12,
struct aws_byte_cursor *pkcs_password);
/**
* Sets alpn list in the form <protocol1;protocol2;...>. A maximum of 4 protocols are supported.
* alpn_list is copied.
*/
AWS_IO_API int aws_tls_ctx_options_set_alpn_list(struct aws_tls_ctx_options *options, const char *alpn_list);
/**
* Enables or disables x.509 validation. Disable this only for testing. To enable mutual TLS in server mode,
* set verify_peer to true.
*/
AWS_IO_API void aws_tls_ctx_options_set_verify_peer(struct aws_tls_ctx_options *options, bool verify_peer);
/**
* Sets preferred TLS Cipher List
*/
AWS_IO_API void aws_tls_ctx_options_set_tls_cipher_preference(
struct aws_tls_ctx_options *options,
enum aws_tls_cipher_pref cipher_pref);
/**
* Sets the minimum TLS version to allow.
*/
AWS_IO_API void aws_tls_ctx_options_set_minimum_tls_version(
struct aws_tls_ctx_options *options,
enum aws_tls_versions minimum_tls_version);
/**
* Override the default trust store. ca_file is a buffer containing a PEM armored chain of trusted CA certificates.
* ca_file is copied.
*/
AWS_IO_API int aws_tls_ctx_options_override_default_trust_store(
struct aws_tls_ctx_options *options,
const struct aws_byte_cursor *ca_file);
/**
* Override the default trust store. ca_path is a path to a directory on disk containing trusted certificates. This is
* only supported on Unix systems (otherwise this parameter is ignored). ca_file is a path to a file on disk containing
* trusted certificates. ca_file is loaded from disk and stored in an internal buffer.
*/
AWS_IO_API int aws_tls_ctx_options_override_default_trust_store_from_path(
struct aws_tls_ctx_options *options,
const char *ca_path,
const char *ca_file);
/**
* When implementing BYO_CRYPTO, if you need extra data to pass to your tls implementation, set it here. The lifetime of
* extension_data must outlive the options object and be cleaned up after options is cleaned up.
*/
AWS_IO_API void aws_tls_ctx_options_set_extension_data(struct aws_tls_ctx_options *options, void *extension_data);
/**
* Initializes default connection options from an instance ot aws_tls_ctx.
*/
AWS_IO_API void aws_tls_connection_options_init_from_ctx(
struct aws_tls_connection_options *conn_options,
struct aws_tls_ctx *ctx);
/**
* Cleans up resources in aws_tls_connection_options. This can be called immediately after initializing
* a tls handler, or if using the bootstrap api, immediately after asking for a channel.
*/
AWS_IO_API void aws_tls_connection_options_clean_up(struct aws_tls_connection_options *connection_options);
/**
* Cleans up 'to' and copies 'from' to 'to'.
* 'to' must be initialized.
*/
AWS_IO_API int aws_tls_connection_options_copy(
struct aws_tls_connection_options *to,
const struct aws_tls_connection_options *from);
/**
* Sets callbacks for use with a tls connection.
*/
AWS_IO_API void aws_tls_connection_options_set_callbacks(
struct aws_tls_connection_options *conn_options,
aws_tls_on_negotiation_result_fn *on_negotiation_result,
aws_tls_on_data_read_fn *on_data_read,
aws_tls_on_error_fn *on_error,
void *user_data);
/**
* Sets server name to use for the SNI extension (supported everywhere), as well as x.509 validation. If you don't
* set this, your x.509 validation will likely fail.
*/
AWS_IO_API int aws_tls_connection_options_set_server_name(
struct aws_tls_connection_options *conn_options,
struct aws_allocator *allocator,
const struct aws_byte_cursor *server_name);
/**
* Sets alpn list in the form <protocol1;protocol2;...>. A maximum of 4 protocols are supported.
* alpn_list is copied. This value is already inherited from aws_tls_ctx, but the aws_tls_ctx is expensive,
* and should be used across as many connections as possible. If you want to set this per connection, set it here.
*/
AWS_IO_API int aws_tls_connection_options_set_alpn_list(
struct aws_tls_connection_options *conn_options,
struct aws_allocator *allocator,
const char *alpn_list);
/********************************* TLS context and state management *********************************/
/**
* Returns true if alpn is available in the underlying tls implementation.
* This function should always be called before setting an alpn list.
*/
AWS_IO_API bool aws_tls_is_alpn_available(void);
/**
* Returns true if this Cipher Preference is available in the underlying TLS implementation.
* This function should always be called before setting a Cipher Preference
*/
AWS_IO_API bool aws_tls_is_cipher_pref_supported(enum aws_tls_cipher_pref cipher_pref);
/**
* Creates a new tls channel handler in client mode. Options will be copied.
* You must call aws_tls_client_handler_start_negotiation and wait on the
* aws_tls_on_negotiation_result_fn callback before the handler can begin processing
* application data.
*/
AWS_IO_API struct aws_channel_handler *aws_tls_client_handler_new(
struct aws_allocator *allocator,
struct aws_tls_connection_options *options,
struct aws_channel_slot *slot);
/**
* Creates a new tls channel handler in server mode. Options will be copied.
* You must wait on the aws_tls_on_negotiation_result_fn callback before the handler can begin processing
* application data.
*/
AWS_IO_API struct aws_channel_handler *aws_tls_server_handler_new(
struct aws_allocator *allocator,
struct aws_tls_connection_options *options,
struct aws_channel_slot *slot);
#ifdef BYO_CRYPTO
/**
* If using BYO_CRYPTO, you need to call this function prior to creating any client channels in the application.
*/
AWS_IO_API void aws_tls_byo_crypto_set_client_setup_options(const struct aws_tls_byo_crypto_setup_options *options);
/**
* If using BYO_CRYPTO, you need to call this function prior to creating any server channels in the application.
*/
AWS_IO_API void aws_tls_byo_crypto_set_server_setup_options(const struct aws_tls_byo_crypto_setup_options *options);
#endif /* BYO_CRYPTO */
/**
* Creates a channel handler, for client or server mode, that handles alpn. This isn't necessarily required
* since you can always call aws_tls_handler_protocol in the aws_tls_on_negotiation_result_fn callback, but
* this makes channel bootstrap easier to handle.
*/
AWS_IO_API struct aws_channel_handler *aws_tls_alpn_handler_new(
struct aws_allocator *allocator,
aws_tls_on_protocol_negotiated on_protocol_negotiated,
void *user_data);
/**
* Kicks off the negotiation process. This function must be called when in client mode to initiate the
* TLS handshake. Once the handshake has completed the aws_tls_on_negotiation_result_fn will be invoked.
*/
AWS_IO_API int aws_tls_client_handler_start_negotiation(struct aws_channel_handler *handler);
#ifndef BYO_CRYPTO
/**
* Creates a new server ctx. This ctx can be used for the lifetime of the application assuming you want the same
* options for every incoming connection. Options will be copied.
*/
AWS_IO_API struct aws_tls_ctx *aws_tls_server_ctx_new(
struct aws_allocator *alloc,
const struct aws_tls_ctx_options *options);
/**
* Creates a new client ctx. This ctx can be used for the lifetime of the application assuming you want the same
* options for every outgoing connection. Options will be copied.
*/
AWS_IO_API struct aws_tls_ctx *aws_tls_client_ctx_new(
struct aws_allocator *alloc,
const struct aws_tls_ctx_options *options);
#endif /* BYO_CRYPTO */
/**
* Increments the reference count on the tls context, allowing the caller to take a reference to it.
*
* Returns the same tls context passed in.
*/
AWS_IO_API struct aws_tls_ctx *aws_tls_ctx_acquire(struct aws_tls_ctx *ctx);
/**
* Decrements a tls context's ref count. When the ref count drops to zero, the object will be destroyed.
*/
AWS_IO_API void aws_tls_ctx_release(struct aws_tls_ctx *ctx);
/**
* Returns a byte buffer by copy of the negotiated protocols. If there is no agreed upon protocol, len will be 0 and
* buffer will be NULL.
*/
AWS_IO_API struct aws_byte_buf aws_tls_handler_protocol(struct aws_channel_handler *handler);
/**
* Client mode only. This is the server name that was used for SNI and host name validation.
*/
AWS_IO_API struct aws_byte_buf aws_tls_handler_server_name(struct aws_channel_handler *handler);
/**************************** TLS KEY OPERATION *******************************/
/* Note: Currently this assumes the user knows what key is being used for key/cert pairs
but s2n supports multiple cert/key pairs. This functionality is not used in the
CRT currently, but in the future, we may need to implement this */
/**
* Complete a successful TLS private key operation by providing its output.
* The output is copied into the TLS connection.
* The operation is freed by this call.
*
* You MUST call this or aws_tls_key_operation_complete_with_error().
* Failure to do so will stall the TLS connection indefinitely and leak memory.
*/
AWS_IO_API
void aws_tls_key_operation_complete(struct aws_tls_key_operation *operation, struct aws_byte_cursor output);
/**
* Complete an failed TLS private key operation.
* The TLS connection will fail.
* The operation is freed by this call.
*
* You MUST call this or aws_tls_key_operation_complete().
* Failure to do so will stall the TLS connection indefinitely and leak memory.
*/
AWS_IO_API
void aws_tls_key_operation_complete_with_error(struct aws_tls_key_operation *operation, int error_code);
/**
* Returns the input data that needs to be operated on by the custom key operation.
*/
AWS_IO_API
struct aws_byte_cursor aws_tls_key_operation_get_input(const struct aws_tls_key_operation *operation);
/**
* Returns the type of operation that needs to be performed by the custom key operation.
* If the implementation cannot perform the operation,
* use aws_tls_key_operation_complete_with_error() to preventing stalling the TLS connection.
*/
AWS_IO_API
enum aws_tls_key_operation_type aws_tls_key_operation_get_type(const struct aws_tls_key_operation *operation);
/**
* Returns the algorithm the operation is expected to be operated with.
* If the implementation does not support the signature algorithm,
* use aws_tls_key_operation_complete_with_error() to preventing stalling the TLS connection.
*/
AWS_IO_API
enum aws_tls_signature_algorithm aws_tls_key_operation_get_signature_algorithm(
const struct aws_tls_key_operation *operation);
/**
* Returns the algorithm the operation digest is signed with.
* If the implementation does not support the digest algorithm,
* use aws_tls_key_operation_complete_with_error() to preventing stalling the TLS connection.
*/
AWS_IO_API
enum aws_tls_hash_algorithm aws_tls_key_operation_get_digest_algorithm(const struct aws_tls_key_operation *operation);
/********************************* Misc TLS related *********************************/
/*
* Injects a tls handler/slot into a channel and begins tls negotiation.
* If desired, ALPN must be handled separately
*
* right_of_slot must be an existing slot in a channel
*/
AWS_IO_API int aws_channel_setup_client_tls(
struct aws_channel_slot *right_of_slot,
struct aws_tls_connection_options *tls_options);
/**
* Given enum, return string like: AWS_TLS_HASH_SHA256 -> "SHA256"
*/
AWS_IO_API
const char *aws_tls_hash_algorithm_str(enum aws_tls_hash_algorithm hash);
/**
* Given enum, return string like: AWS_TLS_SIGNATURE_RSA -> "RSA"
*/
AWS_IO_API
const char *aws_tls_signature_algorithm_str(enum aws_tls_signature_algorithm signature);
/**
* Given enum, return string like: AWS_TLS_SIGNATURE_RSA -> "RSA"
*/
AWS_IO_API
const char *aws_tls_key_operation_type_str(enum aws_tls_key_operation_type operation_type);
AWS_EXTERN_C_END
AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_IO_TLS_CHANNEL_HANDLER_H */

View File

@@ -0,0 +1,11 @@
#ifndef AWS_IO_URI_H
#define AWS_IO_URI_H
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/io/io.h>
#include <aws/common/uri.h>
#endif /* AWS_IO_URI_H */