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,104 @@
#pragma once
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/crt/Exports.h>
#include <aws/crt/Types.h>
#include <aws/crt/io/EventLoopGroup.h>
#include <aws/crt/io/HostResolver.h>
#include <aws/io/channel_bootstrap.h>
#include <aws/io/host_resolver.h>
#include <future>
namespace Aws
{
namespace Crt
{
namespace Io
{
using OnClientBootstrapShutdownComplete = std::function<void()>;
/**
* A ClientBootstrap handles creation and setup of socket connections
* to specific endpoints.
*
* Note that ClientBootstrap may not clean up all its behind-the-scenes
* resources immediately upon destruction. If you need to know when
* behind-the-scenes shutdown is complete, use SetShutdownCompleteCallback()
* or EnableBlockingShutdown() (only safe on main thread).
*/
class AWS_CRT_CPP_API ClientBootstrap final
{
public:
/**
* @param elGroup: EventLoopGroup to use.
* @param resolver: DNS host resolver to use.
* @param allocator memory allocator to use
*/
ClientBootstrap(
EventLoopGroup &elGroup,
HostResolver &resolver,
Allocator *allocator = ApiAllocator()) noexcept;
/**
* Uses the default EventLoopGroup and HostResolver.
* See Aws::Crt::ApiHandle::GetOrCreateStaticDefaultEventLoopGroup
* and Aws::Crt::ApiHandle::GetOrCreateStaticDefaultHostResolver
*/
ClientBootstrap(Allocator *allocator = ApiAllocator()) noexcept;
~ClientBootstrap();
ClientBootstrap(const ClientBootstrap &) = delete;
ClientBootstrap &operator=(const ClientBootstrap &) = delete;
ClientBootstrap(ClientBootstrap &&) = delete;
ClientBootstrap &operator=(ClientBootstrap &&) = delete;
/**
* @return true if the instance is in a valid state, false otherwise.
*/
operator bool() const noexcept;
/**
* @return the value of the last aws error encountered by operations on this instance.
*/
int LastError() const noexcept;
/**
* Set function to invoke when ClientBootstrap's behind-the-scenes
* resources finish shutting down. This function may be invoked
* on any thread. Shutdown begins when the ClientBootstrap's
* destructor runs.
*/
void SetShutdownCompleteCallback(OnClientBootstrapShutdownComplete callback);
/**
* Force the ClientBootstrap's destructor to block until all
* behind-the-scenes resources finish shutting down.
*
* This isn't necessary during the normal flow of an application,
* but it is useful for scenarios, such as tests, that need deterministic
* shutdown ordering. Be aware, if you use this anywhere other
* than the main thread, YOU WILL MOST LIKELY CAUSE A DEADLOCK.
*
* Use SetShutdownCompleteCallback() for a thread-safe way to
* know when shutdown is complete.
*/
void EnableBlockingShutdown() noexcept;
/// @private
aws_client_bootstrap *GetUnderlyingHandle() const noexcept;
private:
aws_client_bootstrap *m_bootstrap;
int m_lastError;
std::unique_ptr<class ClientBootstrapCallbackData> m_callbackData;
std::future<void> m_shutdownFuture;
bool m_enableBlockingShutdown;
};
} // namespace Io
} // namespace Crt
} // namespace Aws

View File

@@ -0,0 +1,238 @@
#pragma once
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/crt/Exports.h>
#include <aws/crt/Types.h>
#include <aws/io/channel.h>
#include <chrono>
#include <cstddef>
struct aws_array_list;
struct aws_io_message;
namespace Aws
{
namespace Crt
{
namespace Io
{
enum class ChannelDirection
{
Read,
Write,
};
enum class MessageType
{
ApplicationData,
};
enum class TaskStatus
{
RunReady,
Canceled,
};
/**
* Wrapper for aws-c-io channel handlers. The semantics are identical as the functions on
* aws_channel_handler.
*
* All virtual calls are made from the same thread (the channel's thread).
*/
class AWS_CRT_CPP_API ChannelHandler
{
public:
virtual ~ChannelHandler() = default;
ChannelHandler(const ChannelHandler &) = delete;
ChannelHandler &operator=(const ChannelHandler &) = delete;
protected:
/**
* 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.
*
* 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.
*
* @return AWS_OP_SUCCESS if the message is being processed.
* If the message cannot be processed raise an error and return AWS_OP_ERR
* and do NOT release the message, it will be released by the caller.
*/
virtual int ProcessReadMessage(struct aws_io_message *message) = 0;
/**
* 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.
*
* @return AWS_OP_SUCCESS if the message is being processed.
* If the message cannot be processed raise an error and return AWS_OP_ERR
* and do NOT release the message, it will be released by the caller.
*/
virtual int ProcessWriteMessage(struct aws_io_message *message) = 0;
/**
* 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
* IncrementUpstreamReadWindow()
*
* @return AWS_OP_SUCCESS if successful.
* Otherwise, raise an error and return AWS_OP_ERR.
*/
virtual int IncrementReadWindow(size_t size) = 0;
/**
* 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
* ChannelDirection::Read. Then shutdown is called on handlers from right to left with dir set to
* ChannelDirection::Write.
*
* The shutdown process does not need to complete immediately and may rely on scheduled tasks.
* The handler MUST call OnShutdownComplete() when it is finished,
* which propagates shutdown to the next handler. If 'freeScarceResourcesImmediately' is true,
* then resources vulnerable to denial-of-service attacks (such as sockets and file handles)
* must be closed immediately before the shutdown process complete.
*/
virtual void ProcessShutdown(
ChannelDirection dir,
int errorCode,
bool freeScarceResourcesImmediately) = 0;
/**
* Called by the channel when the handler is added to a slot, to get the initial window size.
*/
virtual size_t InitialWindowSize() = 0;
/**
* 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.
*/
virtual size_t MessageOverhead() = 0;
/**
* Directs the channel handler to reset all of the internal statistics it tracks about itself.
*/
virtual void ResetStatistics() {};
/**
* 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.
*/
virtual void GatherStatistics(struct aws_array_list *) {}
public:
/// @private
struct aws_channel_handler *SeatForCInterop(const std::shared_ptr<ChannelHandler> &selfRef);
/**
* Return whether the caller is on the same thread as the handler's channel.
*/
bool ChannelsThreadIsCallersThread() const;
/**
* Initiate a shutdown of the handler's channel.
*
* If the channel is already shutting down, this call has no effect.
*/
void ShutDownChannel(int errorCode);
/**
* Schedule a task to run on the next "tick" of the event loop.
* If the channel is completely shut down, the task will run with the 'Canceled' status.
*/
void ScheduleTask(std::function<void(TaskStatus)> &&task);
/**
* Schedule a task to run after a desired length of time has passed.
* The task will run with the 'Canceled' status if the channel completes shutdown
* before that length of time elapses.
*/
void ScheduleTask(std::function<void(TaskStatus)> &&task, std::chrono::nanoseconds run_in);
protected:
ChannelHandler(Allocator *allocator = ApiAllocator());
/**
* Acquire an aws_io_message from the channel's pool.
*/
struct aws_io_message *AcquireMessageFromPool(MessageType messageType, size_t sizeHint);
/**
* Convenience function that invokes AcquireMessageFromPool(),
* asking for the largest reasonable DATA message that can be sent in the write direction,
* with upstream overhead accounted for.
*/
struct aws_io_message *AcquireMaxSizeMessageForWrite();
/**
* Send a message in the read or write direction.
* Returns true if message successfully sent.
* If false is returned, you must release the message yourself.
*/
bool SendMessage(struct aws_io_message *message, ChannelDirection direction);
/**
* Issue a window update notification upstream.
* Returns true if successful.
*/
bool IncrementUpstreamReadWindow(size_t windowUpdateSize);
/**
* Must be called by a handler once they have finished their shutdown in the 'dir' direction.
* Propagates the shutdown process to the next handler in the channel.
*/
void OnShutdownComplete(ChannelDirection direction, int errorCode, bool freeScarceResourcesImmediately);
/**
* 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.
*/
size_t DownstreamReadWindow() const;
/**
* Fetches the current overhead of upstream handlers.
* This provides a hint to avoid fragmentation if you care.
*/
size_t UpstreamMessageOverhead() const;
struct aws_channel_slot *GetSlot() const;
struct aws_channel_handler m_handler;
Allocator *m_allocator;
private:
std::shared_ptr<ChannelHandler> m_selfReference;
static struct aws_channel_handler_vtable s_vtable;
static void s_Destroy(struct aws_channel_handler *handler);
static int s_ProcessReadMessage(
struct aws_channel_handler *,
struct aws_channel_slot *,
struct aws_io_message *);
static int s_ProcessWriteMessage(
struct aws_channel_handler *,
struct aws_channel_slot *,
struct aws_io_message *);
static int s_IncrementReadWindow(struct aws_channel_handler *, struct aws_channel_slot *, size_t size);
static int s_ProcessShutdown(
struct aws_channel_handler *,
struct aws_channel_slot *,
enum aws_channel_direction,
int errorCode,
bool freeScarceResourcesImmediately);
static size_t s_InitialWindowSize(struct aws_channel_handler *);
static size_t s_MessageOverhead(struct aws_channel_handler *);
static void s_ResetStatistics(struct aws_channel_handler *);
static void s_GatherStatistics(struct aws_channel_handler *, struct aws_array_list *statsList);
};
} // namespace Io
} // namespace Crt
} // namespace Aws

View File

@@ -0,0 +1,74 @@
#pragma once
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/crt/Types.h>
#include <aws/io/event_loop.h>
namespace Aws
{
namespace Crt
{
namespace Io
{
/**
* A collection of event loops.
*
* An event-loop is a thread for doing async work, such as I/O. Classes that need to do async work will ask
* the EventLoopGroup for an event-loop to use.
*
* The number of threads used depends on your use-case. IF you
* have a maximum of less than a few hundred connections 1 thread is the ideal
* threadCount.
*
* There should only be one instance of an EventLoopGroup per application and it
* should be passed to all network clients. One exception to this is if you
* want to peg different types of IO to different threads. In that case, you
* may want to have one event loop group dedicated to one IO activity and another
* dedicated to another type.
*/
class AWS_CRT_CPP_API EventLoopGroup final
{
public:
/**
* @param threadCount: The number of event-loops to create, default will be 0, which will create one for
* each processor on the machine.
* @param allocator memory allocator to use.
*/
EventLoopGroup(uint16_t threadCount = 0, Allocator *allocator = ApiAllocator()) noexcept;
/**
* @param cpuGroup: The CPU group (e.g. NUMA nodes) that all hardware threads are pinned to.
* @param threadCount: The number of event-loops to create, default will be 0, which will create one for
* each processor on the machine.
* @param allocator memory allocator to use.
*/
EventLoopGroup(uint16_t cpuGroup, uint16_t threadCount, Allocator *allocator = ApiAllocator()) noexcept;
~EventLoopGroup();
EventLoopGroup(const EventLoopGroup &) = delete;
EventLoopGroup(EventLoopGroup &&) noexcept;
EventLoopGroup &operator=(const EventLoopGroup &) = delete;
EventLoopGroup &operator=(EventLoopGroup &&) noexcept;
/**
* @return true if the instance is in a valid state, false otherwise.
*/
operator bool() const;
/**
* @return the value of the last aws error encountered by operations on this instance.
*/
int LastError() const;
/// @private
aws_event_loop_group *GetUnderlyingHandle() noexcept;
private:
aws_event_loop_group *m_eventLoopGroup;
int m_lastError;
};
} // namespace Io
} // namespace Crt
} // namespace Aws

View File

@@ -0,0 +1,123 @@
#pragma once
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/crt/Types.h>
#include <aws/io/host_resolver.h>
#include <functional>
namespace Aws
{
namespace Crt
{
namespace Io
{
class EventLoopGroup;
class HostResolver;
using HostAddress = aws_host_address;
/**
* Invoked upon resolution of an address. You do not own the memory pointed to in addresses, if you persist
* the data, copy it first. If errorCode is AWS_ERROR_SUCCESS, the operation succeeded. Otherwise, the
* operation failed.
*/
using OnHostResolved =
std::function<void(HostResolver &resolver, const Vector<HostAddress> &addresses, int errorCode)>;
/**
* Simple interface for DNS name lookup implementations
*/
class AWS_CRT_CPP_API HostResolver
{
public:
virtual ~HostResolver();
virtual bool ResolveHost(const String &host, const OnHostResolved &onResolved) noexcept = 0;
/// @private
virtual aws_host_resolver *GetUnderlyingHandle() noexcept = 0;
/// @private
virtual aws_host_resolution_config *GetConfig() noexcept = 0;
};
/**
* A wrapper around the CRT default host resolution system that uses getaddrinfo() farmed off
* to separate threads in order to resolve names.
*/
class AWS_CRT_CPP_API DefaultHostResolver final : public HostResolver
{
public:
/**
* Resolves DNS addresses.
*
* @param elGroup: EventLoopGroup to use.
* @param maxHosts: the number of unique hosts to maintain in the cache.
* @param maxTTL: how long to keep an address in the cache before evicting it.
* @param allocator memory allocator to use.
*/
DefaultHostResolver(
EventLoopGroup &elGroup,
size_t maxHosts,
size_t maxTTL,
Allocator *allocator = ApiAllocator()) noexcept;
/**
* Resolves DNS addresses using the default EventLoopGroup.
*
* For more information on the default EventLoopGroup see
* Aws::Crt::ApiHandle::GetOrCreateStaticDefaultEventLoopGroup
*
* @param maxHosts: the number of unique hosts to maintain in the cache.
* @param maxTTL: how long to keep an address in the cache before evicting it.
* @param allocator memory allocator to use.
*/
DefaultHostResolver(size_t maxHosts, size_t maxTTL, Allocator *allocator = ApiAllocator()) noexcept;
~DefaultHostResolver();
DefaultHostResolver(const DefaultHostResolver &) = delete;
DefaultHostResolver &operator=(const DefaultHostResolver &) = delete;
DefaultHostResolver(DefaultHostResolver &&) = delete;
DefaultHostResolver &operator=(DefaultHostResolver &&) = delete;
/**
* @return true if the instance is in a valid state, false otherwise.
*/
operator bool() const noexcept { return m_initialized; }
/**
* @return the value of the last aws error encountered by operations on this instance.
*/
int LastError() const noexcept { return aws_last_error(); }
/**
* Kicks off an asynchronous resolution of host. onResolved will be invoked upon completion of the
* resolution.
* @return False, the resolution was not attempted. True, onResolved will be
* called with the result.
*/
bool ResolveHost(const String &host, const OnHostResolved &onResolved) noexcept override;
/// @private
aws_host_resolver *GetUnderlyingHandle() noexcept override { return m_resolver; }
/// @private
aws_host_resolution_config *GetConfig() noexcept override { return &m_config; }
private:
aws_host_resolver *m_resolver;
aws_host_resolution_config m_config;
Allocator *m_allocator;
bool m_initialized;
static void s_onHostResolved(
struct aws_host_resolver *resolver,
const struct aws_string *host_name,
int err_code,
const struct aws_array_list *host_addresses,
void *user_data);
};
} // namespace Io
} // namespace Crt
} // namespace Aws

View File

@@ -0,0 +1,116 @@
#pragma once
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/crt/Types.h>
struct aws_pkcs11_lib;
namespace Aws
{
namespace Crt
{
namespace Io
{
/**
* Handle to a loaded PKCS#11 library.
*
* For most use cases, a single instance of Pkcs11Lib should be used for the
* lifetime of your application.
*/
class AWS_CRT_CPP_API Pkcs11Lib
{
public:
/**
* Controls how Pkcs11Lib calls `C_Initialize()` and `C_Finalize()`
* on the PKCS#11 library.
*/
enum class InitializeFinalizeBehavior
{
/**
* 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.
*/
Default,
/**
* 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.
*/
Omit,
/**
* `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).
*/
Strict,
};
/**
* Load and initialize a PKCS#11 library.
*
* `C_Initialize()` and `C_Finalize()` are called on the PKCS#11
* library in the InitializeFinalizeBehavior::Default way.
*
* @param filename Name or path of PKCS#11 library file to load (UTF-8).
* Pass an empty string if your application already has PKCS#11 symbols linked in.
*
* @param allocator Memory allocator to use.
*
* @return If successful a `shared_ptr` containing the Pkcs11Lib is returned.
* If unsuccessful the `shared_ptr` will be empty, and Aws::Crt::LastError()
* will contain the error that occurred.
*/
static std::shared_ptr<Pkcs11Lib> Create(const String &filename, Allocator *allocator = ApiAllocator());
/**
* Load a PKCS#11 library, specifying how `C_Initialize()` and `C_Finalize()` will be called.
*
* @param filename Name or path of PKCS#11 library file to load (UTF-8).
* Pass an empty string if your application already has PKCS#11 symbols linked in.
*
* @param initializeFinalizeBehavior Specifies how `C_Initialize()` and
* `C_Finalize()` will be called on the
* PKCS#11 library.
* @param allocator Memory allocator to use.
*
* @return If successful a `shared_ptr` containing the Pkcs11Lib is returned.
* If unsuccessful the `shared_ptr` will be empty, and Aws::Crt::LastError()
* will contain the error that occurred.
*/
static std::shared_ptr<Pkcs11Lib> Create(
const String &filename,
InitializeFinalizeBehavior initializeFinalizeBehavior,
Allocator *allocator = ApiAllocator());
~Pkcs11Lib();
/// @private
aws_pkcs11_lib *GetNativeHandle() { return impl; }
/// @private Use Create(...), this constructor is for internal use only
explicit Pkcs11Lib(aws_pkcs11_lib &impl);
private:
// no copy/move
Pkcs11Lib(const Pkcs11Lib &) = delete;
Pkcs11Lib(Pkcs11Lib &&) = delete;
Pkcs11Lib &operator=(const Pkcs11Lib &) = delete;
Pkcs11Lib &operator=(Pkcs11Lib &&) = delete;
aws_pkcs11_lib *impl = nullptr;
};
} // namespace Io
} // namespace Crt
} // namespace Aws

View File

@@ -0,0 +1,157 @@
#pragma once
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/crt/Exports.h>
#include <aws/io/socket.h>
namespace Aws
{
namespace Crt
{
namespace Io
{
enum class SocketType
{
/**
* 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
*/
Stream = AWS_SOCKET_STREAM,
/**
* A datagram socket is connectionless and sends unreliable messages.
* This means UDP when used with IPV4/6.
* LOCAL sockets are not compatible with DGRAM.
*/
Dgram = AWS_SOCKET_DGRAM,
};
enum class SocketDomain
{
IPv4 = AWS_SOCKET_IPV4,
IPv6 = AWS_SOCKET_IPV6,
/**
* Unix domain sockets (or at least something like them)
*/
Local = AWS_SOCKET_LOCAL,
};
/**
* Socket configuration options
*/
class AWS_CRT_CPP_API SocketOptions
{
public:
SocketOptions();
SocketOptions(const SocketOptions &rhs) = default;
SocketOptions(SocketOptions &&rhs) = default;
SocketOptions &operator=(const SocketOptions &rhs) = default;
SocketOptions &operator=(SocketOptions &&rhs) = default;
/**
* Set socket type
* @param type: SocketType object.
*/
void SetSocketType(SocketType type) { options.type = (enum aws_socket_type)type; }
/**
* @return the type of socket to use
*/
SocketType GetSocketType() const { return (SocketType)options.type; }
/**
* Set socket domain
* @param domain: SocketDomain object.
*/
void SetSocketDomain(SocketDomain domain) { options.domain = (enum aws_socket_domain)domain; }
/**
* @return the domain type to use with the socket
*/
SocketDomain GetSocketDomain() const { return (SocketDomain)options.domain; }
/**
* Set connection timeout
* @param timeout: connection timeout in milliseconds.
*/
void SetConnectTimeoutMs(uint32_t timeout) { options.connect_timeout_ms = timeout; }
/**
* @return the connection timeout in milliseconds to use with the socket
*/
uint32_t GetConnectTimeoutMs() const { return options.connect_timeout_ms; }
/**
* Set keep alive interval seconds.
* @param keepAliveInterval: Duration, in seconds, between keepalive probes. If 0, then a default value
* is used.
*/
void SetKeepAliveIntervalSec(uint16_t keepAliveInterval)
{
options.keep_alive_interval_sec = keepAliveInterval;
}
/**
* @return the (tcp) keep alive interval to use with the socket, in seconds
*/
uint16_t GetKeepAliveIntervalSec() const { return options.keep_alive_interval_sec; }
/**
* Set keep alive time out seconds.
* @param keepAliveTimeout: interval, in seconds, that a connection must be idle for before keep alive
* probes begin to get sent out
*/
void SetKeepAliveTimeoutSec(uint16_t keepAliveTimeout)
{
options.keep_alive_timeout_sec = keepAliveTimeout;
}
/**
* @return interval, in seconds, that a connection must be idle for before keep alive probes begin
* to get sent out
*/
uint16_t GetKeepAliveTimeoutSec() const { return options.keep_alive_timeout_sec; }
/**
* Set keep alive max failed probes.
* @param maxProbes: The number of keepalive probes allowed to fail before a connection is considered
* lost.
*/
void SetKeepAliveMaxFailedProbes(uint16_t maxProbes)
{
options.keep_alive_max_failed_probes = maxProbes;
}
/**
* @return number of keepalive probes allowed to fail before a connection is considered lost.
*/
uint16_t GetKeepAliveMaxFailedProbes() const { return options.keep_alive_max_failed_probes; }
/**
* Set keep alive option.
* @param keepAlive: True, periodically transmit keepalive messages for detecting a disconnected peer.
*/
void SetKeepAlive(bool keepAlive) { options.keepalive = keepAlive; }
/**
* @return true/false if the socket implementation should use TCP keepalive
*/
bool GetKeepAlive() const { return options.keepalive; }
/// @private
aws_socket_options &GetImpl() { return options; }
/// @private
const aws_socket_options &GetImpl() const { return options; }
private:
aws_socket_options options;
};
} // namespace Io
} // namespace Crt
} // namespace Aws

View File

@@ -0,0 +1,197 @@
#pragma once
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/crt/Exports.h>
#include <aws/crt/RefCounted.h>
#include <aws/crt/Types.h>
#include <aws/io/stream.h>
namespace Aws
{
namespace Crt
{
namespace Io
{
using StreamStatus = aws_stream_status;
/**
* @deprecated Use int64_t instead for offsets in public APIs.
*/
using OffsetType = aws_off_t;
/**
* Controls the direction to seek from
*/
enum class StreamSeekBasis
{
Begin = AWS_SSB_BEGIN,
End = AWS_SSB_END,
};
/***
* Interface for building an Object oriented stream that will be honored by the CRT's low-level
* aws_input_stream interface. To use, create a subclass of InputStream and define the abstract
* functions.
*/
class AWS_CRT_CPP_API InputStream : public std::enable_shared_from_this<InputStream>,
public RefCounted<InputStream>
{
public:
virtual ~InputStream();
InputStream(const InputStream &) = delete;
InputStream &operator=(const InputStream &) = delete;
InputStream(InputStream &&) = delete;
InputStream &operator=(InputStream &&) = delete;
explicit operator bool() const noexcept { return IsValid(); }
/**
* @return true/false if this object is in a valid state
*/
virtual bool IsValid() const noexcept = 0;
/// @private
aws_input_stream *GetUnderlyingStream() noexcept { return &m_underlying_stream; }
/**
* Reads data from the stream into a buffer
* @param dest buffer to add the read data into
* @return success/failure
*/
bool Read(ByteBuf &dest) { return aws_input_stream_read(&m_underlying_stream, &dest) == 0; }
/**
* Moves the head of the stream to a new location
* @param offset how far to move, in bytes
* @param seekBasis what direction to move the head of stream
* @return success/failure
*/
bool Seek(int64_t offset, StreamSeekBasis seekBasis)
{
return aws_input_stream_seek(&m_underlying_stream, offset, (aws_stream_seek_basis)seekBasis) == 0;
}
/**
* Gets the stream's current status
* @param status output parameter for the stream's status
* @return success/failure
*/
bool GetStatus(StreamStatus &status)
{
return aws_input_stream_get_status(&m_underlying_stream, &status) == 0;
}
/**
* Gets the stream's length. Some streams may not be able to answer this.
* @param length output parameter for the length of the stream
* @return success/failure
*/
bool GetLength(int64_t &length)
{
return aws_input_stream_get_length(&m_underlying_stream, &length) == 0;
}
protected:
Allocator *m_allocator;
aws_input_stream m_underlying_stream;
InputStream(Aws::Crt::Allocator *allocator = ApiAllocator());
/***
* Read up-to buffer::capacity - buffer::len into buffer::buffer
* Increment buffer::len by the amount you read in.
*
* @return true if nothing went wrong.
* Return true even if you read 0 bytes because the end-of-file has been reached.
* Return true even if you read 0 bytes because data is not currently available.
*
* Return false if an actual failure condition occurs,
* you SHOULD also raise an error via aws_raise_error().
*/
virtual bool ReadImpl(ByteBuf &buffer) noexcept = 0;
/***
* Read up-to buffer::capacity - buffer::len immediately available bytes into buffer::buffer
* Increment buffer::len by the amount you read in.
*
* @return true if nothing went wrong.
* Return true even if you read 0 bytes because the end-of-file has been reached.
* Return true even if you read 0 bytes because data is not currently available.
*
* Return false if an actual failure condition occurs,
* you SHOULD also raise an error via aws_raise_error().
*/
virtual bool ReadSomeImpl(ByteBuf &buffer) noexcept = 0;
/**
* @return the current status of the stream.
*/
virtual StreamStatus GetStatusImpl() const noexcept = 0;
/**
* @return the total length of the available data for the stream.
* @return -1 if not available.
*/
virtual int64_t GetLengthImpl() const noexcept = 0;
/**
* Seek's the stream to seekBasis based offset bytes.
*
* It is expected, that if seeking to the beginning of a stream,
* all error's are cleared if possible.
*
* @return true on success, false otherwise. You SHOULD raise an error via aws_raise_error()
* if a failure occurs.
*/
virtual bool SeekImpl(int64_t offset, StreamSeekBasis seekBasis) noexcept = 0;
/**
* Peeks the stream
*
* Essentially calls peek on the underlying istream
*
* @return return value of the underlying istream::peek
*/
virtual int64_t PeekImpl() const noexcept = 0;
private:
static int s_Seek(aws_input_stream *stream, int64_t offset, enum aws_stream_seek_basis basis);
static int s_Read(aws_input_stream *stream, aws_byte_buf *dest);
static int s_GetStatus(aws_input_stream *stream, aws_stream_status *status);
static int s_GetLength(struct aws_input_stream *stream, int64_t *out_length);
static void s_Acquire(aws_input_stream *stream);
static void s_Release(aws_input_stream *stream);
static aws_input_stream_vtable s_vtable;
};
/***
* Implementation of Aws::Crt::Io::InputStream that wraps a std::input_stream.
*/
class AWS_CRT_CPP_API StdIOStreamInputStream : public InputStream
{
public:
StdIOStreamInputStream(
std::shared_ptr<Aws::Crt::Io::IStream> stream,
Aws::Crt::Allocator *allocator = ApiAllocator()) noexcept;
bool IsValid() const noexcept override;
protected:
bool ReadImpl(ByteBuf &buffer) noexcept override;
bool ReadSomeImpl(ByteBuf &buffer) noexcept override;
StreamStatus GetStatusImpl() const noexcept override;
int64_t GetLengthImpl() const noexcept override;
bool SeekImpl(OffsetType offsetType, StreamSeekBasis seekBasis) noexcept override;
int64_t PeekImpl() const noexcept override;
private:
std::shared_ptr<Aws::Crt::Io::IStream> m_stream;
};
} // namespace Io
} // namespace Crt
} // namespace Aws

View File

@@ -0,0 +1,453 @@
#pragma once
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/crt/Types.h>
#include <aws/crt/io/ChannelHandler.h>
#include <aws/io/tls_channel_handler.h>
#include <functional>
#include <memory>
struct aws_tls_ctx_options;
namespace Aws
{
namespace Crt
{
namespace Io
{
class Pkcs11Lib;
class TlsContextPkcs11Options;
enum class TlsMode
{
CLIENT,
SERVER,
};
/**
* Top-level tls configuration options. These options are used to create a context from which
* per-connection TLS contexts can be created.
*/
class AWS_CRT_CPP_API TlsContextOptions
{
friend class TlsContext;
public:
TlsContextOptions() noexcept;
virtual ~TlsContextOptions();
TlsContextOptions(const TlsContextOptions &) noexcept = delete;
TlsContextOptions &operator=(const TlsContextOptions &) noexcept = delete;
TlsContextOptions(TlsContextOptions &&) noexcept;
TlsContextOptions &operator=(TlsContextOptions &&) noexcept;
/**
* @return true if the instance is in a valid state, false otherwise.
*/
explicit operator bool() const noexcept { return m_isInit; }
/**
* @return the value of the last aws error encountered by operations on this instance.
*/
int LastError() const noexcept;
/**
* Initializes TlsContextOptions with secure by default options, with
* no client certificates.
*/
static TlsContextOptions InitDefaultClient(Allocator *allocator = ApiAllocator()) noexcept;
/**
* Initializes TlsContextOptions for mutual TLS (mTLS), with
* client certificate and private key. These are paths to a file on disk. These files
* must be in the PEM format.
*
* NOTE: This is unsupported on iOS.
*
* @param cert_path: Path to certificate file.
* @param pkey_path: Path to private key file.
* @param allocator Memory allocator to use.
*/
static TlsContextOptions InitClientWithMtls(
const char *cert_path,
const char *pkey_path,
Allocator *allocator = ApiAllocator()) noexcept;
/**
* Initializes TlsContextOptions for mutual TLS (mTLS), with
* client certificate and private key. These are in memory buffers. These buffers
* must be in the PEM format.
*
* NOTE: This is unsupported on iOS.
*
* @param cert: Certificate contents in memory.
* @param pkey: Private key contents in memory.
* @param allocator Memory allocator to use.
*/
static TlsContextOptions InitClientWithMtls(
const ByteCursor &cert,
const ByteCursor &pkey,
Allocator *allocator = ApiAllocator()) noexcept;
/**
* Initializes TlsContextOptions for mutual TLS (mTLS),
* using a PKCS#11 library for private key operations.
*
* NOTE: This only works on Unix devices.
*
* @param pkcs11Options PKCS#11 options
* @param allocator Memory allocator to use.
*/
static TlsContextOptions InitClientWithMtlsPkcs11(
const TlsContextPkcs11Options &pkcs11Options,
Allocator *allocator = ApiAllocator()) noexcept;
/**
* Initializes TlsContextOptions for mutual TLS (mTLS), with
* client certificate and private key in the PKCS#12 format.
*
* NOTE: This only works on Apple devices.
*
* @param pkcs12_path: Path to PKCS #12 file. The file is loaded from disk and stored internally. It
* must remain in memory for the lifetime of the returned object.
* @param pkcs12_pwd: Password to PKCS #12 file. It must remain in memory for the lifetime of the
* returned object.
* @param allocator Memory allocator to use.
*/
static TlsContextOptions InitClientWithMtlsPkcs12(
const char *pkcs12_path,
const char *pkcs12_pwd,
Allocator *allocator = ApiAllocator()) noexcept;
/**
* @deprecated Custom keychain management is deprecated.
*
* By default the certificates and private keys are stored in the default keychain
* of the account of the process. If you instead wish to provide your own keychain
* for storing them, this makes the TlsContext to use that instead.
* NOTE: The password of your keychain must be empty.
*
* NOTE: This only works on MacOS.
*/
bool SetKeychainPath(ByteCursor &keychain_path) noexcept;
/**
* Initializes TlsContextOptions for mutual TLS (mTLS),
* using a client certificate in a Windows certificate store.
*
* NOTE: This only works on Windows.
*
* @param windowsCertStorePath Path to certificate in a Windows certificate store.
* The path must use backslashes and end with the certificate's thumbprint.
* Example: `CurrentUser\MY\A11F8A9B5DF5B98BA3508FBCA575D09570E0D2C6`
* @param allocator The memory allocator to use.
*/
static TlsContextOptions InitClientWithMtlsSystemPath(
const char *windowsCertStorePath,
Allocator *allocator = ApiAllocator()) noexcept;
/**
* @return true if alpn is supported by the underlying security provider, false
* otherwise.
*/
static bool IsAlpnSupported() noexcept;
/**
* Sets the list of alpn protocols.
* @param alpnList: List of protocol names, delimited by ';'. This string must remain in memory for the
* lifetime of this object.
*/
bool SetAlpnList(const char *alpnList) noexcept;
/**
* In client mode, this turns off x.509 validation. Don't do this unless you're testing.
* It's much better, to just override the default trust store and pass the self-signed
* certificate as the caFile argument.
*
* In server mode, this defaults to false. If you want to support mutual TLS from the server,
* you'll want to set this to true.
*/
void SetVerifyPeer(bool verifyPeer) noexcept;
/**
* Sets the minimum TLS version allowed.
* @param minimumTlsVersion: The minimum TLS version.
*/
void SetMinimumTlsVersion(aws_tls_versions minimumTlsVersion);
/**
* Sets the preferred TLS Cipher List
* @param cipher_pref: The preferred TLS cipher list.
*/
void SetTlsCipherPreference(aws_tls_cipher_pref cipher_pref);
/**
* Overrides the default system trust store.
* @param caPath: Path to directory containing trusted certificates, which will overrides the
* default trust store. Only useful on Unix style systems where all anchors are stored in a directory
* (like /etc/ssl/certs). This string must remain in memory for the lifetime of this object.
* @param caFile: Path to file containing PEM armored chain of trusted CA certificates. This
* string must remain in memory for the lifetime of this object.
*/
bool OverrideDefaultTrustStore(const char *caPath, const char *caFile) noexcept;
/**
* Overrides the default system trust store.
* @param ca: PEM armored chain of trusted CA certificates.
*/
bool OverrideDefaultTrustStore(const ByteCursor &ca) noexcept;
/// @private
const aws_tls_ctx_options *GetUnderlyingHandle() const noexcept { return &m_options; }
private:
aws_tls_ctx_options m_options;
bool m_isInit;
};
/**
* Options for TLS, when using a PKCS#11 library for private key operations.
*
* @see TlsContextOptions::InitClientWithMtlsPkcs11()
*/
class AWS_CRT_CPP_API TlsContextPkcs11Options final
{
public:
/**
* @param pkcs11Lib use this PKCS#11 library
* @param allocator Memory allocator to use.
*/
TlsContextPkcs11Options(
const std::shared_ptr<Pkcs11Lib> &pkcs11Lib,
Allocator *allocator = ApiAllocator()) noexcept;
/**
* Use this PIN to log the user into the PKCS#11 token.
* Leave unspecified to log into a token with a "protected authentication path".
*
* @param pin PIN
*/
void SetUserPin(const String &pin) noexcept;
/**
* Specify the slot ID containing a PKCS#11 token.
* If not specified, the token will be chosen based on other criteria (such as token label).
*
* @param id slot ID
*/
void SetSlotId(const uint64_t id) noexcept;
/**
* Specify the label of the PKCS#11 token to use.
* If not specified, the token will be chosen based on other criteria (such as slot ID).
*
* @param label label of token
*/
void SetTokenLabel(const String &label) noexcept;
/**
* Specify the label of the private key object on the PKCS#11 token.
* If not specified, the key will be chosen based on other criteria
* (such as being the only available private key on the token).
*
* @param label label of private key object
*/
void SetPrivateKeyObjectLabel(const String &label) noexcept;
/**
* Use this X.509 certificate (file on disk).
* The certificate may be specified by other means instead (ex: SetCertificateFileContents())
*
* @param path path to PEM-formatted certificate file on disk.
*/
void SetCertificateFilePath(const String &path) noexcept;
/**
* Use this X.509 certificate (contents in memory).
* The certificate may be specified by other means instead (ex: SetCertificateFilePath())
*
* @param contents contents of PEM-formatted certificate file.
*/
void SetCertificateFileContents(const String &contents) noexcept;
/// @private
aws_tls_ctx_pkcs11_options GetUnderlyingHandle() const noexcept;
private:
std::shared_ptr<Pkcs11Lib> m_pkcs11Lib;
Optional<uint64_t> m_slotId;
Optional<String> m_userPin;
Optional<String> m_tokenLabel;
Optional<String> m_privateKeyObjectLabel;
Optional<String> m_certificateFilePath;
Optional<String> m_certificateFileContents;
};
/**
* Options specific to a single connection.
*/
class AWS_CRT_CPP_API TlsConnectionOptions final
{
public:
TlsConnectionOptions() noexcept;
~TlsConnectionOptions();
TlsConnectionOptions(const TlsConnectionOptions &) noexcept;
TlsConnectionOptions &operator=(const TlsConnectionOptions &) noexcept;
TlsConnectionOptions(TlsConnectionOptions &&options) noexcept;
TlsConnectionOptions &operator=(TlsConnectionOptions &&options) noexcept;
/**
* Sets SNI extension, and also the name used for X.509 validation. serverName is copied.
*
* @return true if the copy succeeded, or false otherwise.
*/
bool SetServerName(ByteCursor &serverName) noexcept;
/**
* Sets list of protocols (semi-colon delimited in priority order) used for ALPN extension.
* alpnList is copied.
*
* @return true if the copy succeeded, or false otherwise.
*/
bool SetAlpnList(const char *alpnList) noexcept;
/**
* @return true if the instance is in a valid state, false otherwise.
*/
explicit operator bool() const noexcept { return isValid(); }
/**
* @return the value of the last aws error encountered by operations on this instance.
*/
int LastError() const noexcept { return m_lastError; }
/// @private
const aws_tls_connection_options *GetUnderlyingHandle() const noexcept
{
return &m_tls_connection_options;
}
private:
bool isValid() const noexcept { return m_isInit; }
TlsConnectionOptions(aws_tls_ctx *ctx, Allocator *allocator) noexcept;
aws_tls_connection_options m_tls_connection_options;
aws_allocator *m_allocator;
int m_lastError;
bool m_isInit;
friend class TlsContext;
};
/**
* Stateful context for TLS with a given configuration. Per-connection TLS "contexts"
* (TlsConnectionOptions) are instantiated from this as needed.
*/
class AWS_CRT_CPP_API TlsContext final
{
public:
TlsContext() noexcept;
TlsContext(TlsContextOptions &options, TlsMode mode, Allocator *allocator = ApiAllocator()) noexcept;
~TlsContext() = default;
TlsContext(const TlsContext &) noexcept = default;
TlsContext &operator=(const TlsContext &) noexcept = default;
TlsContext(TlsContext &&) noexcept = default;
TlsContext &operator=(TlsContext &&) noexcept = default;
/**
* @return a new connection-specific TLS context that can be configured with per-connection options
* (server name, peer verification, etc...)
*/
TlsConnectionOptions NewConnectionOptions() const noexcept;
/**
* @return true if the instance is in a valid state, false otherwise.
*/
explicit operator bool() const noexcept { return isValid(); }
/**
* @return the value of the last aws error encountered by operations on this instance.
*/
int GetInitializationError() const noexcept { return m_initializationError; }
/// @private
aws_tls_ctx *GetUnderlyingHandle() const noexcept { return m_ctx.get(); }
private:
bool isValid() const noexcept { return m_ctx && m_initializationError == AWS_ERROR_SUCCESS; }
std::shared_ptr<aws_tls_ctx> m_ctx;
int m_initializationError;
};
using NewTlsContextImplCallback = std::function<void *(TlsContextOptions &, TlsMode, Allocator *)>;
using DeleteTlsContextImplCallback = std::function<void(void *)>;
using IsTlsAlpnSupportedCallback = std::function<bool()>;
/**
* BYO_CRYPTO: TLS channel-handler base class.
*/
class AWS_CRT_CPP_API TlsChannelHandler : public ChannelHandler
{
public:
virtual ~TlsChannelHandler();
/**
* @return negotiated protocol (or empty string if no agreed upon protocol)
*/
virtual String GetProtocol() const = 0;
protected:
TlsChannelHandler(
struct aws_channel_slot *slot,
const struct aws_tls_connection_options &options,
Allocator *allocator = ApiAllocator());
/**
* Invoke this function from inside your handler after TLS negotiation completes. errorCode ==
* AWS_ERROR_SUCCESS or 0 means the session was successfully established and the connection should
* continue on.
*/
void CompleteTlsNegotiation(int errorCode);
private:
aws_tls_on_negotiation_result_fn *m_OnNegotiationResult;
void *m_userData;
aws_byte_buf m_protocolByteBuf;
friend aws_byte_buf(::aws_tls_handler_protocol)(aws_channel_handler *);
};
/**
* BYO_CRYPTO: Client TLS channel-handler base class.
*
* If using BYO_CRYPTO, you must define a concrete implementation
* and set its creation callback via ApiHandle.SetBYOCryptoClientTlsCallback().
*/
class AWS_CRT_CPP_API ClientTlsChannelHandler : public TlsChannelHandler
{
public:
/**
* Initiates the TLS session negotiation. This is called by the common runtime when it's time to start
* a new session.
*/
virtual void StartNegotiation() = 0;
protected:
ClientTlsChannelHandler(
struct aws_channel_slot *slot,
const struct aws_tls_connection_options &options,
Allocator *allocator = ApiAllocator());
};
using NewClientTlsHandlerCallback = std::function<std::shared_ptr<ClientTlsChannelHandler>(
struct aws_channel_slot *slot,
const struct aws_tls_connection_options &options,
Allocator *allocator)>;
} // namespace Io
} // namespace Crt
} // namespace Aws

View File

@@ -0,0 +1,105 @@
#pragma once
/**
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
* SPDX-License-Identifier: Apache-2.0.
*/
#include <aws/crt/Types.h>
#include <aws/io/uri.h>
namespace Aws
{
namespace Crt
{
namespace Io
{
/**
* Contains a URI used for networking application protocols. This type is move-only.
*/
class AWS_CRT_CPP_API Uri final
{
public:
Uri() noexcept;
~Uri();
/**
* Parses `cursor` as a URI. Upon failure the bool() operator will return false and LastError()
* will contain the errorCode.
*/
Uri(const ByteCursor &cursor, Allocator *allocator = ApiAllocator()) noexcept;
/**
* Builds a URI from `builderOptions`. Upon failure the bool() operator will return false and
* LastError() will contain the errorCode.
*/
Uri(aws_uri_builder_options &builderOptions, Allocator *allocator = ApiAllocator()) noexcept;
Uri(const Uri &);
Uri &operator=(const Uri &);
Uri(Uri &&uri) noexcept;
Uri &operator=(Uri &&) noexcept;
/**
* @return true if the instance is in a valid state, false otherwise.
*/
operator bool() const noexcept { return m_isInit; }
/**
* @return the value of the last aws error encountered by operations on this instance.
*/
int LastError() const noexcept { return m_lastError; }
/**
* @return the scheme portion of the URI if present (e.g. https, http, ftp etc....)
*/
ByteCursor GetScheme() const noexcept;
/**
* @return the authority portion of the URI if present. This will contain host name and port if
* specified.
* */
ByteCursor GetAuthority() const noexcept;
/**
* @return the path portion of the URI. If no path was present, this will be set to '/'.
*/
ByteCursor GetPath() const noexcept;
/**
* @return the query string portion of the URI if present.
*/
ByteCursor GetQueryString() const noexcept;
/**
* @return the host name portion of the authority. (port will not be in this value).
*/
ByteCursor GetHostName() const noexcept;
/**
* @return the port portion of the authority if a port was specified. If it was not, this will
* be set to 0. In that case, it is your responsibility to determine the correct port
* based on the protocol you're using.
*/
uint32_t GetPort() const noexcept;
/** @return the Path and Query portion of the URI. In the case of Http, this likely the value for the
* URI parameter.
*/
ByteCursor GetPathAndQuery() const noexcept;
/**
* @return The full URI as it was passed to or parsed from the constructors.
*/
ByteCursor GetFullUri() const noexcept;
private:
aws_uri m_uri;
int m_lastError;
bool m_isInit;
};
AWS_CRT_CPP_API Aws::Crt::String EncodeQueryParameterValue(ByteCursor paramValue);
} // namespace Io
} // namespace Crt
} // namespace Aws