diff options
author | dakovalkov <dakovalkov@yandex-team.com> | 2023-12-03 13:33:55 +0300 |
---|---|---|
committer | dakovalkov <dakovalkov@yandex-team.com> | 2023-12-03 14:04:39 +0300 |
commit | 2a718325637e5302334b6d0a6430f63168f8dbb3 (patch) | |
tree | 64be81080b7df9ec1d86d053a0c394ae53fcf1fe /contrib/restricted/aws/aws-crt-cpp | |
parent | e0d94a470142d95c3007e9c5d80380994940664a (diff) | |
download | ydb-2a718325637e5302334b6d0a6430f63168f8dbb3.tar.gz |
Update contrib/libs/aws-sdk-cpp to 1.11.37
Diffstat (limited to 'contrib/restricted/aws/aws-crt-cpp')
86 files changed, 25594 insertions, 0 deletions
diff --git a/contrib/restricted/aws/aws-crt-cpp/CMakeLists.darwin-arm64.txt b/contrib/restricted/aws/aws-crt-cpp/CMakeLists.darwin-arm64.txt new file mode 100644 index 0000000000..6c2e3fb3bc --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/CMakeLists.darwin-arm64.txt @@ -0,0 +1,90 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(restricted-aws-aws-crt-cpp) +target_compile_options(restricted-aws-aws-crt-cpp PRIVATE + -DAWS_AUTH_USE_IMPORT_EXPORT + -DAWS_CAL_USE_IMPORT_EXPORT + -DAWS_CHECKSUMS_USE_IMPORT_EXPORT + -DAWS_COMMON_USE_IMPORT_EXPORT + -DAWS_COMPRESSION_USE_IMPORT_EXPORT + -DAWS_CRT_CPP_USE_IMPORT_EXPORT + -DAWS_EVENT_STREAM_USE_IMPORT_EXPORT + -DAWS_HTTP_USE_IMPORT_EXPORT + -DAWS_IO_USE_IMPORT_EXPORT + -DAWS_MQTT_USE_IMPORT_EXPORT + -DAWS_MQTT_WITH_WEBSOCKETS + -DAWS_S3_USE_IMPORT_EXPORT + -DAWS_SDKUTILS_USE_IMPORT_EXPORT + -DAWS_USE_EPOLL + -DCJSON_HIDE_SYMBOLS + -DS2N_CLONE_SUPPORTED + -DS2N_CPUID_AVAILABLE + -DS2N_FALL_THROUGH_SUPPORTED + -DS2N_FEATURES_AVAILABLE + -DS2N_KYBER512R3_AVX2_BMI2 + -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD5_SHA1_HASH + -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX + -DS2N_LIBCRYPTO_SUPPORTS_EVP_RC4 + -DS2N_MADVISE_SUPPORTED + -DS2N_PLATFORM_SUPPORTS_KTLS + -DS2N_STACKTRACE + -DS2N___RESTRICT__SUPPORTED + $<IF:$<CXX_COMPILER_ID:MSVC>,,-Wno-everything> +) +target_include_directories(restricted-aws-aws-crt-cpp PUBLIC + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/include +) +target_link_libraries(restricted-aws-aws-crt-cpp PUBLIC + contrib-libs-cxxsupp + restricted-aws-aws-c-auth + restricted-aws-aws-c-cal + restricted-aws-aws-c-common + restricted-aws-aws-c-event-stream + restricted-aws-aws-c-http + restricted-aws-aws-c-io + restricted-aws-aws-c-mqtt + restricted-aws-aws-c-s3 + restricted-aws-aws-c-sdkutils +) +target_sources(restricted-aws-aws-crt-cpp PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Allocator.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Api.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/DateTime.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/ImdsClient.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/JsonObject.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/StringUtils.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Types.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/UUID.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/auth/Credentials.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/auth/Sigv4Signing.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/crypto/HMAC.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/crypto/Hash.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/endpoints/RuleEngine.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/external/cJSON.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnection.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnectionManager.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpProxyStrategy.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpRequestResponse.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Bootstrap.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/ChannelHandler.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/EventLoopGroup.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/HostResolver.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Pkcs11.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/SocketOptions.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Stream.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/TlsOptions.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Uri.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/Mqtt5Client.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttClient.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttCommon.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Client.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Packets.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/MqttClient.cpp +) diff --git a/contrib/restricted/aws/aws-crt-cpp/CMakeLists.darwin-x86_64.txt b/contrib/restricted/aws/aws-crt-cpp/CMakeLists.darwin-x86_64.txt new file mode 100644 index 0000000000..6c2e3fb3bc --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/CMakeLists.darwin-x86_64.txt @@ -0,0 +1,90 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(restricted-aws-aws-crt-cpp) +target_compile_options(restricted-aws-aws-crt-cpp PRIVATE + -DAWS_AUTH_USE_IMPORT_EXPORT + -DAWS_CAL_USE_IMPORT_EXPORT + -DAWS_CHECKSUMS_USE_IMPORT_EXPORT + -DAWS_COMMON_USE_IMPORT_EXPORT + -DAWS_COMPRESSION_USE_IMPORT_EXPORT + -DAWS_CRT_CPP_USE_IMPORT_EXPORT + -DAWS_EVENT_STREAM_USE_IMPORT_EXPORT + -DAWS_HTTP_USE_IMPORT_EXPORT + -DAWS_IO_USE_IMPORT_EXPORT + -DAWS_MQTT_USE_IMPORT_EXPORT + -DAWS_MQTT_WITH_WEBSOCKETS + -DAWS_S3_USE_IMPORT_EXPORT + -DAWS_SDKUTILS_USE_IMPORT_EXPORT + -DAWS_USE_EPOLL + -DCJSON_HIDE_SYMBOLS + -DS2N_CLONE_SUPPORTED + -DS2N_CPUID_AVAILABLE + -DS2N_FALL_THROUGH_SUPPORTED + -DS2N_FEATURES_AVAILABLE + -DS2N_KYBER512R3_AVX2_BMI2 + -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD5_SHA1_HASH + -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX + -DS2N_LIBCRYPTO_SUPPORTS_EVP_RC4 + -DS2N_MADVISE_SUPPORTED + -DS2N_PLATFORM_SUPPORTS_KTLS + -DS2N_STACKTRACE + -DS2N___RESTRICT__SUPPORTED + $<IF:$<CXX_COMPILER_ID:MSVC>,,-Wno-everything> +) +target_include_directories(restricted-aws-aws-crt-cpp PUBLIC + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/include +) +target_link_libraries(restricted-aws-aws-crt-cpp PUBLIC + contrib-libs-cxxsupp + restricted-aws-aws-c-auth + restricted-aws-aws-c-cal + restricted-aws-aws-c-common + restricted-aws-aws-c-event-stream + restricted-aws-aws-c-http + restricted-aws-aws-c-io + restricted-aws-aws-c-mqtt + restricted-aws-aws-c-s3 + restricted-aws-aws-c-sdkutils +) +target_sources(restricted-aws-aws-crt-cpp PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Allocator.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Api.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/DateTime.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/ImdsClient.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/JsonObject.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/StringUtils.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Types.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/UUID.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/auth/Credentials.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/auth/Sigv4Signing.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/crypto/HMAC.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/crypto/Hash.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/endpoints/RuleEngine.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/external/cJSON.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnection.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnectionManager.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpProxyStrategy.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpRequestResponse.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Bootstrap.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/ChannelHandler.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/EventLoopGroup.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/HostResolver.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Pkcs11.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/SocketOptions.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Stream.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/TlsOptions.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Uri.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/Mqtt5Client.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttClient.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttCommon.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Client.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Packets.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/MqttClient.cpp +) diff --git a/contrib/restricted/aws/aws-crt-cpp/CMakeLists.linux-aarch64.txt b/contrib/restricted/aws/aws-crt-cpp/CMakeLists.linux-aarch64.txt new file mode 100644 index 0000000000..04fd641c30 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/CMakeLists.linux-aarch64.txt @@ -0,0 +1,91 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(restricted-aws-aws-crt-cpp) +target_compile_options(restricted-aws-aws-crt-cpp PRIVATE + -DAWS_AUTH_USE_IMPORT_EXPORT + -DAWS_CAL_USE_IMPORT_EXPORT + -DAWS_CHECKSUMS_USE_IMPORT_EXPORT + -DAWS_COMMON_USE_IMPORT_EXPORT + -DAWS_COMPRESSION_USE_IMPORT_EXPORT + -DAWS_CRT_CPP_USE_IMPORT_EXPORT + -DAWS_EVENT_STREAM_USE_IMPORT_EXPORT + -DAWS_HTTP_USE_IMPORT_EXPORT + -DAWS_IO_USE_IMPORT_EXPORT + -DAWS_MQTT_USE_IMPORT_EXPORT + -DAWS_MQTT_WITH_WEBSOCKETS + -DAWS_S3_USE_IMPORT_EXPORT + -DAWS_SDKUTILS_USE_IMPORT_EXPORT + -DAWS_USE_EPOLL + -DCJSON_HIDE_SYMBOLS + -DS2N_CLONE_SUPPORTED + -DS2N_CPUID_AVAILABLE + -DS2N_FALL_THROUGH_SUPPORTED + -DS2N_FEATURES_AVAILABLE + -DS2N_KYBER512R3_AVX2_BMI2 + -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD5_SHA1_HASH + -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX + -DS2N_LIBCRYPTO_SUPPORTS_EVP_RC4 + -DS2N_MADVISE_SUPPORTED + -DS2N_PLATFORM_SUPPORTS_KTLS + -DS2N_STACKTRACE + -DS2N___RESTRICT__SUPPORTED + $<IF:$<CXX_COMPILER_ID:MSVC>,,-Wno-everything> +) +target_include_directories(restricted-aws-aws-crt-cpp PUBLIC + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/include +) +target_link_libraries(restricted-aws-aws-crt-cpp PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + restricted-aws-aws-c-auth + restricted-aws-aws-c-cal + restricted-aws-aws-c-common + restricted-aws-aws-c-event-stream + restricted-aws-aws-c-http + restricted-aws-aws-c-io + restricted-aws-aws-c-mqtt + restricted-aws-aws-c-s3 + restricted-aws-aws-c-sdkutils +) +target_sources(restricted-aws-aws-crt-cpp PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Allocator.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Api.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/DateTime.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/ImdsClient.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/JsonObject.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/StringUtils.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Types.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/UUID.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/auth/Credentials.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/auth/Sigv4Signing.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/crypto/HMAC.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/crypto/Hash.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/endpoints/RuleEngine.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/external/cJSON.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnection.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnectionManager.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpProxyStrategy.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpRequestResponse.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Bootstrap.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/ChannelHandler.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/EventLoopGroup.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/HostResolver.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Pkcs11.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/SocketOptions.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Stream.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/TlsOptions.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Uri.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/Mqtt5Client.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttClient.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttCommon.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Client.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Packets.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/MqttClient.cpp +) diff --git a/contrib/restricted/aws/aws-crt-cpp/CMakeLists.linux-x86_64.txt b/contrib/restricted/aws/aws-crt-cpp/CMakeLists.linux-x86_64.txt new file mode 100644 index 0000000000..04fd641c30 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/CMakeLists.linux-x86_64.txt @@ -0,0 +1,91 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(restricted-aws-aws-crt-cpp) +target_compile_options(restricted-aws-aws-crt-cpp PRIVATE + -DAWS_AUTH_USE_IMPORT_EXPORT + -DAWS_CAL_USE_IMPORT_EXPORT + -DAWS_CHECKSUMS_USE_IMPORT_EXPORT + -DAWS_COMMON_USE_IMPORT_EXPORT + -DAWS_COMPRESSION_USE_IMPORT_EXPORT + -DAWS_CRT_CPP_USE_IMPORT_EXPORT + -DAWS_EVENT_STREAM_USE_IMPORT_EXPORT + -DAWS_HTTP_USE_IMPORT_EXPORT + -DAWS_IO_USE_IMPORT_EXPORT + -DAWS_MQTT_USE_IMPORT_EXPORT + -DAWS_MQTT_WITH_WEBSOCKETS + -DAWS_S3_USE_IMPORT_EXPORT + -DAWS_SDKUTILS_USE_IMPORT_EXPORT + -DAWS_USE_EPOLL + -DCJSON_HIDE_SYMBOLS + -DS2N_CLONE_SUPPORTED + -DS2N_CPUID_AVAILABLE + -DS2N_FALL_THROUGH_SUPPORTED + -DS2N_FEATURES_AVAILABLE + -DS2N_KYBER512R3_AVX2_BMI2 + -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD5_SHA1_HASH + -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX + -DS2N_LIBCRYPTO_SUPPORTS_EVP_RC4 + -DS2N_MADVISE_SUPPORTED + -DS2N_PLATFORM_SUPPORTS_KTLS + -DS2N_STACKTRACE + -DS2N___RESTRICT__SUPPORTED + $<IF:$<CXX_COMPILER_ID:MSVC>,,-Wno-everything> +) +target_include_directories(restricted-aws-aws-crt-cpp PUBLIC + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/include +) +target_link_libraries(restricted-aws-aws-crt-cpp PUBLIC + contrib-libs-linux-headers + contrib-libs-cxxsupp + restricted-aws-aws-c-auth + restricted-aws-aws-c-cal + restricted-aws-aws-c-common + restricted-aws-aws-c-event-stream + restricted-aws-aws-c-http + restricted-aws-aws-c-io + restricted-aws-aws-c-mqtt + restricted-aws-aws-c-s3 + restricted-aws-aws-c-sdkutils +) +target_sources(restricted-aws-aws-crt-cpp PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Allocator.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Api.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/DateTime.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/ImdsClient.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/JsonObject.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/StringUtils.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Types.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/UUID.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/auth/Credentials.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/auth/Sigv4Signing.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/crypto/HMAC.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/crypto/Hash.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/endpoints/RuleEngine.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/external/cJSON.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnection.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnectionManager.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpProxyStrategy.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpRequestResponse.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Bootstrap.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/ChannelHandler.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/EventLoopGroup.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/HostResolver.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Pkcs11.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/SocketOptions.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Stream.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/TlsOptions.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Uri.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/Mqtt5Client.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttClient.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttCommon.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Client.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Packets.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/MqttClient.cpp +) diff --git a/contrib/restricted/aws/aws-crt-cpp/CMakeLists.txt b/contrib/restricted/aws/aws-crt-cpp/CMakeLists.txt new file mode 100644 index 0000000000..2dce3a77fe --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/CMakeLists.txt @@ -0,0 +1,19 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + +if (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-aarch64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") + include(CMakeLists.darwin-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") + include(CMakeLists.darwin-arm64.txt) +elseif (WIN32 AND CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" AND NOT HAVE_CUDA) + include(CMakeLists.windows-x86_64.txt) +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux" AND CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" AND NOT HAVE_CUDA) + include(CMakeLists.linux-x86_64.txt) +endif() diff --git a/contrib/restricted/aws/aws-crt-cpp/CMakeLists.windows-x86_64.txt b/contrib/restricted/aws/aws-crt-cpp/CMakeLists.windows-x86_64.txt new file mode 100644 index 0000000000..6c2e3fb3bc --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/CMakeLists.windows-x86_64.txt @@ -0,0 +1,90 @@ + +# This file was generated by the build system used internally in the Yandex monorepo. +# Only simple modifications are allowed (adding source-files to targets, adding simple properties +# like target_include_directories). These modifications will be ported to original +# ya.make files by maintainers. Any complex modifications which can't be ported back to the +# original buildsystem will not be accepted. + + + +add_library(restricted-aws-aws-crt-cpp) +target_compile_options(restricted-aws-aws-crt-cpp PRIVATE + -DAWS_AUTH_USE_IMPORT_EXPORT + -DAWS_CAL_USE_IMPORT_EXPORT + -DAWS_CHECKSUMS_USE_IMPORT_EXPORT + -DAWS_COMMON_USE_IMPORT_EXPORT + -DAWS_COMPRESSION_USE_IMPORT_EXPORT + -DAWS_CRT_CPP_USE_IMPORT_EXPORT + -DAWS_EVENT_STREAM_USE_IMPORT_EXPORT + -DAWS_HTTP_USE_IMPORT_EXPORT + -DAWS_IO_USE_IMPORT_EXPORT + -DAWS_MQTT_USE_IMPORT_EXPORT + -DAWS_MQTT_WITH_WEBSOCKETS + -DAWS_S3_USE_IMPORT_EXPORT + -DAWS_SDKUTILS_USE_IMPORT_EXPORT + -DAWS_USE_EPOLL + -DCJSON_HIDE_SYMBOLS + -DS2N_CLONE_SUPPORTED + -DS2N_CPUID_AVAILABLE + -DS2N_FALL_THROUGH_SUPPORTED + -DS2N_FEATURES_AVAILABLE + -DS2N_KYBER512R3_AVX2_BMI2 + -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD5_SHA1_HASH + -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX + -DS2N_LIBCRYPTO_SUPPORTS_EVP_RC4 + -DS2N_MADVISE_SUPPORTED + -DS2N_PLATFORM_SUPPORTS_KTLS + -DS2N_STACKTRACE + -DS2N___RESTRICT__SUPPORTED + $<IF:$<CXX_COMPILER_ID:MSVC>,,-Wno-everything> +) +target_include_directories(restricted-aws-aws-crt-cpp PUBLIC + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/include +) +target_link_libraries(restricted-aws-aws-crt-cpp PUBLIC + contrib-libs-cxxsupp + restricted-aws-aws-c-auth + restricted-aws-aws-c-cal + restricted-aws-aws-c-common + restricted-aws-aws-c-event-stream + restricted-aws-aws-c-http + restricted-aws-aws-c-io + restricted-aws-aws-c-mqtt + restricted-aws-aws-c-s3 + restricted-aws-aws-c-sdkutils +) +target_sources(restricted-aws-aws-crt-cpp PRIVATE + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Allocator.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Api.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/DateTime.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/ImdsClient.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/JsonObject.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/StringUtils.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/Types.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/UUID.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/auth/Credentials.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/auth/Sigv4Signing.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/crypto/HMAC.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/crypto/Hash.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/endpoints/RuleEngine.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/external/cJSON.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnection.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnectionManager.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpProxyStrategy.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/http/HttpRequestResponse.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Bootstrap.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/ChannelHandler.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/EventLoopGroup.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/HostResolver.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Pkcs11.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/SocketOptions.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Stream.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/TlsOptions.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/io/Uri.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/Mqtt5Client.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttClient.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttCommon.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Client.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Packets.cpp + ${CMAKE_SOURCE_DIR}/contrib/restricted/aws/aws-crt-cpp/source/mqtt/MqttClient.cpp +) diff --git a/contrib/restricted/aws/aws-crt-cpp/CODE_OF_CONDUCT.md b/contrib/restricted/aws/aws-crt-cpp/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..3b64466870 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/CODE_OF_CONDUCT.md @@ -0,0 +1,4 @@ +## Code of Conduct +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +opensource-codeofconduct@amazon.com with any additional questions or comments. diff --git a/contrib/restricted/aws/aws-crt-cpp/CONTRIBUTING.md b/contrib/restricted/aws/aws-crt-cpp/CONTRIBUTING.md new file mode 100644 index 0000000000..666c37d6c0 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/CONTRIBUTING.md @@ -0,0 +1,62 @@ +# Contributing Guidelines + +Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional +documentation, we greatly value feedback and contributions from our community. + +Please read through this document before submitting any issues or pull requests to ensure we have all the necessary +information to effectively respond to your bug report or contribution. + + +## Reporting Bugs/Feature Requests + +We welcome you to use the GitHub issue tracker to report bugs or suggest features. + +When filing an issue, please check [existing open](https://github.com/awslabs/aws-crt-cpp/issues), or [recently closed](https://github.com/awslabs/aws-crt-cpp/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already +reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: + +* A reproducible test case or series of steps +* The version of our code being used +* Any modifications you've made relevant to the bug +* Anything unusual about your environment or deployment + + +## Contributing via Pull Requests +Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: + +1. You are working against the latest source on the *main* branch. +2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. +3. You open an issue to discuss any significant work - we would hate for your time to be wasted. + +To send us a pull request, please: + +1. Fork the repository. +2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. +3. Ensure local tests pass. +4. Commit to your fork using clear commit messages. +5. Send us a pull request, answering any default questions in the pull request interface. +6. Wait for a repository collaborator to look at your pull request, run the automated tests, and review. If additional changes or discussion is needed, a collaborator will get back to you, so please stay involved in the conversation. + * Note: pull requests from forks will not run the automated tests without collaborator involvement for security reasons. If you make a pull request and see that the tests are pending, this is normal and expected. + +GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and +[creating a pull request](https://help.github.com/articles/creating-a-pull-request/). + + +## Finding contributions to work on +Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/awslabs/aws-crt-cpp/labels/help%20wanted) issues is a great place to start. + + +## Code of Conduct +This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). +For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact +opensource-codeofconduct@amazon.com with any additional questions or comments. + + +## Security issue notifications +If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. + + +## Licensing + +See the [LICENSE](https://github.com/awslabs/aws-crt-cpp/blob/main/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. + +We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. diff --git a/contrib/restricted/aws/aws-crt-cpp/LICENSE b/contrib/restricted/aws/aws-crt-cpp/LICENSE new file mode 100644 index 0000000000..d645695673 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/contrib/restricted/aws/aws-crt-cpp/NOTICE b/contrib/restricted/aws/aws-crt-cpp/NOTICE new file mode 100644 index 0000000000..8b820137a0 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/NOTICE @@ -0,0 +1,3 @@ +AWS Crt Cpp +Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. +SPDX-License-Identifier: Apache-2.0. diff --git a/contrib/restricted/aws/aws-crt-cpp/README.md b/contrib/restricted/aws/aws-crt-cpp/README.md new file mode 100644 index 0000000000..ba5adf52e3 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/README.md @@ -0,0 +1,144 @@ +## AWS Crt Cpp + +C++ wrapper around the aws-c-* libraries. Provides Cross-Platform Transport Protocols and SSL/TLS implementations for C++. + +### Documentation + +https://awslabs.github.io/aws-crt-cpp/ + +### Currently Included: + +* aws-c-common: Cross-platform primitives and data structures. +* aws-c-io: Cross-platform event-loops, non-blocking I/O, and TLS implementations. +* aws-c-mqtt: MQTT client. +* aws-c-auth: Auth signers such as Aws-auth sigv4 +* aws-c-http: HTTP 1.1 client, and websockets (H2 coming soon) +* aws-checksums: Cross-Platform HW accelerated CRC32c and CRC32 with fallback to efficient SW implementations. +* aws-c-event-stream: C99 implementation of the vnd.amazon.event-stream content-type. + +More protocols and utilities are coming soon, so stay tuned. + +## Building + +The C99 libraries are already included for your convenience as submodules. +You should perform a recursive clone `git clone --recursive` or initialize the submodules via +`git submodule update --init`. These dependencies are compiled by CMake as part of the build process. + +If you want to manage these dependencies manually (e.g. you're using them in other projects), configure CMake with +`-DBUILD_DEPS=OFF` and `-DCMAKE_PREFIX_PATH=<install>` pointing to the absolute path where you have them installed. + +### MSVC +If you want to use a statically linked MSVCRT (/MT, /MTd), you can add `-DSTATIC_CRT=ON` to your cmake configuration. + +### Apple Silicon (aka M1) and Universal Binaries + +aws-crt-cpp supports both `arm64` and `x86_64` architectures. +Configure cmake with `-DCMAKE_OSX_ARCHITECTURES=arm64` to target Apple silicon, +or `-DCMAKE_OSX_ARCHITECTURES=x86_64` to target Intel. +If you wish to create a [universal binary](https://developer.apple.com/documentation/apple-silicon/building-a-universal-macos-binary), +you should use `lipo` to combine the `x86_64` and `arm64` binaries. +For example: `lipo -create -output universal_app x86_app arm_app` + +You SHOULD NOT build for both architectures simultaneously via `-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"`. +aws-crt-cpp has not been tested in this configuration. +aws-crt-cpp's cmake configuration scripts are known to get confused by this, +and will not enable optimizations that would benefit an independent `arm64` or `x86_64` build. + +### OpenSSL and LibCrypto (Unix only) + +If your application uses OpenSSL, configure with `-DUSE_OPENSSL=ON`. + +aws-crt-cpp does not use OpenSSL for TLS. +On Apple and Windows devices, the OS's default TLS library is used. +On Unix devices, [s2n-tls](https://github.com/aws/s2n-tls) is used. +But s2n-tls uses libcrypto, the cryptography math library bundled with OpenSSL. +To simplify the build process, the source code for s2n-tls and libcrypto are +included as git submodules and built along with aws-crt-cpp. +But if your application is also loading the system installation of OpenSSL +(i.e. your application uses libcurl which uses libssl which uses libcrypto) +there may be crashes as the application tries to use two different versions of libcrypto at once. + +Setting `-DUSE_OPENSSL=ON` will cause aws-crt-cpp to link against your system's existing `libcrypto`, +instead of building its own copy. + +You can ignore all this on Windows and Apple platforms, where aws-crt-cpp uses the OS's default libraries for TLS and cryptography math. + +## Dependencies? + +There are no non-OS dependencies that AWS does not own, maintain, and ship. + +## Common Usage + +To do anything with IO, you'll need to create a few objects that will be used by the rest of the library. + +For example: + +```` + Aws::Crt::LoadErrorStrings(); +```` + +Will load error strings for debugging purposes. Since the C libraries use error codes, this will allow you to print the corresponding +error string for each error code. + +```` + Aws::Crt::ApiHandle apiHandle; +```` +This performs one-time static initialization of the library. You'll need it to do anything, so don't forget to create one. + +```` + Aws::Crt::Io::EventLoopGroup eventLoopGroup(<number of threads you want>); +```` +To use any of our APIs that perform IO you'll need at least one event-loop. An event-loop group is a collection of event-loops that +protocol implementations will load balance across. If you won't have very many connections (say, more than 100 or so), then you +most likely only want 1 thread. In this case, you want to pass a single instance of this to every client or server implementation of a protocol +you use in your application. In some advanced use cases, you may want to reserve a thread for different types of IO tasks. In that case, you can have an +instance of this class for each reservation. + +```` + Aws::Crt::Io::TlsContextOptions tlsCtxOptions = + Aws::Crt::Io::TlsContextOptions::InitClientWithMtls(certificatePath.c_str(), keyPath.c_str()); + /* + * If we have a custom CA, set that up here. + */ + if (!caFile.empty()) + { + tlsCtxOptions.OverrideDefaultTrustStore(nullptr, caFile.c_str()); + } + + uint16_t port = 8883; + if (Io::TlsContextOptions::IsAlpnSupported()) + { + /* + * Use ALPN to negotiate the mqtt protocol on a normal + * TLS port if possible. + */ + tlsCtxOptions.SetAlpnList("x-amzn-mqtt-ca"); + port = 443; + } + + Aws::Crt::Io::TlsContext tlsCtx(tlsCtxOptions, Io::TlsMode::CLIENT); +```` + +If you plan on using TLS, you will need a TlsContext. These are NOT CHEAP, so use as few as possible to perform your task. +If you're in client mode and not doing anything fancy (e.g. mutual TLS), then you can likely get away with using a single +instance for the entire application. + +```` +Aws::Crt::Io::ClientBootstrap bootstrap(eventLoopGroup); +```` + +Lastly, you will need a client or server bootstrap to use a client or server protocol implementation. Since everything is +non-blocking and event driven, this handles most of the "callback hell" inherent in the design. Assuming you aren't partitioning +threads for particular use-cases, you can have a single instance of this that you pass to multiple clients. + +## Mac-Only TLS Behavior + +Please note that on Mac, once a private key is used with a certificate, that certificate-key pair is imported into the Mac Keychain. All subsequent uses of that certificate will use the stored private key and ignore anything passed in programmatically. Beginning in v0.8.10, when a stored private key from the Keychain is used, the following will be logged at the "info" log level: + +``` +static: certificate has an existing certificate-key pair that was previously imported into the Keychain. Using key from Keychain instead of the one provided. +``` + +## License + +This library is licensed under the Apache 2.0 License. diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Allocator.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Allocator.h new file mode 100644 index 0000000000..d0193a24b7 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Allocator.h @@ -0,0 +1,47 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/common/common.h> +#include <aws/crt/Exports.h> + +namespace Aws +{ + namespace Crt + { + using Allocator = aws_allocator; + + /** + * Each object from this library can use an explicit allocator. + * If you construct an object without specifying an allocator, + * then THIS allocator is used instead. + * + * You can customize this allocator when initializing + * \ref ApiHandle::ApiHandle(Allocator*) "ApiHandle". + */ + AWS_CRT_CPP_API Allocator *ApiAllocator() noexcept; + + /** + * Returns the default implementation of an Allocator. + * + * If you initialize \ref ApiHandle::ApiHandle(Allocator*) "ApiHandle" + * without specifying a custom allocator, then this implementation is used. + */ + AWS_CRT_CPP_API Allocator *DefaultAllocatorImplementation() noexcept; + + /** + * @deprecated Use DefaultAllocatorImplementation() instead. + * DefaultAllocator() is too easily confused with ApiAllocator(). + */ + AWS_CRT_CPP_API Allocator *DefaultAllocator() noexcept; + + /** + * @deprecated Use ApiAllocator() instead, to avoid issues with delay-loaded DLLs. + * https://github.com/aws/aws-sdk-cpp/issues/1960 + */ + extern AWS_CRT_CPP_API Allocator *g_allocator; + + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Api.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Api.h new file mode 100644 index 0000000000..74fde424cf --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Api.h @@ -0,0 +1,218 @@ +#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/crypto/HMAC.h> +#include <aws/crt/crypto/Hash.h> +#include <aws/crt/mqtt/Mqtt5Client.h> +#include <aws/crt/mqtt/MqttClient.h> + +#include <aws/common/logging.h> + +namespace Aws +{ + namespace Crt + { + /** + * Detail level control for logging output + */ + enum class LogLevel + { + None = AWS_LL_NONE, + Fatal = AWS_LL_FATAL, + Error = AWS_LL_ERROR, + Warn = AWS_LL_WARN, + Info = AWS_LL_INFO, + Debug = AWS_LL_DEBUG, + Trace = AWS_LL_TRACE, + + Count + }; + + /** + * Should the API Handle destructor block on all shutdown/thread completion logic or not? + */ + enum class ApiHandleShutdownBehavior + { + Blocking, + NonBlocking + }; + + /** + * A singleton object representing the init/cleanup state of the entire CRT. It's invalid to have more than one + * active simultaneously and it's also invalid to use CRT functionality without one active. + */ + class AWS_CRT_CPP_API ApiHandle + { + public: + /** + * Customize the ApiAllocator(), which is be used by any objects + * constructed without an explicit allocator. + */ + ApiHandle(Allocator *allocator) noexcept; + ApiHandle() noexcept; + ~ApiHandle(); + ApiHandle(const ApiHandle &) = delete; + ApiHandle(ApiHandle &&) = delete; + ApiHandle &operator=(const ApiHandle &) = delete; + ApiHandle &operator=(ApiHandle &&) = delete; + + /** + * Initialize logging in awscrt. + * @param level: Display messages of this importance and higher. LogLevel.NoLogs will disable + * logging. + * @param filename: Logging destination, a file path from the disk. + */ + void InitializeLogging(LogLevel level, const char *filename); + + /** + * Initialize logging in awscrt. + * @param level: Display messages of this importance and higher. LogLevel.NoLogs will disable + * logging. + * @param fp: The FILE object for logging destination. + */ + void InitializeLogging(LogLevel level, FILE *fp); + + /** + * Configures the shutdown behavior of the api handle instance + * @param behavior desired shutdown behavior + */ + void SetShutdownBehavior(ApiHandleShutdownBehavior behavior); + + /** + * BYO_CRYPTO: set callback for creating MD5 hashes. + * If using BYO_CRYPTO, you must call this. + */ + void SetBYOCryptoNewMD5Callback(Crypto::CreateHashCallback &&callback); + + /** + * BYO_CRYPTO: set callback for creating SHA256 hashes. + * If using BYO_CRYPTO, you must call this. + */ + void SetBYOCryptoNewSHA256Callback(Crypto::CreateHashCallback &&callback); + + /** + * BYO_CRYPTO: set callback for creating Streaming SHA256 HMAC objects. + * If using BYO_CRYPTO, you must call this. + */ + void SetBYOCryptoNewSHA256HMACCallback(Crypto::CreateHMACCallback &&callback); + + /** + * BYO_CRYPTO: set callback for creating a ClientTlsChannelHandler. + * If using BYO_CRYPTO, you must call this prior to creating any client channels in the + * application. + */ + void SetBYOCryptoClientTlsCallback(Io::NewClientTlsHandlerCallback &&callback); + + /** + * BYO_CRYPTO: set callbacks for the TlsContext. + * If using BYO_CRYPTO, you need to call this function prior to creating a TlsContext. + * + * @param newCallback Create custom implementation object, to be stored inside TlsContext. + * Return nullptr if failure occurs. + * @param deleteCallback Destroy object that was created by newCallback. + * @param alpnCallback Return whether ALPN is supported. + */ + void SetBYOCryptoTlsContextCallbacks( + Io::NewTlsContextImplCallback &&newCallback, + Io::DeleteTlsContextImplCallback &&deleteCallback, + Io::IsTlsAlpnSupportedCallback &&alpnCallback); + + /// @private + static const Io::NewTlsContextImplCallback &GetBYOCryptoNewTlsContextImplCallback(); + /// @private + static const Io::DeleteTlsContextImplCallback &GetBYOCryptoDeleteTlsContextImplCallback(); + /// @private + static const Io::IsTlsAlpnSupportedCallback &GetBYOCryptoIsTlsAlpnSupportedCallback(); + + /** + * Gets the static default ClientBootstrap, creating it if necessary. + * + * This default will be used when a ClientBootstrap is not explicitly passed but is needed + * to allow the process to function. An example of this would be in the MQTT connection creation workflow. + * The default ClientBootstrap will use the default EventLoopGroup and HostResolver, creating them if + * necessary. + * + * The default ClientBootstrap will be automatically managed and released by the API handle when it's + * resources are being freed, not requiring any manual memory management. + * + * @return ClientBootstrap* A pointer to the static default ClientBootstrap + */ + static Io::ClientBootstrap *GetOrCreateStaticDefaultClientBootstrap(); + + /** + * Gets the static default EventLoopGroup, creating it if necessary. + * + * This default will be used when a EventLoopGroup is not explicitly passed but is needed + * to allow the process to function. An example of this would be in the MQTT connection creation workflow. + * + * The EventLoopGroup will automatically pick a default number of threads based on the system. You can + * manually adjust the number of threads being used by creating a EventLoopGroup and passing it through + * the SetDefaultEventLoopGroup function. + * + * The default EventLoopGroup will be automatically managed and released by the API handle when it's + * resources are being freed, not requiring any manual memory management. + * + * @return EventLoopGroup* A pointer to the static default EventLoopGroup + */ + static Io::EventLoopGroup *GetOrCreateStaticDefaultEventLoopGroup(); + + /** + * Gets the static default HostResolver, creating it if necessary. + * + * This default will be used when a HostResolver is not explicitly passed but is needed + * to allow the process to function. An example of this would be in the MQTT connection creation workflow. + * + * The HostResolver will be set to have a maximum of 8 entries by default. You can + * manually adjust the maximum number of entries being used by creating a HostResolver and passing it + * through the SetDefaultEventLoopGroup function. + * + * The default HostResolver will be automatically managed and released by the API handle when it's + * resources are being freed, not requiring any manual memory management. + * + * @return HostResolver* A pointer to the static default HostResolver + */ + static Io::HostResolver *GetOrCreateStaticDefaultHostResolver(); + + private: + void InitializeLoggingCommon(struct aws_logger_standard_options &options); + + aws_logger m_logger; + + ApiHandleShutdownBehavior m_shutdownBehavior; + + static Io::ClientBootstrap *s_static_bootstrap; + static std::mutex s_lock_client_bootstrap; + static void ReleaseStaticDefaultClientBootstrap(); + + static Io::EventLoopGroup *s_static_event_loop_group; + static std::mutex s_lock_event_loop_group; + static void ReleaseStaticDefaultEventLoopGroup(); + + static int s_host_resolver_default_max_hosts; + static Io::HostResolver *s_static_default_host_resolver; + static std::mutex s_lock_default_host_resolver; + static void ReleaseStaticDefaultHostResolver(); + }; + + /** + * Gets a string description of a CRT error code + * @param error error code to get a descriptive string for + * @return a string description of the error code + */ + AWS_CRT_CPP_API const char *ErrorDebugString(int error) noexcept; + + /** + * @return the value of the last aws error on the current thread. Return 0 if no aws-error raised before. + */ + AWS_CRT_CPP_API int LastError() noexcept; + + /** + * @return the value of the last aws error on the current thread. Return AWS_ERROR_UNKNOWN, if no aws-error + * raised before. + */ + AWS_CRT_CPP_API int LastErrorOrUnknown() noexcept; + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Config.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Config.h new file mode 100644 index 0000000000..99d1aae4c6 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Config.h @@ -0,0 +1,11 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#define AWS_CRT_CPP_VERSION "0.19.8" +#define AWS_CRT_CPP_VERSION_MAJOR 0 +#define AWS_CRT_CPP_VERSION_MINOR 19 +#define AWS_CRT_CPP_VERSION_PATCH 8 +#define AWS_CRT_CPP_GIT_HASH "" diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/DateTime.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/DateTime.h new file mode 100644 index 0000000000..1861d1620e --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/DateTime.h @@ -0,0 +1,198 @@ +#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/common/date_time.h> + +#include <chrono> + +namespace Aws +{ + namespace Crt + { + enum class DateFormat + { + RFC822 = AWS_DATE_FORMAT_RFC822, + ISO_8601 = AWS_DATE_FORMAT_ISO_8601, + AutoDetect = AWS_DATE_FORMAT_AUTO_DETECT, + }; + + enum class Month + { + January = AWS_DATE_MONTH_JANUARY, + February = AWS_DATE_MONTH_FEBRUARY, + March = AWS_DATE_MONTH_MARCH, + April = AWS_DATE_MONTH_APRIL, + May = AWS_DATE_MONTH_MAY, + June = AWS_DATE_MONTH_JUNE, + July = AWS_DATE_MONTH_JULY, + August = AWS_DATE_MONTH_AUGUST, + September = AWS_DATE_MONTH_SEPTEMBER, + October = AWS_DATE_MONTH_OCTOBER, + November = AWS_DATE_MONTH_NOVEMBER, + December = AWS_DATE_MONTH_DECEMBER, + }; + + enum class DayOfWeek + { + Sunday = AWS_DATE_DAY_OF_WEEK_SUNDAY, + Monday = AWS_DATE_DAY_OF_WEEK_MONDAY, + Tuesday = AWS_DATE_DAY_OF_WEEK_TUESDAY, + Wednesday = AWS_DATE_DAY_OF_WEEK_WEDNESDAY, + Thursday = AWS_DATE_DAY_OF_WEEK_THURSDAY, + Friday = AWS_DATE_DAY_OF_WEEK_FRIDAY, + Saturday = AWS_DATE_DAY_OF_WEEK_SATURDAY, + }; + + class AWS_CRT_CPP_API DateTime final + { + public: + /** + * Initializes time point to epoch + */ + DateTime() noexcept; + + /** + * Initializes time point to any other arbitrary timepoint + */ + DateTime(const std::chrono::system_clock::time_point &timepointToAssign) noexcept; + + /** + * Initializes time point to millis Since epoch + */ + DateTime(uint64_t millisSinceEpoch) noexcept; + + /** + * Initializes time point to epoch time in seconds.millis + */ + DateTime(double epoch_millis) noexcept; + + /** + * Initializes time point to value represented by timestamp and format. + */ + DateTime(const char *timestamp, DateFormat format) noexcept; + + bool operator==(const DateTime &other) const noexcept; + bool operator<(const DateTime &other) const noexcept; + bool operator>(const DateTime &other) const noexcept; + bool operator!=(const DateTime &other) const noexcept; + bool operator<=(const DateTime &other) const noexcept; + bool operator>=(const DateTime &other) const noexcept; + + DateTime operator+(const std::chrono::milliseconds &a) const noexcept; + DateTime operator-(const std::chrono::milliseconds &a) const noexcept; + + /** + * Assign from seconds.millis since epoch. + */ + DateTime &operator=(double secondsSinceEpoch) noexcept; + + /** + * Assign from millis since epoch. + */ + DateTime &operator=(uint64_t millisSinceEpoch) noexcept; + + /** + * Assign from another time_point + */ + DateTime &operator=(const std::chrono::system_clock::time_point &timepointToAssign) noexcept; + + /** + * Assign from an ISO8601 or RFC822 formatted string + */ + DateTime &operator=(const char *timestamp) noexcept; + + explicit operator bool() const noexcept; + int GetLastError() const noexcept; + + /** + * Convert dateTime to local time string using predefined format. + */ + bool ToLocalTimeString(DateFormat format, ByteBuf &outputBuf) const noexcept; + + /** + * Convert dateTime to GMT time string using predefined format. + */ + bool ToGmtString(DateFormat format, ByteBuf &outputBuf) const noexcept; + + /** + * Get the representation of this datetime as seconds.milliseconds since epoch + */ + double SecondsWithMSPrecision() const noexcept; + + /** + * Milliseconds since epoch of this datetime. + */ + uint64_t Millis() const noexcept; + + /** + * In the likely case this class doesn't do everything you need to do, here's a copy of the time_point + * structure. Have fun. + */ + std::chrono::system_clock::time_point UnderlyingTimestamp() const noexcept; + + /** + * Get the Year portion of this dateTime. localTime if true, return local time, otherwise return UTC + */ + uint16_t GetYear(bool localTime = false) const noexcept; + + /** + * Get the Month portion of this dateTime. localTime if true, return local time, otherwise return UTC + */ + Month GetMonth(bool localTime = false) const noexcept; + + /** + * Get the Day of the Month portion of this dateTime. localTime if true, return local time, otherwise return + * UTC + */ + uint8_t GetDay(bool localTime = false) const noexcept; + + /** + * Get the Day of the Week portion of this dateTime. localTime if true, return local time, otherwise return + * UTC + */ + DayOfWeek GetDayOfWeek(bool localTime = false) const noexcept; + + /** + * Get the Hour portion of this dateTime. localTime if true, return local time, otherwise return UTC + */ + uint8_t GetHour(bool localTime = false) const noexcept; + + /** + * Get the Minute portion of this dateTime. localTime if true, return local time, otherwise return UTC + */ + uint8_t GetMinute(bool localTime = false) const noexcept; + + /** + * Get the Second portion of this dateTime. localTime if true, return local time, otherwise return UTC + */ + uint8_t GetSecond(bool localTime = false) const noexcept; + + /** + * Get whether or not this dateTime is in Daylight savings time. localTime if true, return local time, + * otherwise return UTC + */ + bool IsDST(bool localTime = false) const noexcept; + + /** + * Get an instance of DateTime representing this very instant. + */ + static DateTime Now() noexcept; + + /** + * Computes the difference between two DateTime instances and returns the difference + * in milliseconds. + */ + std::chrono::milliseconds operator-(const DateTime &other) const noexcept; + + private: + aws_date_time m_date_time; + bool m_good; + }; + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Exports.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Exports.h new file mode 100644 index 0000000000..bd171dc736 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Exports.h @@ -0,0 +1,39 @@ +#pragma once + +/* + *Copyright 2010-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + *Licensed under the Apache License, Version 2.0 (the "License"). + *You may not use this file except in compliance with the License. + *A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#if defined(USE_WINDOWS_DLL_SEMANTICS) || defined(WIN32) +# ifdef _MSC_VER +# pragma warning(disable : 4251) +# endif // _MSC_VER +# ifdef AWS_CRT_CPP_USE_IMPORT_EXPORT +# ifdef AWS_CRT_CPP_EXPORTS +# define AWS_CRT_CPP_API __declspec(dllexport) +# else +# define AWS_CRT_CPP_API __declspec(dllimport) +# endif /* AWS_CRT_CPP_API */ +# else +# define AWS_CRT_CPP_API +# endif // AWS_CRT_CPP_USE_IMPORT_EXPORT + +#else // defined (USE_WINDOWS_DLL_SEMANTICS) || defined (WIN32) +# if ((__GNUC__ >= 4) || defined(__clang__)) && defined(AWS_CRT_CPP_USE_IMPORT_EXPORT) && \ + defined(AWS_CRT_CPP_EXPORTS) +# define AWS_CRT_CPP_API __attribute__((visibility("default"))) +# else +# define AWS_CRT_CPP_API +# endif // __GNUC__ >= 4 || defined(__clang__) +#endif diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/ImdsClient.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/ImdsClient.h new file mode 100644 index 0000000000..c73e2bf27c --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/ImdsClient.h @@ -0,0 +1,386 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/crt/DateTime.h> +#include <aws/crt/Exports.h> +#include <aws/crt/Types.h> +#include <functional> + +struct aws_credentials; +struct aws_imds_client; +struct aws_imds_instance_info; +struct aws_imds_iam_profile; + +namespace Aws +{ + + namespace Crt + { + + namespace Io + { + class ClientBootstrap; + } + + namespace Auth + { + class Credentials; + } + + namespace Imds + { + + struct AWS_CRT_CPP_API ImdsClientConfig + { + ImdsClientConfig() : Bootstrap(nullptr) {} + + /** + * Connection bootstrap to use to create the http connection required to + * query resource from the Ec2 instance metadata service + * + * Note: If null, then the default ClientBootstrap is used + * (see Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap) + */ + Io::ClientBootstrap *Bootstrap; + + /* Should add retry strategy support once that is available */ + }; + + /** + * https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-categories.html + */ + struct AWS_CRT_CPP_API IamProfileView + { + DateTime lastUpdated; + StringView instanceProfileArn; + StringView instanceProfileId; + }; + + /** + * A convenient class for you to persist data from IamProfileView, which has StringView members. + */ + struct AWS_CRT_CPP_API IamProfile + { + IamProfile() {} + IamProfile(const IamProfileView &other); + + IamProfile &operator=(const IamProfileView &other); + + DateTime lastUpdated; + String instanceProfileArn; + String instanceProfileId; + }; + + /** + * Block of per-instance EC2-specific data + * + * https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html + */ + struct AWS_CRT_CPP_API InstanceInfoView + { + /* an array of StringView */ + Vector<StringView> marketplaceProductCodes; + StringView availabilityZone; + StringView privateIp; + StringView version; + StringView instanceId; + /* an array of StringView */ + Vector<StringView> billingProducts; + StringView instanceType; + StringView accountId; + StringView imageId; + DateTime pendingTime; + StringView architecture; + StringView kernelId; + StringView ramdiskId; + StringView region; + }; + + /** + * A convenient class for you to persist data from InstanceInfoView, which has StringView members. + */ + struct AWS_CRT_CPP_API InstanceInfo + { + InstanceInfo() {} + InstanceInfo(const InstanceInfoView &other); + + InstanceInfo &operator=(const InstanceInfoView &other); + + /* an array of StringView */ + Vector<String> marketplaceProductCodes; + String availabilityZone; + String privateIp; + String version; + String instanceId; + /* an array of StringView */ + Vector<String> billingProducts; + String instanceType; + String accountId; + String imageId; + DateTime pendingTime; + String architecture; + String kernelId; + String ramdiskId; + String region; + }; + + using OnResourceAcquired = std::function<void(const StringView &resource, int errorCode, void *userData)>; + using OnVectorResourceAcquired = + std::function<void(const Vector<StringView> &resource, int errorCode, void *userData)>; + using OnCredentialsAcquired = + std::function<void(const Auth::Credentials &credentials, int errorCode, void *userData)>; + using OnIamProfileAcquired = + std::function<void(const IamProfileView &iamProfile, int errorCode, void *userData)>; + using OnInstanceInfoAcquired = + std::function<void(const InstanceInfoView &instanceInfo, int errorCode, void *userData)>; + + class AWS_CRT_CPP_API ImdsClient + { + public: + ImdsClient(const ImdsClientConfig &config, Allocator *allocator = ApiAllocator()) noexcept; + + ~ImdsClient(); + + ImdsClient(const ImdsClient &) = delete; + ImdsClient(ImdsClient &&) = delete; + ImdsClient &operator=(const ImdsClient &) = delete; + ImdsClient &operator=(ImdsClient &&) = delete; + + aws_imds_client *GetUnderlyingHandle() { return m_client; } + + /** + * Queries a generic resource (string) from the ec2 instance metadata document + * + * @param resourcePath path of the resource to query + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetResource(const StringView &resourcePath, OnResourceAcquired callback, void *userData); + + /** + * Gets the ami id of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetAmiId(OnResourceAcquired callback, void *userData); + + /** + * Gets the ami launch index of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetAmiLaunchIndex(OnResourceAcquired callback, void *userData); + + /** + * Gets the ami manifest path of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetAmiManifestPath(OnResourceAcquired callback, void *userData); + + /** + * Gets the list of ancestor ami ids of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetAncestorAmiIds(OnVectorResourceAcquired callback, void *userData); + + /** + * Gets the instance-action of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetInstanceAction(OnResourceAcquired callback, void *userData); + + /** + * Gets the instance id of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetInstanceId(OnResourceAcquired callback, void *userData); + + /** + * Gets the instance type of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetInstanceType(OnResourceAcquired callback, void *userData); + + /** + * Gets the mac address of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetMacAddress(OnResourceAcquired callback, void *userData); + + /** + * Gets the private ip address of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetPrivateIpAddress(OnResourceAcquired callback, void *userData); + + /** + * Gets the availability zone of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetAvailabilityZone(OnResourceAcquired callback, void *userData); + + /** + * Gets the product codes of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetProductCodes(OnResourceAcquired callback, void *userData); + + /** + * Gets the public key of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetPublicKey(OnResourceAcquired callback, void *userData); + + /** + * Gets the ramdisk id of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetRamDiskId(OnResourceAcquired callback, void *userData); + + /** + * Gets the reservation id of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetReservationId(OnResourceAcquired callback, void *userData); + + /** + * Gets the list of the security groups of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetSecurityGroups(OnVectorResourceAcquired callback, void *userData); + + /** + * Gets the list of block device mappings of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetBlockDeviceMapping(OnVectorResourceAcquired callback, void *userData); + + /** + * Gets the attached iam role of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetAttachedIamRole(OnResourceAcquired callback, void *userData); + + /** + * Gets temporary credentials based on the attached iam role of the ec2 instance + * + * @param iamRoleName iam role name to get temporary credentials through + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetCredentials(const StringView &iamRoleName, OnCredentialsAcquired callback, void *userData); + + /** + * Gets the iam profile information of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetIamProfile(OnIamProfileAcquired callback, void *userData); + + /** + * Gets the user data of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetUserData(OnResourceAcquired callback, void *userData); + + /** + * Gets the signature of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetInstanceSignature(OnResourceAcquired callback, void *userData); + + /** + * Gets the instance information data block of the ec2 instance from the instance metadata document + * + * @param callback callback function to invoke on query success or failure + * @param userData opaque data to invoke the completion callback with + * @return AWS_OP_SUCCESS if the query was successfully started, AWS_OP_ERR otherwise + */ + int GetInstanceInfo(OnInstanceInfoAcquired callback, void *userData); + + private: + static void s_onResourceAcquired(const aws_byte_buf *resource, int erroCode, void *userData); + + static void s_onVectorResourceAcquired(const aws_array_list *array, int errorCode, void *userData); + + static void s_onCredentialsAcquired(const aws_credentials *credentials, int errorCode, void *userData); + + static void s_onIamProfileAcquired( + const aws_imds_iam_profile *iamProfileInfo, + int errorCode, + void *userData); + + static void s_onInstanceInfoAcquired( + const aws_imds_instance_info *instanceInfo, + int error_code, + void *userData); + + aws_imds_client *m_client; + Allocator *m_allocator; + }; + + } // namespace Imds + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/JsonObject.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/JsonObject.h new file mode 100644 index 0000000000..feca11eb4f --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/JsonObject.h @@ -0,0 +1,406 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/StlAllocator.h> +#include <aws/crt/Types.h> + +namespace Aws +{ + struct cJSON; + + namespace Crt + { + class JsonView; + /** + * JSON DOM manipulation class. + * To read or serialize use @ref View function. + */ + class AWS_CRT_CPP_API JsonObject + { + public: + /** + * Constructs empty JSON DOM. + */ + JsonObject(); + + /** + * Constructs a JSON DOM by parsing the input string. + */ + JsonObject(const String &value); + + /** + * Performs a deep copy of the JSON DOM parameter. + * Prefer using a @ref JsonView if copying is not needed. + */ + JsonObject(const JsonObject &value); + + /** + * Moves the ownership of the internal JSON DOM. + * No copying is performed. + */ + JsonObject(JsonObject &&value) noexcept; + + ~JsonObject(); + + /** + * Performs a deep copy of the JSON DOM parameter. + */ + JsonObject &operator=(const JsonObject &other); + + /** + * Moves the ownership of the internal JSON DOM of the parameter to the current object. + * No copying is performed. + * A DOM currently owned by the object will be freed prior to copying. + * @warning This will result in invalidating any outstanding views of the current DOM. However, views + * to the moved-from DOM would still valid. + */ + JsonObject &operator=(JsonObject &&other) noexcept; + + bool operator==(const JsonObject &other) const; + bool operator!=(const JsonObject &other) const; + + /** + * Adds a string to the top level of this node with key. + */ + JsonObject &WithString(const String &key, const String &value); + JsonObject &WithString(const char *key, const String &value); + + /** + * Converts the current JSON node to a string. + */ + JsonObject &AsString(const String &value); + + /** + * Adds a bool value with key to the top level of this node. + */ + JsonObject &WithBool(const String &key, bool value); + JsonObject &WithBool(const char *key, bool value); + + /** + * Converts the current JSON node to a bool. + */ + JsonObject &AsBool(bool value); + + /** + * Adds an integer value at key at the top level of this node. + */ + JsonObject &WithInteger(const String &key, int value); + JsonObject &WithInteger(const char *key, int value); + + /** + * Converts the current JSON node to an integer. + */ + JsonObject &AsInteger(int value); + + /** + * Adds a 64-bit integer value at key to the top level of this node. + */ + JsonObject &WithInt64(const String &key, int64_t value); + JsonObject &WithInt64(const char *key, int64_t value); + + /** + * Converts the current JSON node to a 64-bit integer. + */ + JsonObject &AsInt64(int64_t value); + + /** + * Adds a double value at key at the top level of this node. + */ + JsonObject &WithDouble(const String &key, double value); + JsonObject &WithDouble(const char *key, double value); + + /** + * Converts the current JSON node to a double. + */ + JsonObject &AsDouble(double value); + + /** + * Adds an array of strings to the top level of this node at key. + */ + JsonObject &WithArray(const String &key, const Vector<String> &array); + JsonObject &WithArray(const char *key, const Vector<String> &array); + + /** + * Adds an array of arbitrary JSON objects to the top level of this node at key. + * The values in the array parameter will be deep-copied. + */ + JsonObject &WithArray(const String &key, const Vector<JsonObject> &array); + + /** + * Adds an array of arbitrary JSON objects to the top level of this node at key. + * The values in the array parameter will be moved-from. + */ + JsonObject &WithArray(const String &key, Vector<JsonObject> &&array); + + /** + * Converts the current JSON node to an array whose values are deep-copied from the array parameter. + */ + JsonObject &AsArray(const Vector<JsonObject> &array); + + /** + * Converts the current JSON node to an array whose values are moved from the array parameter. + */ + JsonObject &AsArray(Vector<JsonObject> &&array); + + /** + * Sets the current JSON node as null. + */ + JsonObject &AsNull(); + + /** + * Adds a JSON object to the top level of this node at key. + * The object parameter is deep-copied. + */ + JsonObject &WithObject(const String &key, const JsonObject &value); + JsonObject &WithObject(const char *key, const JsonObject &value); + + /** + * Adds a JSON object to the top level of this node at key. + */ + JsonObject &WithObject(const String &key, JsonObject &&value); + JsonObject &WithObject(const char *key, JsonObject &&value); + + /** + * Converts the current JSON node to a JSON object by deep-copying the parameter. + */ + JsonObject &AsObject(const JsonObject &value); + + /** + * Converts the current JSON node to a JSON object by moving from the parameter. + */ + JsonObject &AsObject(JsonObject &&value); + + /** + * Returns true if the last parse request was successful. If this returns false, + * you can call GetErrorMessage() to find the cause. + */ + inline bool WasParseSuccessful() const { return m_wasParseSuccessful; } + + /** + * Returns the last error message from a failed parse attempt. Returns empty string if no error. + */ + inline const String &GetErrorMessage() const { return m_errorMessage; } + + /** + * Creates a view from the current root JSON node. + */ + JsonView View() const; + + private: + void Destroy(); + JsonObject(cJSON *value); + cJSON *m_value; + bool m_wasParseSuccessful; + String m_errorMessage; + friend class JsonView; + }; + + /** + * Provides read-only view to an existing JsonObject. This allows lightweight copying without making deep + * copies of the JsonObject. + * Note: This class does not extend the lifetime of the given JsonObject. It's your responsibility to ensure + * the lifetime of the JsonObject is extended beyond the lifetime of its view. + */ + class AWS_CRT_CPP_API JsonView + { + public: + /* constructors */ + JsonView(); + JsonView(const JsonObject &val); + JsonView &operator=(const JsonObject &val); + + /** + * Gets a string from this node by its key. + */ + String GetString(const String &key) const; + /** + * Gets a string from this node by its key. + */ + String GetString(const char *key) const; + + /** + * Returns the value of this node as a string. + * The behavior is undefined if the node is _not_ of type string. + */ + String AsString() const; + + /** + * Gets a boolean value from this node by its key. + */ + bool GetBool(const String &key) const; + /** + * Gets a boolean value from this node by its key. + */ + bool GetBool(const char *key) const; + + /** + * Returns the value of this node as a boolean. + */ + bool AsBool() const; + + /** + * Gets an integer value from this node by its key. + * The integer is of the same size as an int on the machine. + */ + int GetInteger(const String &key) const; + /** + * Gets an integer value from this node by its key. + * The integer is of the same size as an int on the machine. + */ + int GetInteger(const char *key) const; + + /** + * Returns the value of this node as an int. + */ + int AsInteger() const; + + /** + * Gets a 64-bit integer value from this node by its key. + * The value is 64-bit regardless of the platform/machine. + */ + int64_t GetInt64(const String &key) const; + /** + * Gets a 64-bit integer value from this node by its key. + * The value is 64-bit regardless of the platform/machine. + */ + int64_t GetInt64(const char *key) const; + + /** + * Returns the value of this node as 64-bit integer. + */ + int64_t AsInt64() const; + + /** + * Gets a double precision floating-point value from this node by its key. + */ + double GetDouble(const String &key) const; + /** + * Gets a double precision floating-point value from this node by its key. + */ + double GetDouble(const char *key) const; + + /** + * Returns the value of this node as a double precision floating-point. + */ + double AsDouble() const; + + /** + * Gets an array of JsonView objects from this node by its key. + */ + Vector<JsonView> GetArray(const String &key) const; + /** + * Gets an array of JsonView objects from this node by its key. + */ + Vector<JsonView> GetArray(const char *key) const; + + /** + * Returns the value of this node as an array of JsonView objects. + */ + Vector<JsonView> AsArray() const; + + /** + * Gets a JsonView object from this node by its key. + */ + JsonView GetJsonObject(const String &key) const; + /** + * Gets a JsonView object from this node by its key. + */ + JsonView GetJsonObject(const char *key) const; + + JsonObject GetJsonObjectCopy(const String &key) const; + + JsonObject GetJsonObjectCopy(const char *key) const; + + /** + * Returns the value of this node as a JsonView object. + */ + JsonView AsObject() const; + + /** + * Reads all json objects at the top level of this node (does not traverse the tree any further) + * along with their keys. + */ + Map<String, JsonView> GetAllObjects() const; + + /** + * Tests whether a value exists at the current node level for the given key. + * Returns true if a value has been found and its value is not null, false otherwise. + */ + bool ValueExists(const String &key) const; + /** + * Tests whether a value exists at the current node level for the given key. + * Returns true if a value has been found and its value is not null, false otherwise. + */ + bool ValueExists(const char *key) const; + + /** + * Tests whether a key exists at the current node level. + */ + bool KeyExists(const String &key) const; + /** + * Tests whether a key exists at the current node level. + */ + bool KeyExists(const char *key) const; + + /** + * Tests whether the current value is a JSON object. + */ + bool IsObject() const; + + /** + * Tests whether the current value is a boolean. + */ + bool IsBool() const; + + /** + * Tests whether the current value is a string. + */ + bool IsString() const; + + /** + * Tests whether the current value is an int or int64_t. + * Returns false if the value is floating-point. + */ + bool IsIntegerType() const; + + /** + * Tests whether the current value is a floating-point. + */ + bool IsFloatingPointType() const; + + /** + * Tests whether the current value is a JSON array. + */ + bool IsListType() const; + + /** + * Tests whether the current value is NULL. + */ + bool IsNull() const; + + /** + * Writes the current JSON view without whitespace characters starting at the current level to a string. + * @param treatAsObject if the current value is empty, writes out '{}' rather than an empty string. + */ + String WriteCompact(bool treatAsObject = true) const; + + /** + * Writes the current JSON view to a string in a human friendly format. + * @param treatAsObject if the current value is empty, writes out '{}' rather than an empty string. + */ + String WriteReadable(bool treatAsObject = true) const; + + /** + * Creates a deep copy of the JSON value rooted in the current JSON view. + */ + JsonObject Materialize() const; + + private: + JsonView(cJSON *val); + JsonView &operator=(cJSON *val); + cJSON *m_value; + }; + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Optional.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Optional.h new file mode 100644 index 0000000000..5acc232557 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Optional.h @@ -0,0 +1,203 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <utility> + +namespace Aws +{ + namespace Crt + { + /** + * Custom implementation of an Option type. std::optional requires C++17 + * @tparam T type of the optional value + */ + template <typename T> class Optional + { + public: + Optional() : m_value(nullptr) {} + Optional(const T &val) + { + new (m_storage) T(val); + m_value = reinterpret_cast<T *>(m_storage); + } + + Optional(T &&val) + { + new (m_storage) T(std::forward<T>(val)); + m_value = reinterpret_cast<T *>(m_storage); + } + + ~Optional() + { + if (m_value) + { + m_value->~T(); + } + } + + template <typename U = T> Optional &operator=(U &&u) + { + if (m_value) + { + *m_value = std::forward<U>(u); + return *this; + } + + new (m_storage) T(std::forward<U>(u)); + m_value = reinterpret_cast<T *>(m_storage); + + return *this; + } + + Optional(const Optional<T> &other) + { + if (other.m_value) + { + new (m_storage) T(*other.m_value); + m_value = reinterpret_cast<T *>(m_storage); + } + else + { + m_value = nullptr; + } + } + + Optional(Optional<T> &&other) + { + if (other.m_value) + { + new (m_storage) T(std::forward<T>(*other.m_value)); + m_value = reinterpret_cast<T *>(m_storage); + } + else + { + m_value = nullptr; + } + } + + Optional &operator=(const Optional &other) + { + if (this == &other) + { + return *this; + } + + if (m_value) + { + if (other.m_value) + { + *m_value = *other.m_value; + } + else + { + m_value->~T(); + m_value = nullptr; + } + + return *this; + } + + if (other.m_value) + { + new (m_storage) T(*other.m_value); + m_value = reinterpret_cast<T *>(m_storage); + } + + return *this; + } + + template <typename U = T> Optional<T> &operator=(const Optional<U> &other) + { + if (this == &other) + { + return *this; + } + + if (m_value) + { + if (other.m_value) + { + *m_value = *other.m_value; + } + else + { + m_value->~T(); + m_value = nullptr; + } + + return *this; + } + + if (other.m_value) + { + new (m_storage) T(*other.m_value); + m_value = reinterpret_cast<T *>(m_storage); + } + + return *this; + } + + template <typename U = T> Optional<T> &operator=(Optional<U> &&other) + { + if (this == &other) + { + return *this; + } + + if (m_value) + { + if (other.m_value) + { + *m_value = std::forward<U>(*other.m_value); + } + else + { + m_value->~T(); + m_value = nullptr; + } + + return *this; + } + + if (other.m_value) + { + new (m_storage) T(std::forward<U>(*other.m_value)); + m_value = reinterpret_cast<T *>(m_storage); + } + + return *this; + } + + const T *operator->() const { return m_value; } + T *operator->() { return m_value; } + const T &operator*() const & { return *m_value; } + T &operator*() & { return *m_value; } + const T &&operator*() const && { return std::move(*m_value); } + T &&operator*() && { return std::move(*m_value); } + + explicit operator bool() const noexcept { return m_value != nullptr; } + bool has_value() const noexcept { return m_value != nullptr; } + + T &value() & { return *m_value; } + const T &value() const & { return *m_value; } + + T &&value() && { return std::move(*m_value); } + const T &&value() const && { return std::move(*m_value); } + + void reset() + { + if (m_value) + { + m_value->~T(); + m_value = nullptr; + } + } + + private: + alignas(T) char m_storage[sizeof(T)]; + T *m_value; + }; + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/RefCounted.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/RefCounted.h new file mode 100644 index 0000000000..d9ec11d818 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/RefCounted.h @@ -0,0 +1,68 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/common/assert.h> +#include <memory> +#include <mutex> + +namespace Aws +{ + namespace Crt + { + /** + * Inherit from RefCounted to allow reference-counting from C code, + * which will keep your C++ object alive as long as the count is non-zero. + * + * A class must inherit from RefCounted and std::enable_shared_from_this. + * Your class must always be placed inside a shared_ptr (do not create on + * the stack, or keep on the heap as a raw pointer). + * + * Whenever the reference count goes from 0 to 1 a shared_ptr is created + * internally to keep this object alive. Whenever the reference count + * goes from 1 to 0 the internal shared_ptr is reset, allowing this object + * to be destroyed. + */ + template <class T> class RefCounted + { + protected: + RefCounted() {} + ~RefCounted() {} + + void AcquireRef() + { + m_mutex.lock(); + if (m_count++ == 0) + { + m_strongPtr = static_cast<T *>(this)->shared_from_this(); + } + m_mutex.unlock(); + } + + void ReleaseRef() + { + // Move contents of m_strongPtr to a temp so that this + // object can't be destroyed until the function exits. + std::shared_ptr<T> tmpStrongPtr; + + m_mutex.lock(); + AWS_ASSERT(m_count > 0 && "refcount has gone negative"); + if (m_count-- == 1) + { + std::swap(m_strongPtr, tmpStrongPtr); + } + m_mutex.unlock(); + } + + private: + RefCounted(const RefCounted &) = delete; + RefCounted &operator=(const RefCounted &) = delete; + + size_t m_count = 0; + std::shared_ptr<T> m_strongPtr; + std::mutex m_mutex; + }; + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/StlAllocator.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/StlAllocator.h new file mode 100644 index 0000000000..13ec6ff68e --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/StlAllocator.h @@ -0,0 +1,63 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/crt/Allocator.h> + +#include <memory> +#include <type_traits> + +namespace Aws +{ + namespace Crt + { + /** + * Stateful allocator variant that uses an underlying CRT allocator + * @tparam T type that allocator can allocate + */ + template <typename T> class StlAllocator : public std::allocator<T> + { + public: + using Base = std::allocator<T>; + + StlAllocator() noexcept : Base() { m_allocator = ApiAllocator(); } + + StlAllocator(Allocator *allocator) noexcept : Base() { m_allocator = allocator; } + + StlAllocator(const StlAllocator<T> &a) noexcept : Base(a) { m_allocator = a.m_allocator; } + + template <class U> StlAllocator(const StlAllocator<U> &a) noexcept : Base(a) + { + m_allocator = a.m_allocator; + } + + ~StlAllocator() {} + + using size_type = std::size_t; + + template <typename U> struct rebind + { + typedef StlAllocator<U> other; + }; + + using RawPointer = typename std::allocator_traits<std::allocator<T>>::pointer; + + RawPointer allocate(size_type n, const void *hint = nullptr) + { + (void)hint; + AWS_ASSERT(m_allocator); + return static_cast<RawPointer>(aws_mem_acquire(m_allocator, n * sizeof(T))); + } + + void deallocate(RawPointer p, size_type) + { + AWS_ASSERT(m_allocator); + aws_mem_release(m_allocator, p); + } + + Allocator *m_allocator; + }; + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/StringUtils.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/StringUtils.h new file mode 100644 index 0000000000..7ab98c2b0f --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/StringUtils.h @@ -0,0 +1,21 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/Exports.h> + +#include <stddef.h> + +namespace Aws +{ + namespace Crt + { + /** + * C-string hash function + * @param str string to hash + * @return hash code of the string + */ + size_t AWS_CRT_CPP_API HashString(const char *str) noexcept; + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/StringView.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/StringView.h new file mode 100644 index 0000000000..b8c6c66881 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/StringView.h @@ -0,0 +1,864 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +/** + * To keep ABI compatability, we use CRT's own string view implementation even for C++ 17. + */ + +#include <algorithm> +#include <cassert> +#include <iterator> +#include <limits> +#include <stddef.h> +#include <type_traits> + +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# include <string_view> +#endif + +namespace Aws +{ + namespace Crt + { + /** + * Custom string view implementation in order to meet C++11 baseline + * @tparam CharT + * @tparam Traits + */ + template <typename CharT, typename Traits = std::char_traits<CharT>> class basic_string_view + { + public: + // types + using traits_type = Traits; + using value_type = CharT; + using pointer = value_type *; + using const_pointer = const value_type *; + using reference = value_type &; + using const_reference = const value_type &; + using const_iterator = const value_type *; + using iterator = const_iterator; + using const_reverse_iterator = std::reverse_iterator<const_iterator>; + using reverse_iterator = const_reverse_iterator; + using size_type = size_t; + using difference_type = ptrdiff_t; + static constexpr size_type npos = static_cast<size_type>(-1); + + // constructors and assignment + + constexpr basic_string_view() noexcept : m_size{0}, m_data{nullptr} {} + + constexpr basic_string_view(const basic_string_view &) noexcept = default; + + constexpr basic_string_view(const CharT *s) noexcept : m_size{traits_type::length(s)}, m_data{s} {} + + constexpr basic_string_view(const CharT *s, size_type count) noexcept : m_size{count}, m_data{s} {} + + basic_string_view &operator=(const basic_string_view &) noexcept = default; + +#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) + constexpr basic_string_view(const std::basic_string_view<CharT, Traits> &other) noexcept + : m_size(other.size()), m_data(other.data()) + { + } + + basic_string_view &operator=(const std::basic_string_view<CharT, Traits> &other) noexcept + { + m_data = other->data(); + m_size = other->size(); + return *this; + } +#endif + // iterators + + constexpr const_iterator begin() const noexcept { return this->m_data; } + + constexpr const_iterator end() const noexcept { return this->m_data + this->m_size; } + + constexpr const_iterator cbegin() const noexcept { return this->m_data; } + + constexpr const_iterator cend() const noexcept { return this->m_data + this->m_size; } + + constexpr const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(this->end()); } + + constexpr const_reverse_iterator rend() const noexcept { return const_reverse_iterator(this->begin()); } + + constexpr const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(this->end()); } + + constexpr const_reverse_iterator crend() const noexcept { return const_reverse_iterator(this->begin()); } + + constexpr size_type size() const noexcept { return this->m_size; } + + constexpr size_type length() const noexcept { return this->m_size; } + + constexpr size_type max_size() const noexcept { return (std::numeric_limits<size_type>::max)(); } + + constexpr bool empty() const noexcept { return this->m_size == 0; } + + // element accessors + + const_reference operator[](size_type pos) const noexcept + { + assert(pos < m_size); + return *(this->m_data + pos); + } + + const_reference at(size_type pos) const + { + assert(pos < m_size); + return *(this->m_data + pos); + } + + const_reference front() const noexcept + { + assert(m_size > 0); + return *this->m_data; + } + + const_reference back() const noexcept + { + assert(m_size > 0); + return *(this->m_data + this->m_size - 1); + } + + constexpr const_pointer data() const noexcept { return this->m_data; } + + // modifiers + void remove_prefix(size_type n) noexcept + { + assert(this->m_size >= n); + this->m_data += n; + this->m_size -= n; + } + + void remove_suffix(size_type n) noexcept { this->m_size -= n; } + + void swap(basic_string_view &other) noexcept + { + auto tmp = *this; + *this = other; + other = tmp; + } + + // string operations + size_type copy(CharT *s, size_type n, size_type pos = 0) const + { + assert(pos <= size()); + const size_type copyLen = (std::min)(n, m_size - pos); + traits_type::copy(s, data() + pos, copyLen); + return copyLen; + } + + basic_string_view substr(size_type pos = 0, size_type n = npos) const noexcept(false) + { + assert(pos <= size()); + const size_type copyLen = (std::min)(n, m_size - pos); + return basic_string_view{m_data + pos, copyLen}; + } + + int compare(const basic_string_view &s) const noexcept + { + const size_type compareLen = (std::min)(this->m_size, s.m_size); + int ret = traits_type::compare(this->m_data, s.m_data, compareLen); + if (ret == 0) + { + ret = _s_compare(this->m_size, s.m_size); + } + return ret; + } + + constexpr int compare(size_type pos1, size_type n1, const basic_string_view &s) const + { + return this->substr(pos1, n1).compare(s); + } + + constexpr int compare( + size_type pos1, + size_type n1, + const basic_string_view &s, + size_type pos2, + size_type n2) const + { + return this->substr(pos1, n1).compare(s.substr(pos2, n2)); + } + + constexpr int compare(const CharT *s) const noexcept { return this->compare(basic_string_view{s}); } + + constexpr int compare(size_type pos1, size_type n1, const CharT *s) const + { + return this->substr(pos1, n1).compare(basic_string_view{s}); + } + + constexpr int compare(size_type pos1, size_type n1, const CharT *s, size_type n2) const noexcept(false) + { + return this->substr(pos1, n1).compare(basic_string_view(s, n2)); + } + + constexpr bool starts_with(const basic_string_view &other) const noexcept + { + return this->substr(0, other.size()) == other; + } + + constexpr bool starts_with(CharT c) const noexcept + { + return !this->empty() && traits_type::eq(this->front(), c); + } + + constexpr bool starts_with(const CharT *s) const noexcept + { + return this->starts_with(basic_string_view(s)); + } + + constexpr bool ends_with(const basic_string_view &other) const noexcept + { + return this->m_size >= other.m_size && this->compare(this->m_size - other.m_size, npos, other) == 0; + } + + constexpr bool ends_with(CharT c) const noexcept + { + return !this->empty() && traits_type::eq(this->back(), c); + } + + constexpr bool ends_with(const CharT *s) const noexcept { return this->ends_with(basic_string_view(s)); } + + // find utilities + constexpr size_type find(const basic_string_view &s, size_type pos = 0) const noexcept + { + return this->find(s.m_data, pos, s.m_size); + } + + size_type find(CharT c, size_type pos = 0) const noexcept + { + if (pos >= m_size) + { + return npos; + } + const CharT *r = Traits::find(m_data + pos, m_size - pos, c); + if (r == nullptr) + { + return npos; + } + return static_cast<size_type>(r - m_data); + } + + size_type find(const CharT *s, size_type pos, size_type n) const noexcept + { + if (n && !s) + { + return npos; + } + + if (pos > m_size) + { + return npos; + } + + if (n == 0) + { + return pos; + } + + const CharT *r = _s_search_substr(m_data + pos, m_data + m_size, s, s + n); + + if (r == m_data + m_size) + { + return npos; + } + return static_cast<size_type>(r - m_data); + } + + constexpr size_type find(const CharT *s, size_type pos = 0) const noexcept + { + return this->find(s, pos, traits_type::length(s)); + } + + size_type rfind(basic_string_view s, size_type pos = npos) const noexcept + { + if (s.m_size && !s.m_data) + { + return npos; + } + return this->rfind(s.m_data, pos, s.m_size); + } + + size_type rfind(CharT c, size_type pos = npos) const noexcept + { + if (m_size <= 0) + { + return npos; + } + + if (pos < m_size) + { + ++pos; + } + else + { + pos = m_size; + } + + for (const CharT *ptr = m_data + pos; ptr != m_data;) + { + if (Traits::eq(*--ptr, c)) + { + return static_cast<size_type>(ptr - m_data); + } + } + return npos; + } + + size_type rfind(const CharT *s, size_type pos, size_type n) const noexcept + { + if (n && !s) + { + return npos; + } + + pos = (std::min)(pos, m_size); + if (n < m_size - pos) + { + pos += n; + } + else + { + pos = m_size; + } + const CharT *r = _s_find_end(m_data, m_data + pos, s, s + n); + if (n > 0 && r == m_data + pos) + { + return npos; + } + return static_cast<size_type>(r - m_data); + } + + constexpr size_type rfind(const CharT *s, size_type pos = npos) const noexcept + { + return this->rfind(s, pos, traits_type::length(s)); + } + + constexpr size_type find_first_of(basic_string_view s, size_type pos = 0) const noexcept + { + return this->find_first_of(s.m_data, pos, s.m_size); + } + + constexpr size_type find_first_of(CharT c, size_type pos = 0) const noexcept { return this->find(c, pos); } + + size_type find_first_of(const CharT *s, size_type pos, size_type n) const noexcept + { + if (pos >= m_size || !n || !s) + { + return npos; + } + + const CharT *r = _s_find_first_of_ce(m_data + pos, m_data + m_size, s, s + n); + + if (r == m_data + m_size) + { + return npos; + } + + return static_cast<size_type>(r - m_data); + } + + constexpr size_type find_first_of(const CharT *s, size_type pos = 0) const noexcept + { + return this->find_first_of(s, pos, traits_type::length(s)); + } + + constexpr size_type find_last_of(basic_string_view s, size_type pos = npos) const noexcept + { + return this->find_last_of(s.m_data, pos, s.m_size); + } + + constexpr size_type find_last_of(CharT c, size_type pos = npos) const noexcept + { + return this->rfind(c, pos); + } + + size_type find_last_of(const CharT *s, size_type pos, size_type n) const noexcept + { + if (!n || s == nullptr) + { + return npos; + } + + if (pos < m_size) + { + ++pos; + } + else + { + pos = m_size; + } + + for (const CharT *ptr = m_data + pos; ptr != m_data;) + { + const CharT *r = Traits::find(s, n, *--ptr); + if (r) + { + return static_cast<size_type>(ptr - m_data); + } + } + + return npos; + } + + constexpr size_type find_last_of(const CharT *s, size_type pos = npos) const noexcept + { + return this->find_last_of(s, pos, traits_type::length(s)); + } + + size_type find_first_not_of(basic_string_view s, size_type pos = 0) const noexcept + { + if (s.m_size && !s.m_data) + { + return npos; + } + return this->find_first_not_of(s.m_data, pos, s.m_size); + } + + size_type find_first_not_of(CharT c, size_type pos = 0) const noexcept + { + if (!m_data || pos >= m_size) + { + return npos; + } + + const CharT *pend = m_data + m_size; + for (const CharT *ptr = m_data + pos; ptr != pend; ++ptr) + { + if (!Traits::eq(*ptr, c)) + { + return static_cast<size_type>(ptr - m_data); + } + } + + return npos; + } + + size_type find_first_not_of(const CharT *s, size_type pos, size_type n) const noexcept + { + if (n && s == nullptr) + { + return npos; + } + + if (m_data == nullptr || pos >= m_size) + { + return npos; + } + + const CharT *pend = m_data + m_size; + for (const CharT *ptr = m_data + pos; ptr != pend; ++ptr) + { + if (Traits::find(s, n, *ptr) == 0) + { + return static_cast<size_type>(ptr - m_data); + } + } + + return npos; + } + + constexpr size_type find_first_not_of(const CharT *s, size_type pos = 0) const noexcept + { + return this->find_first_not_of(s, pos, traits_type::length(s)); + } + + size_type find_last_not_of(basic_string_view s, size_type pos = npos) const noexcept + { + if (s.m_size && !s.m_data) + { + return npos; + } + return this->find_last_not_of(s.m_data, pos, s.m_size); + } + + size_type find_last_not_of(CharT c, size_type pos = npos) const noexcept + { + if (pos < m_size) + { + ++pos; + } + else + { + pos = m_size; + } + + for (const CharT *ptr = m_data + pos; ptr != m_data;) + { + if (!Traits::eq(*--ptr, c)) + { + return static_cast<size_type>(ptr - m_data); + } + } + return npos; + } + + size_type find_last_not_of(const CharT *s, size_type pos, size_type n) const noexcept + { + if (n && !s) + { + return npos; + } + + if (pos < m_size) + { + ++pos; + } + else + { + pos = m_size; + } + + for (const CharT *ptr = m_data + pos; ptr != m_data;) + { + if (Traits::find(s, n, *--ptr) == 0) + { + return static_cast<size_type>(ptr - m_data); + } + } + return npos; + } + + constexpr size_type find_last_not_of(const CharT *s, size_type pos = npos) const noexcept + { + return this->find_last_not_of(s, pos, traits_type::length(s)); + } + + private: + static int _s_compare(size_type n1, size_type n2) noexcept + { + const difference_type diff = n1 - n2; + + if (diff > (std::numeric_limits<int>::max)()) + { + return (std::numeric_limits<int>::max)(); + } + + if (diff < (std::numeric_limits<int>::min)()) + { + return (std::numeric_limits<int>::min)(); + } + + return static_cast<int>(diff); + } + + static const CharT *_s_search_substr( + const CharT *first1, + const CharT *last1, + const CharT *first2, + const CharT *last2) + { + const ptrdiff_t length2 = last2 - first2; + if (length2 == 0) + { + return first1; + } + + ptrdiff_t length1 = last1 - first1; + if (length1 < length2) + { + return last1; + } + + while (true) + { + length1 = last1 - first1; + if (length1 < length2) + { + return last1; + } + + first1 = Traits::find(first1, length1 - length2 + 1, *first2); + if (first1 == 0) + { + return last1; + } + + if (Traits::compare(first1, first2, length2) == 0) + { + return first1; + } + + ++first1; + } + } + + static const CharT *_s_find_end( + const CharT *first1, + const CharT *last1, + const CharT *first2, + const CharT *last2) + { + const CharT *r = last1; + if (first2 == last2) + { + return r; + } + + while (true) + { + while (true) + { + if (first1 == last1) + { + return r; + } + if (Traits::eq(*first1, *first2)) + { + break; + } + ++first1; + } + + const CharT *m1 = first1; + const CharT *m2 = first2; + while (true) + { + if (++m2 == last2) + { + r = first1; + ++first1; + break; + } + if (++m1 == last1) + { + return r; + } + if (!Traits::eq(*m1, *m2)) + { + ++first1; + break; + } + } + } + } + + static const CharT *_s_find_first_of_ce( + const CharT *first1, + const CharT *last1, + const CharT *first2, + const CharT *last2) + { + for (; first1 != last1; ++first1) + { + for (const CharT *ptr = first2; ptr != last2; ++ptr) + { + if (Traits::eq(*first1, *ptr)) + { + return first1; + } + } + } + return last1; + } + + size_type m_size; + const CharT *m_data; + }; + + // operator == + template <class CharT, class Traits> + bool operator==( + const basic_string_view<CharT, Traits> &lhs, + const basic_string_view<CharT, Traits> &rhs) noexcept + { + return (lhs.size() != rhs.size()) ? false : lhs.compare(rhs) == 0; + } + + template <class CharT, class Traits> + bool operator==( + const basic_string_view<CharT, Traits> &lhs, + typename std::common_type<basic_string_view<CharT, Traits>>::type &rhs) noexcept + { + return (lhs.size() != rhs.size()) ? false : lhs.compare(rhs) == 0; + } + + template <class CharT, class Traits> + bool operator==( + typename std::common_type<basic_string_view<CharT, Traits>>::type &lhs, + const basic_string_view<CharT, Traits> &rhs) noexcept + { + return (lhs.size() != rhs.size()) ? false : lhs.compare(rhs) == 0; + } + + // operator != + template <class CharT, class Traits> + bool operator!=( + const basic_string_view<CharT, Traits> &lhs, + const basic_string_view<CharT, Traits> &rhs) noexcept + { + return (lhs.size() != rhs.size()) ? true : lhs.compare(rhs) != 0; + } + + template <class CharT, class Traits> + bool operator!=( + const basic_string_view<CharT, Traits> &lhs, + typename std::common_type<basic_string_view<CharT, Traits>>::type &rhs) noexcept + { + return (lhs.size() != rhs.size()) ? true : lhs.compare(rhs) != 0; + } + + template <class CharT, class Traits> + bool operator!=( + typename std::common_type<basic_string_view<CharT, Traits>>::type &lhs, + const basic_string_view<CharT, Traits> &rhs) noexcept + { + return (lhs.size() != rhs.size()) ? true : lhs.compare(rhs) != 0; + } + + // operator < + template <class CharT, class Traits> + bool operator<( + const basic_string_view<CharT, Traits> &lhs, + const basic_string_view<CharT, Traits> &rhs) noexcept + { + return lhs.compare(rhs) < 0; + } + + template <class CharT, class Traits> + constexpr bool operator<( + const basic_string_view<CharT, Traits> &lhs, + typename std::common_type<basic_string_view<CharT, Traits>>::type &rhs) noexcept + { + return lhs.compare(rhs) < 0; + } + + template <class CharT, class Traits> + constexpr bool operator<( + typename std::common_type<basic_string_view<CharT, Traits>>::type &lhs, + const basic_string_view<CharT, Traits> &rhs) noexcept + { + return lhs.compare(rhs) < 0; + } + + // operator > + template <class CharT, class Traits> + constexpr bool operator>( + const basic_string_view<CharT, Traits> &lhs, + const basic_string_view<CharT, Traits> &rhs) noexcept + { + return lhs.compare(rhs) > 0; + } + + template <class CharT, class Traits> + constexpr bool operator>( + const basic_string_view<CharT, Traits> &lhs, + typename std::common_type<basic_string_view<CharT, Traits>>::type &rhs) noexcept + { + return lhs.compare(rhs) > 0; + } + + template <class CharT, class Traits> + constexpr bool operator>( + typename std::common_type<basic_string_view<CharT, Traits>>::type &lhs, + const basic_string_view<CharT, Traits> &rhs) noexcept + { + return lhs.compare(rhs) > 0; + } + + // operator <= + template <class CharT, class Traits> + constexpr bool operator<=( + const basic_string_view<CharT, Traits> &lhs, + const basic_string_view<CharT, Traits> &rhs) noexcept + { + return lhs.compare(rhs) <= 0; + } + + template <class CharT, class Traits> + constexpr bool operator<=( + const basic_string_view<CharT, Traits> &lhs, + typename std::common_type<basic_string_view<CharT, Traits>>::type &rhs) noexcept + { + return lhs.compare(rhs) <= 0; + } + + template <class CharT, class Traits> + constexpr bool operator<=( + typename std::common_type<basic_string_view<CharT, Traits>>::type &lhs, + const basic_string_view<CharT, Traits> &rhs) noexcept + { + return lhs.compare(rhs) <= 0; + } + + // operator >= + template <class CharT, class Traits> + constexpr bool operator>=( + const basic_string_view<CharT, Traits> &lhs, + const basic_string_view<CharT, Traits> &rhs) noexcept + { + return lhs.compare(rhs) >= 0; + } + + template <class CharT, class Traits> + constexpr bool operator>=( + const basic_string_view<CharT, Traits> &lhs, + typename std::common_type<basic_string_view<CharT, Traits>>::type &rhs) noexcept + { + return lhs.compare(rhs) >= 0; + } + + template <class CharT, class Traits> + constexpr bool operator>=( + typename std::common_type<basic_string_view<CharT, Traits>>::type &lhs, + const basic_string_view<CharT, Traits> &rhs) noexcept + { + return lhs.compare(rhs) >= 0; + } + + typedef basic_string_view<char> string_view; + typedef basic_string_view<char16_t> u16string_view; + typedef basic_string_view<char32_t> u32string_view; + typedef basic_string_view<wchar_t> wstring_view; + + inline namespace literals + { + inline namespace string_view_literals + { + inline basic_string_view<char> operator"" _sv(const char *s, size_t length) noexcept + { + return basic_string_view<char>(s, length); + } + + inline basic_string_view<wchar_t> operator"" _sv(const wchar_t *s, size_t length) noexcept + { + return basic_string_view<wchar_t>(s, length); + } + + inline basic_string_view<char16_t> operator"" _sv(const char16_t *s, size_t length) noexcept + { + return basic_string_view<char16_t>(s, length); + } + + inline basic_string_view<char32_t> operator"" _sv(const char32_t *s, size_t length) noexcept + { + return basic_string_view<char32_t>(s, length); + } + } // namespace string_view_literals + + } // namespace literals + + using StringView = string_view; + } // namespace Crt +} // namespace Aws + +// hash +namespace std +{ + template <class CharT, class Traits> struct hash<Aws::Crt::basic_string_view<CharT, Traits>> + { + size_t operator()(const Aws::Crt::basic_string_view<CharT, Traits> &val) const noexcept; + }; + + template <class CharT, class Traits> + size_t hash<Aws::Crt::basic_string_view<CharT, Traits>>::operator()( + const Aws::Crt::basic_string_view<CharT, Traits> &val) const noexcept + { + auto str = std::basic_string<CharT, Traits>(val.data(), val.size()); + return std::hash<std::basic_string<CharT, Traits>>()(str); + } +} // namespace std diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Types.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Types.h new file mode 100644 index 0000000000..3972aa7aff --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/Types.h @@ -0,0 +1,165 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/common/common.h> +#include <aws/crt/Exports.h> +#include <aws/crt/Optional.h> +#include <aws/crt/StlAllocator.h> +#include <aws/crt/StringView.h> +#include <aws/io/socket.h> +#include <aws/mqtt/mqtt.h> +#include <functional> +#include <list> +#include <map> +#include <sstream> +#include <string> +#include <unordered_map> +#include <utility> +#include <vector> + +struct aws_byte_buf; +struct aws_byte_cursor; +struct aws_socket_options; + +namespace Aws +{ + namespace Crt + { + using ByteBuf = aws_byte_buf; + using ByteCursor = aws_byte_cursor; + + namespace Io + { + using IStream = std::basic_istream<char, std::char_traits<char>>; + } // namespace Io + + namespace Mqtt + { + using QOS = aws_mqtt_qos; + using ReturnCode = aws_mqtt_connect_return_code; + } // namespace Mqtt + + template <typename T> class StlAllocator; + using String = std::basic_string<char, std::char_traits<char>, StlAllocator<char>>; + using StringStream = std::basic_stringstream<char, std::char_traits<char>, StlAllocator<char>>; + template <typename K, typename V> using Map = std::map<K, V, std::less<K>, StlAllocator<std::pair<const K, V>>>; + template <typename K, typename V> + using UnorderedMap = + std::unordered_map<K, V, std::hash<K>, std::equal_to<K>, StlAllocator<std::pair<const K, V>>>; + template <typename K, typename V> + using MultiMap = std::multimap<K, V, std::less<K>, StlAllocator<std::pair<const K, V>>>; + template <typename T> using Vector = std::vector<T, StlAllocator<T>>; + template <typename T> using List = std::list<T, StlAllocator<T>>; + + AWS_CRT_CPP_API ByteBuf ByteBufFromCString(const char *str) noexcept; + AWS_CRT_CPP_API ByteBuf ByteBufFromEmptyArray(const uint8_t *array, size_t len) noexcept; + AWS_CRT_CPP_API ByteBuf ByteBufFromArray(const uint8_t *array, size_t capacity) noexcept; + AWS_CRT_CPP_API ByteBuf ByteBufNewCopy(Allocator *alloc, const uint8_t *array, size_t len); + AWS_CRT_CPP_API void ByteBufDelete(ByteBuf &); + + AWS_CRT_CPP_API ByteCursor ByteCursorFromCString(const char *str) noexcept; + AWS_CRT_CPP_API ByteCursor ByteCursorFromString(const Crt::String &str) noexcept; + AWS_CRT_CPP_API ByteCursor ByteCursorFromStringView(const Crt::StringView &str) noexcept; + AWS_CRT_CPP_API ByteCursor ByteCursorFromByteBuf(const ByteBuf &) noexcept; + AWS_CRT_CPP_API ByteCursor ByteCursorFromArray(const uint8_t *array, size_t len) noexcept; + + AWS_CRT_CPP_API Vector<uint8_t> Base64Decode(const String &decode); + AWS_CRT_CPP_API String Base64Encode(const Vector<uint8_t> &encode); + + template <typename RawType, typename TargetType> using TypeConvertor = std::function<TargetType(RawType)>; + + /** + * Template function to convert an aws_array_list of RawType to a C++ like Vector of TargetType. + * A conversion function should be provided to do the type conversion + */ + template <typename RawType, typename TargetType> + Vector<TargetType> ArrayListToVector(const aws_array_list *array, TypeConvertor<RawType, TargetType> conv) + { + Vector<TargetType> v; + size_t cnt = aws_array_list_length(array); + for (size_t i = 0; i < cnt; i++) + { + RawType t; + aws_array_list_get_at(array, &t, i); + v.emplace_back(conv(t)); + } + return v; + } + + /** + * Template function to convert an aws_array_list of RawType to a C++ like Vector of TargetType. + * This template assumes a direct constructor: TargetType(RawType) is available + */ + template <typename RawType, typename TargetType> + Vector<TargetType> ArrayListToVector(const aws_array_list *array) + { + Vector<TargetType> v; + size_t cnt = aws_array_list_length(array); + for (size_t i = 0; i < cnt; i++) + { + RawType t; + aws_array_list_get_at(array, &t, i); + v.emplace_back(TargetType(t)); + } + return v; + } + + /** + * Template function to convert an aws_array_list of Type to a C++ like Vector of Type. + */ + template <typename Type> Vector<Type> ArrayListToVector(const aws_array_list *array) + { + Vector<Type> v; + size_t cnt = aws_array_list_length(array); + for (size_t i = 0; i < cnt; i++) + { + Type t; + aws_array_list_get_at(array, &t, i); + v.emplace_back(t); + } + return v; + } + + AWS_CRT_CPP_API inline StringView ByteCursorToStringView(const ByteCursor &bc) + { + return StringView(reinterpret_cast<char *>(bc.ptr), bc.len); + } + + AWS_CRT_CPP_API inline ByteCursor StringViewToByteCursor(const StringView &sv) + { + ByteCursor bc; + bc.ptr = (uint8_t *)(sv.data()); + bc.len = sv.size(); + return bc; + } + + template <typename T> void Delete(T *t, Allocator *allocator) + { + t->~T(); + aws_mem_release(allocator, t); + } + + template <typename T, typename... Args> T *New(Allocator *allocator, Args &&...args) + { + T *t = reinterpret_cast<T *>(aws_mem_acquire(allocator, sizeof(T))); + if (!t) + return nullptr; + return new (t) T(std::forward<Args>(args)...); + } + + template <typename T, typename... Args> std::shared_ptr<T> MakeShared(Allocator *allocator, Args &&...args) + { + T *t = reinterpret_cast<T *>(aws_mem_acquire(allocator, sizeof(T))); + if (!t) + return nullptr; + new (t) T(std::forward<Args>(args)...); + + return std::shared_ptr<T>(t, [allocator](T *obj) { Delete(obj, allocator); }); + } + + template <typename T> using ScopedResource = std::unique_ptr<T, std::function<void(T *)>>; + + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/UUID.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/UUID.h new file mode 100644 index 0000000000..5ca53221cc --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/UUID.h @@ -0,0 +1,42 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/StlAllocator.h> +#include <aws/crt/Types.h> + +#include <aws/common/uuid.h> + +namespace Aws +{ + namespace Crt + { + /** + * Utility class for creating UUIDs and serializing them to a string + */ + class AWS_CRT_CPP_API UUID final + { + public: + UUID() noexcept; + UUID(const String &str) noexcept; + + UUID &operator=(const String &str) noexcept; + + bool operator==(const UUID &other) noexcept; + bool operator!=(const UUID &other) noexcept; + operator String() const; + operator ByteBuf() const noexcept; + + inline operator bool() const noexcept { return m_good; } + + int GetLastError() const noexcept; + + String ToString() const; + + private: + aws_uuid m_uuid; + bool m_good; + }; + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/auth/Credentials.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/auth/Credentials.h new file mode 100644 index 0000000000..48181d1ce0 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/auth/Credentials.h @@ -0,0 +1,585 @@ +#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/http/HttpConnection.h> +#include <aws/crt/io/TlsOptions.h> + +#include <chrono> +#include <functional> + +struct aws_credentials; +struct aws_credentials_provider; + +namespace Aws +{ + namespace Crt + { + namespace Io + { + class ClientBootstrap; + } + + namespace Http + { + class HttpClientConnectionProxyOptions; + } + + namespace Auth + { + /** + * A class to hold the basic components necessary for various AWS authentication protocols. + */ + class AWS_CRT_CPP_API Credentials + { + public: + Credentials(const aws_credentials *credentials) noexcept; + Credentials( + ByteCursor access_key_id, + ByteCursor secret_access_key, + ByteCursor session_token, + uint64_t expiration_timepoint_in_seconds, + Allocator *allocator = ApiAllocator()) noexcept; + + /** + * Create new anonymous Credentials. + * Use anonymous Credentials when you want to skip signing. + * @param allocator + */ + Credentials(Allocator *allocator = ApiAllocator()) noexcept; + + ~Credentials(); + + Credentials(const Credentials &) = delete; + Credentials(Credentials &&) = delete; + Credentials &operator=(const Credentials &) = delete; + Credentials &operator=(Credentials &&) = delete; + + /** + * Gets the value of the access key component of aws credentials + */ + ByteCursor GetAccessKeyId() const noexcept; + + /** + * Gets the value of the secret access key component of aws credentials + */ + ByteCursor GetSecretAccessKey() const noexcept; + + /** + * Gets the value of the session token of aws credentials + */ + ByteCursor GetSessionToken() const noexcept; + + /** + * Gets the expiration timestamp for the credentials, or UINT64_MAX if no expiration + */ + uint64_t GetExpirationTimepointInSeconds() const noexcept; + + /** + * Validity check - returns true if the instance is valid, false otherwise + */ + explicit operator bool() const noexcept; + + /** + * Returns the underlying credentials implementation. + */ + const aws_credentials *GetUnderlyingHandle() const noexcept { return m_credentials; } + + private: + const aws_credentials *m_credentials; + }; + + /** + * Callback invoked by credentials providers when resolution succeeds (credentials will be non-null) + * or fails (credentials will be null) + */ + using OnCredentialsResolved = std::function<void(std::shared_ptr<Credentials>, int errorCode)>; + + /** + * Invoked when the native delegate credentials provider needs to fetch a credential. + */ + using GetCredentialsHandler = std::function<std::shared_ptr<Credentials>()>; + + /** + * Base interface for all credentials providers. Credentials providers are objects that + * retrieve AWS credentials from some source. + */ + class AWS_CRT_CPP_API ICredentialsProvider : public std::enable_shared_from_this<ICredentialsProvider> + { + public: + virtual ~ICredentialsProvider() = default; + + /** + * Asynchronous method to query for AWS credentials based on the internal provider implementation. + */ + virtual bool GetCredentials(const OnCredentialsResolved &onCredentialsResolved) const = 0; + + /** + * Returns the underlying credentials provider implementation. Support for credentials providers + * not based on a C implementation is theoretically possible, but requires some re-implementation to + * support provider chains and caching (whose implementations rely on links to C implementation + * providers) + */ + virtual aws_credentials_provider *GetUnderlyingHandle() const noexcept = 0; + + /** + * Validity check method + */ + virtual bool IsValid() const noexcept = 0; + }; + + /** + * Configuration options for the static credentials provider + */ + struct AWS_CRT_CPP_API CredentialsProviderStaticConfig + { + CredentialsProviderStaticConfig() + { + AWS_ZERO_STRUCT(AccessKeyId); + AWS_ZERO_STRUCT(SecretAccessKey); + AWS_ZERO_STRUCT(SessionToken); + } + + /** + * The value of the access key component for the provider's static aws credentials + */ + ByteCursor AccessKeyId; + + /** + * The value of the secret access key component for the provider's static aws credentials + */ + ByteCursor SecretAccessKey; + + /** + * The value of the session token for the provider's static aws credentials + */ + ByteCursor SessionToken; + }; + + /** + * Configuration options for the profile credentials provider + */ + struct AWS_CRT_CPP_API CredentialsProviderProfileConfig + { + CredentialsProviderProfileConfig() : Bootstrap(nullptr), TlsContext(nullptr) + { + AWS_ZERO_STRUCT(ProfileNameOverride); + AWS_ZERO_STRUCT(ConfigFileNameOverride); + AWS_ZERO_STRUCT(CredentialsFileNameOverride); + } + + /** + * Override profile name to use (instead of default) when the provider sources credentials + */ + ByteCursor ProfileNameOverride; + + /** + * Override file path (instead of '~/.aws/config' for the aws config file to use during + * credential sourcing + */ + ByteCursor ConfigFileNameOverride; + + /** + * Override file path (instead of '~/.aws/credentials' for the aws credentials file to use during + * credential sourcing + */ + ByteCursor CredentialsFileNameOverride; + + /** + * Connection bootstrap to use for any network connections made while sourcing credentials. + * (for example, a profile that uses assume-role will need to query STS). + */ + Io::ClientBootstrap *Bootstrap; + + /** + * Client TLS context to use for any secure network connections made while sourcing credentials + * (for example, a profile that uses assume-role will need to query STS). + * + * If a TLS context is needed, and you did not pass one in, it will be created automatically. + * However, you are encouraged to pass in a shared one since these are expensive objects. + * If using BYO_CRYPTO, you must provide the TLS context since it cannot be created automatically. + */ + Io::TlsContext *TlsContext; + }; + + /** + * Configuration options for the Ec2 instance metadata service credentials provider + */ + struct AWS_CRT_CPP_API CredentialsProviderImdsConfig + { + CredentialsProviderImdsConfig() : Bootstrap(nullptr) {} + + /** + * Connection bootstrap to use to create the http connection required to + * query credentials from the Ec2 instance metadata service + * + * Note: If null, then the default ClientBootstrap is used + * (see Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap) + */ + Io::ClientBootstrap *Bootstrap; + }; + + /** + * Configuration options for a chain-of-responsibility-based credentials provider. + * This provider works by traversing the chain and returning the first positive + * result. + */ + struct AWS_CRT_CPP_API CredentialsProviderChainConfig + { + CredentialsProviderChainConfig() : Providers() {} + + /** + * The sequence of providers that make up the chain. + */ + Vector<std::shared_ptr<ICredentialsProvider>> Providers; + }; + + /** + * Configuration options for a provider that caches the results of another provider + */ + struct AWS_CRT_CPP_API CredentialsProviderCachedConfig + { + CredentialsProviderCachedConfig() : Provider(), CachedCredentialTTL() {} + + /** + * The provider to cache credentials from + */ + std::shared_ptr<ICredentialsProvider> Provider; + + /** + * How long a cached credential set will be used for + */ + std::chrono::milliseconds CachedCredentialTTL; + }; + + /** + * Configuration options for a provider that implements a cached provider chain + * based on the AWS SDK defaults: + * + * Cache-Of(Environment -> Profile -> IMDS) + */ + struct AWS_CRT_CPP_API CredentialsProviderChainDefaultConfig + { + CredentialsProviderChainDefaultConfig() : Bootstrap(nullptr), TlsContext(nullptr) {} + + /** + * Connection bootstrap to use for any network connections made while sourcing credentials. + * + * Note: If null, then the default ClientBootstrap is used + * (see Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap) + */ + Io::ClientBootstrap *Bootstrap; + + /** + * Client TLS context to use for any secure network connections made while sourcing credentials. + * + * If not provided the default chain will construct a new one, but these + * are expensive objects so you are encouraged to pass in a shared one. + * Must be provided if using BYO_CRYPTO. + */ + Io::TlsContext *TlsContext; + }; + + /** + * Configuration options for the X509 credentials provider + */ + struct AWS_CRT_CPP_API CredentialsProviderX509Config + { + CredentialsProviderX509Config() + : Bootstrap(nullptr), TlsOptions(), ThingName(), RoleAlias(), Endpoint(), ProxyOptions() + { + } + + /** + * Connection bootstrap to use to create the http connection required to + * query credentials from the x509 provider + * + * Note: If null, then the default ClientBootstrap is used + * (see Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap) + */ + Io::ClientBootstrap *Bootstrap; + + /* TLS connection options that have been initialized with your x509 certificate and private key */ + Io::TlsConnectionOptions TlsOptions; + + /* IoT thing name you registered with AWS IOT for your device, it will be used in http request header */ + String ThingName; + + /* Iot role alias you created with AWS IoT for your IAM role, it will be used in http request path */ + String RoleAlias; + + /** + * AWS account specific endpoint that can be acquired using AWS CLI following instructions from the demo + * example: c2sakl5huz0afv.credentials.iot.us-east-1.amazonaws.com + * + * This a different endpoint than the IoT data mqtt broker endpoint. + */ + String Endpoint; + + /** + * (Optional) Http proxy configuration for the http request that fetches credentials + */ + Optional<Http::HttpClientConnectionProxyOptions> ProxyOptions; + }; + + /** + * Configuration options for the delegate credentials provider + */ + struct AWS_CRT_CPP_API CredentialsProviderDelegateConfig + { + /* handler to provider credentials */ + GetCredentialsHandler Handler; + }; + + /** + * A pair defining an identity provider and a valid login token sourced from it. + */ + struct AWS_CRT_CPP_API CognitoLoginPair + { + + /** + * Name of an identity provider + */ + String IdentityProviderName; + + /** + * Valid login token source from the identity provider + */ + String IdentityProviderToken; + }; + + /** + * Configuration options for the Cognito credentials provider + */ + struct AWS_CRT_CPP_API CredentialsProviderCognitoConfig + { + CredentialsProviderCognitoConfig(); + + /** + * Cognito service regional endpoint to source credentials from. + */ + String Endpoint; + + /** + * Cognito identity to fetch credentials relative to. + */ + String Identity; + + /** + * Optional set of identity provider token pairs to allow for authenticated identity access. + */ + Optional<Vector<CognitoLoginPair>> Logins; + + /** + * Optional ARN of the role to be assumed when multiple roles were received in the token from the + * identity provider. + */ + Optional<String> CustomRoleArn; + + /** + * Connection bootstrap to use to create the http connection required to + * query credentials from the cognito provider + * + * Note: If null, then the default ClientBootstrap is used + * (see Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap) + */ + Io::ClientBootstrap *Bootstrap; + + /** + * TLS configuration for secure socket connections. + */ + Io::TlsContext TlsCtx; + + /** + * (Optional) Http proxy configuration for the http request that fetches credentials + */ + Optional<Http::HttpClientConnectionProxyOptions> ProxyOptions; + }; + + /** + * Configuration options for the STS credentials provider + */ + struct AWS_CRT_CPP_API CredentialsProviderSTSConfig + { + CredentialsProviderSTSConfig(); + + /** + * Credentials provider to be used to sign the requests made to STS to fetch credentials. + */ + std::shared_ptr<ICredentialsProvider> Provider; + + /** + * Arn of the role to assume by fetching credentials for + */ + String RoleArn; + + /** + * Assumed role session identifier to be associated with the sourced credentials + */ + String SessionName; + + /** + * How long sourced credentials should remain valid for, in seconds. 900 is the minimum allowed value. + */ + uint16_t DurationSeconds; + + /** + * Connection bootstrap to use to create the http connection required to + * query credentials from the STS provider + * + * Note: If null, then the default ClientBootstrap is used + * (see Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap) + */ + Io::ClientBootstrap *Bootstrap; + + /** + * TLS configuration for secure socket connections. + */ + Io::TlsContext TlsCtx; + + /** + * (Optional) Http proxy configuration for the http request that fetches credentials + */ + Optional<Http::HttpClientConnectionProxyOptions> ProxyOptions; + }; + + /** + * Simple credentials provider implementation that wraps one of the internal C-based implementations. + * + * Contains a set of static factory methods for building each supported provider, as well as one for the + * default provider chain. + */ + class AWS_CRT_CPP_API CredentialsProvider : public ICredentialsProvider + { + public: + CredentialsProvider(aws_credentials_provider *provider, Allocator *allocator = ApiAllocator()) noexcept; + + virtual ~CredentialsProvider(); + + CredentialsProvider(const CredentialsProvider &) = delete; + CredentialsProvider(CredentialsProvider &&) = delete; + CredentialsProvider &operator=(const CredentialsProvider &) = delete; + CredentialsProvider &operator=(CredentialsProvider &&) = delete; + + /** + * Asynchronous method to query for AWS credentials based on the internal provider implementation. + */ + virtual bool GetCredentials(const OnCredentialsResolved &onCredentialsResolved) const override; + + /** + * Returns the underlying credentials provider implementation. + */ + virtual aws_credentials_provider *GetUnderlyingHandle() const noexcept override { return m_provider; } + + /** + * Validity check method + */ + virtual bool IsValid() const noexcept override { return m_provider != nullptr; } + + /* + * Factory methods for all of the basic credentials provider types + */ + + /** + * Creates a provider that returns a fixed set of credentials + */ + static std::shared_ptr<ICredentialsProvider> CreateCredentialsProviderStatic( + const CredentialsProviderStaticConfig &config, + Allocator *allocator = ApiAllocator()); + + /** + * Creates an anonymous provider that have anonymous credentials + * Use anonymous credentials when you want to skip signing + */ + static std::shared_ptr<ICredentialsProvider> CreateCredentialsProviderAnonymous( + Allocator *allocator = ApiAllocator()); + + /** + * Creates a provider that returns credentials sourced from environment variables + */ + static std::shared_ptr<ICredentialsProvider> CreateCredentialsProviderEnvironment( + Allocator *allocator = ApiAllocator()); + + /** + * Creates a provider that returns credentials sourced from config files + */ + static std::shared_ptr<ICredentialsProvider> CreateCredentialsProviderProfile( + const CredentialsProviderProfileConfig &config, + Allocator *allocator = ApiAllocator()); + + /** + * Creates a provider that returns credentials sourced from Ec2 instance metadata service + */ + static std::shared_ptr<ICredentialsProvider> CreateCredentialsProviderImds( + const CredentialsProviderImdsConfig &config, + Allocator *allocator = ApiAllocator()); + + /** + * Creates a provider that sources credentials by querying a series of providers and + * returning the first valid credential set encountered + */ + static std::shared_ptr<ICredentialsProvider> CreateCredentialsProviderChain( + const CredentialsProviderChainConfig &config, + Allocator *allocator = ApiAllocator()); + + /* + * Creates a provider that puts a simple time-based cache in front of its queries + * to a subordinate provider. + */ + static std::shared_ptr<ICredentialsProvider> CreateCredentialsProviderCached( + const CredentialsProviderCachedConfig &config, + Allocator *allocator = ApiAllocator()); + + /** + * Creates the SDK-standard default credentials provider which is a cache-fronted chain of: + * + * Environment -> Profile -> IMDS/ECS + * + */ + static std::shared_ptr<ICredentialsProvider> CreateCredentialsProviderChainDefault( + const CredentialsProviderChainDefaultConfig &config, + Allocator *allocator = ApiAllocator()); + + /** + * Creates a provider that sources credentials from the IoT X509 provider service + * + */ + static std::shared_ptr<ICredentialsProvider> CreateCredentialsProviderX509( + const CredentialsProviderX509Config &config, + Allocator *allocator = ApiAllocator()); + + /** + * Creates a provider that sources credentials from the provided function. + * + */ + static std::shared_ptr<ICredentialsProvider> CreateCredentialsProviderDelegate( + const CredentialsProviderDelegateConfig &config, + Allocator *allocator = ApiAllocator()); + + /** + * Creates a provider that sources credentials from the Cognito Identity service + */ + static std::shared_ptr<ICredentialsProvider> CreateCredentialsProviderCognito( + const CredentialsProviderCognitoConfig &config, + Allocator *allocator = ApiAllocator()); + + /** + * Creates a provider that sources credentials from STS + */ + static std::shared_ptr<ICredentialsProvider> CreateCredentialsProviderSTS( + const CredentialsProviderSTSConfig &config, + Allocator *allocator = ApiAllocator()); + + private: + static void s_onCredentialsResolved(aws_credentials *credentials, int error_code, void *user_data); + + Allocator *m_allocator; + aws_credentials_provider *m_provider; + }; + } // namespace Auth + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/auth/Signing.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/auth/Signing.h new file mode 100644 index 0000000000..5723f00e2b --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/auth/Signing.h @@ -0,0 +1,99 @@ +#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/auth/signing_config.h> + +#include <functional> +#include <memory> + +namespace Aws +{ + namespace Crt + { + namespace Http + { + class HttpRequest; + } + + namespace Auth + { + /** + * RTTI indicator for signing configuration. We currently only support a single type (AWS), but + * we could expand to others in the future if needed. + */ + enum class SigningConfigType + { + Aws = AWS_SIGNING_CONFIG_AWS + }; + + /** + * HTTP signing callback. The second parameter is an aws error code, The signing was successful + * iff the error code is AWS_ERROR_SUCCESS. + */ + using OnHttpRequestSigningComplete = + std::function<void(const std::shared_ptr<Aws::Crt::Http::HttpRequest> &, int)>; + + /** + * Base class for all different signing configurations. Type functions as a + * primitive RTTI for downcasting. + */ + class AWS_CRT_CPP_API ISigningConfig + { + public: + ISigningConfig() = default; + ISigningConfig(const ISigningConfig &) = delete; + ISigningConfig(ISigningConfig &&) = delete; + ISigningConfig &operator=(const ISigningConfig &) = delete; + ISigningConfig &operator=(ISigningConfig &&) = delete; + + virtual ~ISigningConfig() = default; + + /** + * RTTI query for the SigningConfig hierarchy + * @return the type of signing configuration + */ + virtual SigningConfigType GetType(void) const = 0; + }; + + /** + * Abstract base for all http request signers. Asynchronous interface. Intended to + * be a tight wrapper around aws-c-* signer implementations. + */ + class AWS_CRT_CPP_API IHttpRequestSigner + { + public: + IHttpRequestSigner() = default; + IHttpRequestSigner(const IHttpRequestSigner &) = delete; + IHttpRequestSigner(IHttpRequestSigner &&) = delete; + IHttpRequestSigner &operator=(const IHttpRequestSigner &) = delete; + IHttpRequestSigner &operator=(IHttpRequestSigner &&) = delete; + + virtual ~IHttpRequestSigner() = default; + + /** + * Signs an http request based on the signing implementation and supplied configuration + * @param request http request to sign + * @param config base signing configuration. Actual type should match the configuration expected + * by the signer implementation + * @param completionCallback completion function to invoke when signing has completed or failed + * @return true if the signing process was kicked off, false if there was a synchronous failure. + */ + virtual bool SignRequest( + const std::shared_ptr<Aws::Crt::Http::HttpRequest> &request, + const ISigningConfig &config, + const OnHttpRequestSigningComplete &completionCallback) = 0; + + /** + * @return Whether or not the signer is in a valid state + */ + virtual bool IsValid() const = 0; + }; + + } // namespace Auth + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/auth/Sigv4Signing.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/auth/Sigv4Signing.h new file mode 100644 index 0000000000..e902b1363f --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/auth/Sigv4Signing.h @@ -0,0 +1,352 @@ +#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/DateTime.h> +#include <aws/crt/Types.h> +#include <aws/crt/auth/Signing.h> + +struct aws_signing_config_aws; + +namespace Aws +{ + namespace Crt + { + namespace Auth + { + class Credentials; + class ICredentialsProvider; + + /** + * Enumeration indicating what version of the AWS signing process we should use. + */ + enum class SigningAlgorithm + { + /** + * Standard AWS Sigv4 signing using a symmetric secret, per + * https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html + */ + SigV4 = AWS_SIGNING_ALGORITHM_V4, + + /** + * A variant of AWS Sigv4 signing that uses ecdsa signatures based on an ECC key, rather than relying on + * a shared secret. + */ + SigV4A = AWS_SIGNING_ALGORITHM_V4_ASYMMETRIC, + }; + + /** + * What kind of AWS signature should be computed? + */ + enum class SignatureType + { + /** + * A signature for a full http request should be computed, with header updates applied to the signing + * result. + */ + HttpRequestViaHeaders = AWS_ST_HTTP_REQUEST_HEADERS, + + /** + * A signature for a full http request should be computed, with query param updates applied to the + * signing result. + */ + HttpRequestViaQueryParams = AWS_ST_HTTP_REQUEST_QUERY_PARAMS, + + /** + * Compute a signature for a payload chunk. + */ + HttpRequestChunk = AWS_ST_HTTP_REQUEST_CHUNK, + + /** + * Compute a signature for an event stream event. + * + * This option is not yet supported. + */ + HttpRequestEvent = AWS_ST_HTTP_REQUEST_EVENT, + }; + + /** + * A collection of signed body constants. Some are specific to certain + * signature types, while others are just there to save time (empty sha, for example). + */ + namespace SignedBodyValue + { + /** + * The SHA-256 of an empty string: + * 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' + * For use with `Aws::Crt::Auth::AwsSigningConfig.SetSignedBodyValue()`. + */ + AWS_CRT_CPP_API const char *EmptySha256Str(); + + /** + * 'UNSIGNED-PAYLOAD' + * For use with `Aws::Crt::Auth::AwsSigningConfig.SetSignedBodyValue()`. + */ + AWS_CRT_CPP_API const char *UnsignedPayloadStr(); + + /** + * 'STREAMING-AWS4-HMAC-SHA256-PAYLOAD' + * For use with `Aws::Crt::Auth::AwsSigningConfig.SetSignedBodyValue()`. + */ + AWS_CRT_CPP_API const char *StreamingAws4HmacSha256PayloadStr(); + /** + * 'STREAMING-AWS4-HMAC-SHA256-EVENTS' + * For use with `Aws::Crt::Auth::AwsSigningConfig.SetSignedBodyValue()`. + */ + AWS_CRT_CPP_API const char *StreamingAws4HmacSha256EventsStr(); + + /** @deprecated to avoid issues with /DELAYLOAD on Windows. */ + AWS_CRT_CPP_API extern const char *UnsignedPayload; + /** @deprecated to avoid issues with /DELAYLOAD on Windows. */ + AWS_CRT_CPP_API extern const char *EmptySha256; + /** @deprecated to avoid issues with /DELAYLOAD on Windows. */ + AWS_CRT_CPP_API extern const char *StreamingAws4HmacSha256Payload; + /** @deprecated to avoid issues with /DELAYLOAD on Windows. */ + AWS_CRT_CPP_API extern const char *StreamingAws4HmacSha256Events; + } // namespace SignedBodyValue + + /** + * Controls if signing adds a header containing the canonical request's body value + */ + enum class SignedBodyHeaderType + { + /** + * Do not add a header + */ + None = AWS_SBHT_NONE, + + /** + * Add the "x-amz-content-sha256" header with the canonical request's body value + */ + XAmzContentSha256 = AWS_SBHT_X_AMZ_CONTENT_SHA256, + }; + + using ShouldSignHeaderCb = bool (*)(const Crt::ByteCursor *, void *); + + /** + * Wrapper around the configuration structure specific to the AWS + * Sigv4 signing process + */ + class AWS_CRT_CPP_API AwsSigningConfig : public ISigningConfig + { + public: + AwsSigningConfig(Allocator *allocator = ApiAllocator()); + virtual ~AwsSigningConfig(); + + virtual SigningConfigType GetType() const noexcept override { return SigningConfigType::Aws; } + + /** + * @return the signing process we want to invoke + */ + SigningAlgorithm GetSigningAlgorithm() const noexcept; + + /** + * Sets the signing process we want to invoke + */ + void SetSigningAlgorithm(SigningAlgorithm algorithm) noexcept; + + /** + * @return the type of signature we want to calculate + */ + SignatureType GetSignatureType() const noexcept; + + /** + * Sets the type of signature we want to calculate + */ + void SetSignatureType(SignatureType signatureType) noexcept; + + /** + * @return the AWS region to sign against + */ + const Crt::String &GetRegion() const noexcept; + + /** + * Sets the AWS region to sign against + */ + void SetRegion(const Crt::String ®ion) noexcept; + + /** + * @return the (signing) name of the AWS service to sign a request for + */ + const Crt::String &GetService() const noexcept; + + /** + * Sets the (signing) name of the AWS service to sign a request for + */ + void SetService(const Crt::String &service) noexcept; + + /** + * @return the timestamp to use during the signing process. + */ + DateTime GetSigningTimepoint() const noexcept; + + /** + * Sets the timestamp to use during the signing process. + */ + void SetSigningTimepoint(const DateTime &date) noexcept; + + /* + * We assume the uri will be encoded once in preparation for transmission. Certain services + * do not decode before checking signature, requiring us to actually double-encode the uri in the + * canonical request in order to pass a signature check. + */ + + /** + * @return whether or not the signing process should perform a uri encode step before creating the + * canonical request. + */ + bool GetUseDoubleUriEncode() const noexcept; + + /** + * Sets whether or not the signing process should perform a uri encode step before creating the + * canonical request. + */ + void SetUseDoubleUriEncode(bool useDoubleUriEncode) noexcept; + + /** + * @return whether or not the uri paths should be normalized when building the canonical request + */ + bool GetShouldNormalizeUriPath() const noexcept; + + /** + * Sets whether or not the uri paths should be normalized when building the canonical request + */ + void SetShouldNormalizeUriPath(bool shouldNormalizeUriPath) noexcept; + + /** + * @return whether or not to omit the session token during signing. Only set to true when performing + * a websocket handshake with IoT Core. + */ + bool GetOmitSessionToken() const noexcept; + + /** + * Sets whether or not to omit the session token during signing. Only set to true when performing + * a websocket handshake with IoT Core. + */ + void SetOmitSessionToken(bool omitSessionToken) noexcept; + + /** + * @return the ShouldSignHeadersCb from the underlying config. + */ + ShouldSignHeaderCb GetShouldSignHeaderCallback() const noexcept; + + /** + * Sets a callback invoked during the signing process for white-listing headers that can be signed. + * If you do not set this, all headers will be signed. + */ + void SetShouldSignHeaderCallback(ShouldSignHeaderCb shouldSignHeaderCb) noexcept; + + /** + * @return the should_sign_header_ud from the underlying config. + */ + void *GetShouldSignHeaderUserData() const noexcept; + + /** + * Sets the userData you could get from the ShouldSignHeaderCb callback function. + */ + void SetShouldSignHeaderUserData(void *userData) noexcept; + + /** + * @return the string used as the canonical request's body value. + * If string is empty, a value is be calculated from the payload during signing. + */ + const Crt::String &GetSignedBodyValue() const noexcept; + + /** + * Sets the string to use as the canonical request's body value. + * If an empty string is set (the default), a value will be calculated from the payload during signing. + * Typically, this is the SHA-256 of the (request/chunk/event) payload, written as lowercase hex. + * If this has been precalculated, it can be set here. + * Special values used by certain services can also be set (see Aws::Crt::Auth::SignedBodyValue). + */ + void SetSignedBodyValue(const Crt::String &signedBodyValue) noexcept; + + /** + * @return the name of the header to add that stores the signed body value + */ + SignedBodyHeaderType GetSignedBodyHeader() const noexcept; + + /** + * Sets the name of the header to add that stores the signed body value + */ + void SetSignedBodyHeader(SignedBodyHeaderType signedBodyHeader) noexcept; + + /** + * @return (Query param signing only) Gets the amount of time, in seconds, the (pre)signed URI will be + * good for + */ + uint64_t GetExpirationInSeconds() const noexcept; + + /** + * (Query param signing only) Sets the amount of time, in seconds, the (pre)signed URI will be good for + */ + void SetExpirationInSeconds(uint64_t expirationInSeconds) noexcept; + + /* + * For Sigv4 signing, either the credentials provider or the credentials must be set. + * Credentials, if set, takes precedence over the provider. + */ + + /** + * @return the credentials provider to use for signing. + */ + const std::shared_ptr<ICredentialsProvider> &GetCredentialsProvider() const noexcept; + + /** + * Set the credentials provider to use for signing. + */ + void SetCredentialsProvider(const std::shared_ptr<ICredentialsProvider> &credsProvider) noexcept; + + /** + * @return the credentials to use for signing. + */ + const std::shared_ptr<Credentials> &GetCredentials() const noexcept; + + /** + * Set the credentials to use for signing. + */ + void SetCredentials(const std::shared_ptr<Credentials> &credentials) noexcept; + + /// @private + const struct aws_signing_config_aws *GetUnderlyingHandle() const noexcept; + + private: + Allocator *m_allocator; + std::shared_ptr<ICredentialsProvider> m_credentialsProvider; + std::shared_ptr<Credentials> m_credentials; + struct aws_signing_config_aws m_config; + Crt::String m_signingRegion; + Crt::String m_serviceName; + Crt::String m_signedBodyValue; + }; + + /** + * Http request signer that performs Aws Sigv4 signing. Expects the signing configuration to be and + * instance of AwsSigningConfig + */ + class AWS_CRT_CPP_API Sigv4HttpRequestSigner : public IHttpRequestSigner + { + public: + Sigv4HttpRequestSigner(Allocator *allocator = ApiAllocator()); + virtual ~Sigv4HttpRequestSigner() = default; + + bool IsValid() const override { return true; } + + /** + * Signs an http request with AWS-auth sigv4. OnCompletionCallback will be invoked upon completion. + */ + virtual bool SignRequest( + const std::shared_ptr<Aws::Crt::Http::HttpRequest> &request, + const ISigningConfig &config, + const OnHttpRequestSigningComplete &completionCallback) override; + + private: + Allocator *m_allocator; + }; + } // namespace Auth + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/crypto/HMAC.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/crypto/HMAC.h new file mode 100644 index 0000000000..e4452f6bc5 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/crypto/HMAC.h @@ -0,0 +1,150 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/cal/hmac.h> +#include <aws/crt/Exports.h> +#include <aws/crt/Types.h> + +struct aws_hmac; +namespace Aws +{ + namespace Crt + { + namespace Crypto + { + static const size_t SHA256_HMAC_DIGEST_SIZE = 32; + + /** + * Computes a SHA256 HMAC with secret over input, and writes the digest to output. If truncateTo is + * non-zero, the digest will be truncated to the value of truncateTo. Returns true on success. If this + * function fails, Aws::Crt::LastError() will contain the error that occurred. Unless you're using + * 'truncateTo', output should have a minimum capacity of SHA256_HMAC_DIGEST_SIZE. + */ + bool AWS_CRT_CPP_API ComputeSHA256HMAC( + Allocator *allocator, + const ByteCursor &secret, + const ByteCursor &input, + ByteBuf &output, + size_t truncateTo = 0) noexcept; + + /** + * Computes a SHA256 HMAC using the default allocator with secret over input, and writes the digest to + * output. If truncateTo is non-zero, the digest will be truncated to the value of truncateTo. Returns true + * on success. If this function fails, Aws::Crt::LastError() will contain the error that occurred. Unless + * you're using 'truncateTo', output should have a minimum capacity of SHA256_HMAC_DIGEST_SIZE. + */ + bool AWS_CRT_CPP_API ComputeSHA256HMAC( + const ByteCursor &secret, + const ByteCursor &input, + ByteBuf &output, + size_t truncateTo = 0) noexcept; + /** + * Streaming HMAC object. The typical use case is for computing the HMAC of an object that is too large to + * load into memory. You can call Update() multiple times as you load chunks of data into memory. When + * you're finished simply call Digest(). After Digest() is called, this object is no longer usable. + */ + class AWS_CRT_CPP_API HMAC final + { + public: + ~HMAC(); + HMAC(const HMAC &) = delete; + HMAC &operator=(const HMAC &) = delete; + HMAC(HMAC &&toMove); + HMAC &operator=(HMAC &&toMove); + + /** + * Returns true if the instance is in a valid state, false otherwise. + */ + inline operator bool() const noexcept { return m_good; } + + /** + * Returns the value of the last aws error encountered by operations on this instance. + */ + inline int LastError() const noexcept { return m_lastError; } + + /** + * Creates an instance of a Streaming SHA256 HMAC. + */ + static HMAC CreateSHA256HMAC(Allocator *allocator, const ByteCursor &secret) noexcept; + + /** + * Creates an instance of a Streaming SHA256 HMAC using the Default Allocator. + */ + static HMAC CreateSHA256HMAC(const ByteCursor &secret) noexcept; + + /** + * Updates the running HMAC object with data in toHMAC. Returns true on success. Call + * LastError() for the reason this call failed. + */ + bool Update(const ByteCursor &toHMAC) noexcept; + + /** + * Finishes the running HMAC operation and writes the digest into output. The available capacity of + * output must be large enough for the digest. See: SHA256_DIGEST_SIZE and MD5_DIGEST_SIZE for size + * hints. 'truncateTo' is for if you want truncated output (e.g. you only want the first 16 bytes of a + * SHA256 digest. Returns true on success. Call LastError() for the reason this call failed. + */ + bool Digest(ByteBuf &output, size_t truncateTo = 0) noexcept; + + private: + HMAC(aws_hmac *hmac) noexcept; + HMAC() = delete; + + aws_hmac *m_hmac; + bool m_good; + int m_lastError; + }; + + /** + * BYO_CRYPTO: Base class for custom HMAC implementations. + * + * If using BYO_CRYPTO, you must define concrete implementations for the required HMAC algorithms + * and set their creation callbacks via functions like ApiHandle.SetBYOCryptoNewSHA256HMACCallback(). + */ + class AWS_CRT_CPP_API ByoHMAC + { + public: + virtual ~ByoHMAC() = default; + + /** @private + * this is called by the framework. If you're trying to create instances of this class manually, + * please don't. But if you do. Look at the other factory functions for reference. + */ + aws_hmac *SeatForCInterop(const std::shared_ptr<ByoHMAC> &selfRef); + + protected: + ByoHMAC(size_t digestSize, const ByteCursor &secret, Allocator *allocator = ApiAllocator()); + + /** + * Updates the running HMAC with to_hash. + * This can be called multiple times. + * Raise an AWS error and return false to indicate failure. + */ + virtual bool UpdateInternal(const ByteCursor &toHash) noexcept = 0; + + /** + * Complete the HMAC computation and write the final digest to output. + * This cannote be called more than once. + * If truncate_to is something other than 0, the output must be truncated to that number of bytes. + * Raise an AWS error and return false to indicate failure. + */ + virtual bool DigestInternal(ByteBuf &output, size_t truncateTo = 0) noexcept = 0; + + private: + static void s_Destroy(struct aws_hmac *hmac); + static int s_Update(struct aws_hmac *hmac, const struct aws_byte_cursor *buf); + static int s_Finalize(struct aws_hmac *hmac, struct aws_byte_buf *out); + + static aws_hmac_vtable s_Vtable; + aws_hmac m_hmacValue; + std::shared_ptr<ByoHMAC> m_selfReference; + }; + + using CreateHMACCallback = + std::function<std::shared_ptr<ByoHMAC>(size_t digestSize, const ByteCursor &secret, Allocator *)>; + + } // namespace Crypto + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/crypto/Hash.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/crypto/Hash.h new file mode 100644 index 0000000000..42b9a3ed34 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/crypto/Hash.h @@ -0,0 +1,168 @@ +#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/cal/hash.h> + +struct aws_hash; +namespace Aws +{ + namespace Crt + { + namespace Crypto + { + static const size_t SHA256_DIGEST_SIZE = 32; + static const size_t MD5_DIGEST_SIZE = 16; + + /** + * Computes a SHA256 Hash over input, and writes the digest to output. If truncateTo is non-zero, the digest + * will be truncated to the value of truncateTo. Returns true on success. If this function fails, + * Aws::Crt::LastError() will contain the error that occurred. Unless you're using 'truncateTo', output + * should have a minimum capacity of SHA256_DIGEST_SIZE. + */ + bool AWS_CRT_CPP_API ComputeSHA256( + Allocator *allocator, + const ByteCursor &input, + ByteBuf &output, + size_t truncateTo = 0) noexcept; + + /** + * Computes a SHA256 Hash using the default allocator over input, and writes the digest to output. If + * truncateTo is non-zero, the digest will be truncated to the value of truncateTo. Returns true on success. + * If this function fails, Aws::Crt::LastError() will contain the error that occurred. Unless you're using + * 'truncateTo', output should have a minimum capacity of SHA256_DIGEST_SIZE. + */ + bool AWS_CRT_CPP_API + ComputeSHA256(const ByteCursor &input, ByteBuf &output, size_t truncateTo = 0) noexcept; + + /** + * Computes a MD5 Hash over input, and writes the digest to output. If truncateTo is non-zero, the digest + * will be truncated to the value of truncateTo. Returns true on success. If this function fails, + * Aws::Crt::LastError() will contain the error that occurred. Unless you're using 'truncateTo', + * output should have a minimum capacity of MD5_DIGEST_SIZE. + */ + bool AWS_CRT_CPP_API ComputeMD5( + Allocator *allocator, + const ByteCursor &input, + ByteBuf &output, + size_t truncateTo = 0) noexcept; + + /** + * Computes a MD5 Hash using the default allocator over input, and writes the digest to output. If + * truncateTo is non-zero, the digest will be truncated to the value of truncateTo. Returns true on success. + * If this function fails, Aws::Crt::LastError() will contain the error that occurred. Unless you're using + * 'truncateTo', output should have a minimum capacity of MD5_DIGEST_SIZE. + */ + bool AWS_CRT_CPP_API ComputeMD5(const ByteCursor &input, ByteBuf &output, size_t truncateTo = 0) noexcept; + + /** + * Streaming Hash object. The typical use case is for computing the hash of an object that is too large to + * load into memory. You can call Update() multiple times as you load chunks of data into memory. When + * you're finished simply call Digest(). After Digest() is called, this object is no longer usable. + */ + class AWS_CRT_CPP_API Hash final + { + public: + ~Hash(); + Hash(const Hash &) = delete; + Hash &operator=(const Hash &) = delete; + Hash(Hash &&toMove); + Hash &operator=(Hash &&toMove); + + /** + * Returns true if the instance is in a valid state, false otherwise. + */ + inline operator bool() const noexcept { return m_good; } + + /** + * Returns the value of the last aws error encountered by operations on this instance. + */ + inline int LastError() const noexcept { return m_lastError; } + + /** + * Creates an instance of a Streaming SHA256 Hash. + */ + static Hash CreateSHA256(Allocator *allocator = ApiAllocator()) noexcept; + + /** + * Creates an instance of a Streaming MD5 Hash. + */ + static Hash CreateMD5(Allocator *allocator = ApiAllocator()) noexcept; + + /** + * Updates the running hash object with data in toHash. Returns true on success. Call + * LastError() for the reason this call failed. + */ + bool Update(const ByteCursor &toHash) noexcept; + + /** + * Finishes the running hash operation and writes the digest into output. The available capacity of + * output must be large enough for the digest. See: SHA256_DIGEST_SIZE and MD5_DIGEST_SIZE for size + * hints. 'truncateTo' is for if you want truncated output (e.g. you only want the first 16 bytes of a + * SHA256 digest. Returns true on success. Call LastError() for the reason this call failed. + */ + bool Digest(ByteBuf &output, size_t truncateTo = 0) noexcept; + + private: + Hash(aws_hash *hash) noexcept; + Hash() = delete; + + aws_hash *m_hash; + bool m_good; + int m_lastError; + }; + + /** + * BYO_CRYPTO: Base class for custom hash implementations. + * + * If using BYO_CRYPTO, you must define concrete implementations for the required hash algorithms + * and set their creation callbacks via functions like ApiHandle.SetBYOCryptoNewMD5Callback(). + */ + class AWS_CRT_CPP_API ByoHash + { + public: + virtual ~ByoHash(); + + /** @private + * this is called by the framework. If you're trying to create instances of this class manually, + * please don't. But if you do. Look at the other factory functions for reference. + */ + aws_hash *SeatForCInterop(const std::shared_ptr<ByoHash> &selfRef); + + protected: + ByoHash(size_t digestSize, Allocator *allocator = ApiAllocator()); + + /** + * Update the running hash with to_hash. + * This can be called multiple times. + * Raise an AWS error and return false to indicate failure. + */ + virtual bool UpdateInternal(const ByteCursor &toHash) noexcept = 0; + + /** + * Complete the hash computation and write the final digest to output. + * This cannote be called more than once. + * If truncate_to is something other than 0, the output must be truncated to that number of bytes. + * Raise an AWS error and return false to indicate failure. + */ + virtual bool DigestInternal(ByteBuf &output, size_t truncateTo = 0) noexcept = 0; + + private: + static void s_Destroy(struct aws_hash *hash); + static int s_Update(struct aws_hash *hash, const struct aws_byte_cursor *buf); + static int s_Finalize(struct aws_hash *hash, struct aws_byte_buf *out); + + static aws_hash_vtable s_Vtable; + aws_hash m_hashValue; + std::shared_ptr<ByoHash> m_selfReference; + }; + + using CreateHashCallback = std::function<std::shared_ptr<ByoHash>(size_t digestSize, Allocator *)>; + + } // namespace Crypto + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/endpoints/RuleEngine.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/endpoints/RuleEngine.h new file mode 100644 index 0000000000..84baff71d9 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/endpoints/RuleEngine.h @@ -0,0 +1,155 @@ +#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_endpoints_rule_engine; +struct aws_endpoints_request_context; +struct aws_endpoints_resolved_endpoint; + +namespace Aws +{ + namespace Crt + { + namespace Endpoints + { + /* + * Add parameter to the context. + * Only string and boolean values are supported. + * Adding parameter several times with the same name will overwrite + * previous values. + */ + class AWS_CRT_CPP_API RequestContext final + { + public: + RequestContext(Allocator *allocator = ApiAllocator()) noexcept; + ~RequestContext(); + + /* TODO: move/copy semantics */ + RequestContext(const RequestContext &) = delete; + RequestContext &operator=(const RequestContext &) = delete; + RequestContext(RequestContext &&) = delete; + RequestContext &operator=(RequestContext &&) = delete; + + /** + * @return true if the instance is in a valid state, false otherwise. + */ + operator bool() const noexcept { return m_requestContext != nullptr; } + + /* + * Add string parameter. + * True if added successfully and false if failed. + * Aws::Crt::LastError() can be used to retrieve failure error code. + */ + bool AddString(const ByteCursor &name, const ByteCursor &value); + + /* + * Add boolean parameter. + * True if added successfully and false if failed. + * Aws::Crt::LastError() can be used to retrieve failure error code. + */ + bool AddBoolean(const ByteCursor &name, bool value); + + /// @private + aws_endpoints_request_context *GetNativeHandle() const noexcept { return m_requestContext; } + + private: + Allocator *m_allocator; + aws_endpoints_request_context *m_requestContext; + }; + + /* + * Outcome of Endpoint Resolution. + * Outcome can be either endpoint (IsEndpoint) or error (IsError). + * Endpoint outcome means that engine was able to resolve context to + * an endpoint and outcome can have the following fields defined: + * - Url (required) - resolved url + * - Headers (optional) - additional headers to be included with request + * - Properties (optional) - custom list of properties associated + * with request (json blob to be interpreted by the caller.) + * + * Error outcome means that context could not be resolved to an endpoint. + * Outcome will have following fields: + * - Error (required) - error message providing more info on why + * endpoint could not be resolved. + */ + class AWS_CRT_CPP_API ResolutionOutcome final + { + public: + ~ResolutionOutcome(); + + /* TODO: move/copy semantics */ + ResolutionOutcome(const ResolutionOutcome &) = delete; + ResolutionOutcome &operator=(const ResolutionOutcome &) = delete; + ResolutionOutcome(ResolutionOutcome &&toMove) noexcept; + ResolutionOutcome &operator=(ResolutionOutcome &&); + + bool IsEndpoint() const noexcept; + bool IsError() const noexcept; + + /* + * Endpoint properties. + * Note: following fields are none if outcome is error. + * Headers and Properties are optional and could also be None. + */ + Optional<StringView> GetUrl() const; + Optional<StringView> GetProperties() const; + Optional<UnorderedMap<StringView, Vector<StringView>>> GetHeaders() const; + + /* + * Error properties. + * Note: following fields are none if outcome is error. + */ + Optional<StringView> GetError() const; + + /** + * @return true if the instance is in a valid state, false otherwise. + */ + operator bool() const noexcept { return m_resolvedEndpoint != nullptr; } + + /// @private For use by rule engine. + ResolutionOutcome(aws_endpoints_resolved_endpoint *impl); + + private: + aws_endpoints_resolved_endpoint *m_resolvedEndpoint; + }; + + /** + * Endpoints Rule Engine. + */ + class AWS_CRT_CPP_API RuleEngine final + { + public: + RuleEngine( + const ByteCursor &rulesetCursor, + const ByteCursor &partitionsCursor, + Allocator *allocator = ApiAllocator()) noexcept; + ~RuleEngine(); + + RuleEngine(const RuleEngine &) = delete; + RuleEngine &operator=(const RuleEngine &) = delete; + RuleEngine(RuleEngine &&) = delete; + RuleEngine &operator=(RuleEngine &&) = delete; + + /** + * @return true if the instance is in a valid state, false otherwise. + */ + operator bool() const noexcept { return m_ruleEngine != nullptr; } + + /* + * Resolves rules against the provided context. + * If successful return will have resolution outcome. + * If not, return will be none and Aws::Crt::LastError() can be + * used to retrieve CRT error code. + */ + Optional<ResolutionOutcome> Resolve(const RequestContext &context) const; + + private: + aws_endpoints_rule_engine *m_ruleEngine; + }; + } // namespace Endpoints + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/external/cJSON.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/external/cJSON.h new file mode 100644 index 0000000000..ee10f0225d --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/external/cJSON.h @@ -0,0 +1,309 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/** MODIFICATIONS: + * valueInt was moved up to improve alignment. + * Wrap all symbols in the Aws namespace as a short-term collision resolution + * Replace strcpy() with strncpy() + * + * Modifications licensed under: + * + * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +#ifndef cJSON__h +#define cJSON__h + +namespace Aws { + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif + +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 3 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +#define CJSON_CDECL __cdecl +#define CJSON_STDCALL __stdcall + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type CJSON_STDCALL +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type CJSON_STDCALL +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type CJSON_STDCALL +#endif +#else /* !__WINDOWS__ */ +#define CJSON_CDECL +#define CJSON_STDCALL + +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 14 + +#include <stddef.h> + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + /* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */ + void *(CJSON_CDECL *malloc_fn)(size_t sz); + void (CJSON_CDECL *free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated); + +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "index" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check item type and return its value */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item); +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/array that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); + +/* These utilities create an Array of count items. + * The parameter count cannot be greater than the number of elements in the number array, otherwise array access will be out of bounds.*/ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detach items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will + * need to be released. With recurse!=0, it will duplicate any children connected to the item. + * The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + +/* Minify a strings, remove blank characters(such as ' ', '\t', '\r', '\n') from strings. + * The input pointer json cannot point to a read-only address area, such as a string constant, + * but should point to a readable and writable adress area. */ +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) +/* Change the valuestring of a cJSON_String object, only takes effect when type of object is cJSON_String */ +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring); + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +} + +#endif diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/http/HttpConnection.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/http/HttpConnection.h new file mode 100644 index 0000000000..1ae79cefd8 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/http/HttpConnection.h @@ -0,0 +1,514 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/http/connection.h> +#include <aws/http/proxy.h> +#include <aws/http/request_response.h> + +#include <aws/crt/Types.h> +#include <aws/crt/io/Bootstrap.h> +#include <aws/crt/io/SocketOptions.h> +#include <aws/crt/io/TlsOptions.h> + +#include <functional> +#include <memory> + +namespace Aws +{ + namespace Crt + { + namespace Io + { + class ClientBootstrap; + } + + namespace Http + { + class HttpClientConnection; + class HttpStream; + class HttpClientStream; + class HttpRequest; + class HttpProxyStrategy; + using HttpHeader = aws_http_header; + + /** + * Invoked upon connection setup, whether it was successful or not. If the connection was + * successfully established, `connection` will be valid and errorCode will be AWS_ERROR_SUCCESS. + * Upon an error, `connection` will not be valid, and errorCode will contain the cause of the connection + * failure. + */ + using OnConnectionSetup = + std::function<void(const std::shared_ptr<HttpClientConnection> &connection, int errorCode)>; + + /** + * Invoked upon connection shutdown. `connection` will always be a valid pointer. `errorCode` will specify + * shutdown reason. A graceful connection close will set `errorCode` to AWS_ERROR_SUCCESS. + * Internally, the connection pointer will be unreferenced immediately after this call; if you took a + * reference to it in OnConnectionSetup(), you'll need to release your reference before the underlying + * memory is released. If you never took a reference to it, the resources for the connection will be + * immediately released after completion of this callback. + */ + using OnConnectionShutdown = std::function<void(HttpClientConnection &connection, int errorCode)>; + + /** + * Called as headers are received from the peer. `headersArray` will contain the header value + * read from the wire. The number of entries in `headersArray` are specified in `headersCount`. + * + * Keep in mind that this function will likely be called multiple times until all headers are received. + * + * On HttpStream, this function must be set. + */ + using OnIncomingHeaders = std::function<void( + HttpStream &stream, + enum aws_http_header_block headerBlock, + const HttpHeader *headersArray, + std::size_t headersCount)>; + + /** + * Invoked when the headers portion of the message has been completely received. `hasBody` will indicate + * if there is an incoming body. + * + * On HttpStream, this function can be empty. + */ + using OnIncomingHeadersBlockDone = + std::function<void(HttpStream &stream, enum aws_http_header_block block)>; + + /** + * Invoked as chunks of the body are read. `data` contains the data read from the wire. If chunked encoding + * was used, it will already be decoded (TBD). + * + * On HttpStream, this function can be empty if you are not expecting a body (e.g. a HEAD request). + */ + using OnIncomingBody = std::function<void(HttpStream &stream, const ByteCursor &data)>; + + /** + * Invoked upon completion of the stream. This means the request has been sent and a completed response + * has been received (in client mode), or the request has been received and the response has been completed. + * + * In H2, this will mean RST_STREAM state has been reached for the stream. + * + * On HttpStream, this function must be set. + */ + using OnStreamComplete = std::function<void(HttpStream &stream, int errorCode)>; + + /** + * POD structure used for setting up an Http Request + */ + struct AWS_CRT_CPP_API HttpRequestOptions + { + /** + * The actual http request + */ + HttpRequest *request; + + /** + * See `OnIncomingHeaders` for more info. This value must be set. + */ + OnIncomingHeaders onIncomingHeaders; + OnIncomingHeadersBlockDone onIncomingHeadersBlockDone; + + /** + * See `OnIncomingBody` for more info. This value can be empty if you will not be receiving a body. + */ + OnIncomingBody onIncomingBody; + + /** + * See `OnStreamComplete` for more info. This value can be empty. + */ + OnStreamComplete onStreamComplete; + }; + + /** + * Represents a single http message exchange (request/response) or in H2, it can also represent + * a PUSH_PROMISE followed by the accompanying Response. + */ + class AWS_CRT_CPP_API HttpStream : public std::enable_shared_from_this<HttpStream> + { + public: + virtual ~HttpStream(); + HttpStream(const HttpStream &) = delete; + HttpStream(HttpStream &&) = delete; + HttpStream &operator=(const HttpStream &) = delete; + HttpStream &operator=(HttpStream &&) = delete; + + /** + * Get the underlying connection for the stream. + */ + HttpClientConnection &GetConnection() const noexcept; + + /** + * @return request's Http Response Code. Requires response headers to have been processed first. * + */ + virtual int GetResponseStatusCode() const noexcept = 0; + + /** + * Updates the read window on the connection. In Http 1.1 this relieves TCP back pressure, in H2 + * this will trigger two WINDOW_UPDATE frames, one for the connection and one for the stream. + * + * You do not need to call this unless you utilized the `outWindowUpdateSize` in `OnIncomingBody`. + * See `OnIncomingBody` for more information. + * + * `incrementSize` is the amount to update the read window by. + */ + void UpdateWindow(std::size_t incrementSize) noexcept; + + protected: + aws_http_stream *m_stream; + std::shared_ptr<HttpClientConnection> m_connection; + HttpStream(const std::shared_ptr<HttpClientConnection> &connection) noexcept; + + private: + OnIncomingHeaders m_onIncomingHeaders; + OnIncomingHeadersBlockDone m_onIncomingHeadersBlockDone; + OnIncomingBody m_onIncomingBody; + OnStreamComplete m_onStreamComplete; + + static int s_onIncomingHeaders( + struct aws_http_stream *stream, + enum aws_http_header_block headerBlock, + const struct aws_http_header *headerArray, + size_t numHeaders, + void *userData) noexcept; + static int s_onIncomingHeaderBlockDone( + struct aws_http_stream *stream, + enum aws_http_header_block headerBlock, + void *userData) noexcept; + static int s_onIncomingBody( + struct aws_http_stream *stream, + const struct aws_byte_cursor *data, + void *userData) noexcept; + static void s_onStreamComplete(struct aws_http_stream *stream, int errorCode, void *userData) noexcept; + + friend class HttpClientConnection; + }; + + struct ClientStreamCallbackData + { + ClientStreamCallbackData() : allocator(nullptr), stream(nullptr) {} + Allocator *allocator; + std::shared_ptr<HttpStream> stream; + }; + + /** + * Subclass that represents an http client's view of an HttpStream. + */ + class AWS_CRT_CPP_API HttpClientStream final : public HttpStream + { + public: + ~HttpClientStream(); + HttpClientStream(const HttpClientStream &) = delete; + HttpClientStream(HttpClientStream &&) = delete; + HttpClientStream &operator=(const HttpClientStream &) = delete; + HttpClientStream &operator=(HttpClientStream &&) = delete; + + /** + * If this stream was initiated as a request, assuming the headers of the response has been + * received, this value contains the Http Response Code. * + */ + virtual int GetResponseStatusCode() const noexcept override; + + /** + * Activates the request's outgoing stream processing. + * + * Returns true on success, false otherwise. + */ + bool Activate() noexcept; + + private: + HttpClientStream(const std::shared_ptr<HttpClientConnection> &connection) noexcept; + + ClientStreamCallbackData m_callbackData; + friend class HttpClientConnection; + }; + + /** + * @deprecated enum that designates what kind of authentication, if any, to use when connecting to a + * proxy server. + * + * Here for backwards compatibility. Has been superceded by proxy strategies. + */ + enum class AwsHttpProxyAuthenticationType + { + None, + Basic, + }; + + /** + * Mirror of aws_http_proxy_connection_type enum. Indicates the basic http proxy behavior of the + * proxy we're connecting to. + */ + enum class AwsHttpProxyConnectionType + { + /** + * Deprecated, but 0-valued for backwards compatibility + * + * If tls options are provided (for the main connection) then treat the proxy as a tunneling proxy + * If tls options are not provided (for the main connection), then treat the proxy as a forwarding + * proxy + */ + Legacy = AWS_HPCT_HTTP_LEGACY, + + /** + * Use the proxy to forward http requests. Attempting to use both this mode and TLS to the destination + * is a configuration error. + */ + Forwarding = AWS_HPCT_HTTP_FORWARD, + + /** + * Use the proxy to establish an http connection via a CONNECT request to the proxy. Works for both + * plaintext and tls connections. + */ + Tunneling = AWS_HPCT_HTTP_TUNNEL, + }; + + /** + * Configuration structure that holds all proxy-related http connection options + */ + class AWS_CRT_CPP_API HttpClientConnectionProxyOptions + { + public: + HttpClientConnectionProxyOptions(); + HttpClientConnectionProxyOptions(const HttpClientConnectionProxyOptions &rhs) = default; + HttpClientConnectionProxyOptions(HttpClientConnectionProxyOptions &&rhs) = default; + + HttpClientConnectionProxyOptions &operator=(const HttpClientConnectionProxyOptions &rhs) = default; + HttpClientConnectionProxyOptions &operator=(HttpClientConnectionProxyOptions &&rhs) = default; + + ~HttpClientConnectionProxyOptions() = default; + + /** + * Intended for internal use only. Initializes the C proxy configuration structure, + * aws_http_proxy_options, from an HttpClientConnectionProxyOptions instance. + * + * @param raw_options - output parameter containing low level proxy options to be passed to the C + * interface + * + */ + void InitializeRawProxyOptions(struct aws_http_proxy_options &raw_options) const; + + /** + * The name of the proxy server to connect through. + * Required. + */ + String HostName; + + /** + * The port of the proxy server to connect to. + * Required. + */ + uint16_t Port; + + /** + * Sets the TLS options for the connection to the proxy. + * Optional. + */ + Optional<Io::TlsConnectionOptions> TlsOptions; + + /** + * What kind of proxy connection to make + */ + AwsHttpProxyConnectionType ProxyConnectionType; + + /** + * Proxy strategy to use while negotiating the connection. Use null for no additional + * steps. + */ + std::shared_ptr<HttpProxyStrategy> ProxyStrategy; + + /** + * @deprecated What kind of authentication approach to use when connecting to the proxy + * Replaced by proxy strategy + * + * Backwards compatibility achieved by invoking CreateBasicHttpProxyStrategy if + * (1) ProxyStrategy is null + * (2) AuthType is AwsHttpProxyAuthenticationType::Basic + */ + AwsHttpProxyAuthenticationType AuthType; + + /** + * @deprecated The username to use if connecting to the proxy via basic authentication + * Replaced by using the result of CreateBasicHttpProxyStrategy() + */ + String BasicAuthUsername; + + /** + * @deprecated The password to use if connecting to the proxy via basic authentication + * Replaced by using the result of CreateBasicHttpProxyStrategy() + */ + String BasicAuthPassword; + }; + + /** + * Configuration structure holding all options relating to http connection establishment + */ + class AWS_CRT_CPP_API HttpClientConnectionOptions + { + public: + HttpClientConnectionOptions(); + HttpClientConnectionOptions(const HttpClientConnectionOptions &rhs) = default; + HttpClientConnectionOptions(HttpClientConnectionOptions &&rhs) = default; + + ~HttpClientConnectionOptions() = default; + + HttpClientConnectionOptions &operator=(const HttpClientConnectionOptions &rhs) = default; + HttpClientConnectionOptions &operator=(HttpClientConnectionOptions &&rhs) = default; + + /** + * The client bootstrap to use for setting up and tearing down connections. + * Note: If null, then the default ClientBootstrap is used + * (see Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap) + */ + Io::ClientBootstrap *Bootstrap; + + /** + * The TCP read window allowed for Http 1.1 connections and Initial Windows for H2 connections. + */ + size_t InitialWindowSize; + + /** + * The callback invoked on connection establishment, whether success or failure. + * See `OnConnectionSetup` for more info. + * Required. + */ + OnConnectionSetup OnConnectionSetupCallback; + + /** + * The callback invoked on connection shutdown. + * See `OnConnectionShutdown` for more info. + * Required. + */ + OnConnectionShutdown OnConnectionShutdownCallback; + + /** + * The name of the http server to connect to. + * Required. + */ + String HostName; + + /** + * The port of the http server to connect to. + * Required. + */ + uint16_t Port; + + /** + * The socket options of the connection. + * Required. + */ + Io::SocketOptions SocketOptions; + + /** + * The TLS options for the http connection. + * Optional. + */ + Optional<Io::TlsConnectionOptions> TlsOptions; + + /** + * The proxy options for the http connection. + * Optional. + */ + Optional<HttpClientConnectionProxyOptions> ProxyOptions; + + /** + * If set to true, then the TCP read back pressure mechanism will be enabled. You should + * only use this if you're allowing http response body data to escape the callbacks. E.g. you're + * putting the data into a queue for another thread to process and need to make sure the memory + * usage is bounded. If this is enabled, you must call HttpStream::UpdateWindow() for every + * byte read from the OnIncomingBody callback. + */ + bool ManualWindowManagement; + }; + + enum class HttpVersion + { + Unknown = AWS_HTTP_VERSION_UNKNOWN, + Http1_0 = AWS_HTTP_VERSION_1_0, + Http1_1 = AWS_HTTP_VERSION_1_1, + Http2 = AWS_HTTP_VERSION_2, + }; + + /** + * Represents a connection from a Http Client to a Server. + */ + class AWS_CRT_CPP_API HttpClientConnection : public std::enable_shared_from_this<HttpClientConnection> + { + public: + virtual ~HttpClientConnection() = default; + HttpClientConnection(const HttpClientConnection &) = delete; + HttpClientConnection(HttpClientConnection &&) = delete; + HttpClientConnection &operator=(const HttpClientConnection &) = delete; + HttpClientConnection &operator=(HttpClientConnection &&) = delete; + + /** + * Make a new client initiated request on this connection. + * + * If you take a reference to the return value, the memory and resources for the connection + * and stream will not be cleaned up until you release it. You can however, release the reference + * as soon as you don't need it anymore. The internal reference count ensures the resources will + * not be freed until the stream is completed. + * + * Returns an instance of HttpStream upon success and nullptr on failure. + * + * You must call HttpClientStream::Activate() to begin outgoing processing of the stream. + */ + std::shared_ptr<HttpClientStream> NewClientStream(const HttpRequestOptions &requestOptions) noexcept; + + /** + * @return true unless the connection is closed or closing. + */ + bool IsOpen() const noexcept; + + /** + * Initiate a shutdown of the connection. Sometimes, connections are persistent and you want + * to close them before shutting down your application or whatever is consuming this interface. + * + * Assuming `OnConnectionShutdown` has not already been invoked, it will be invoked as a result of this + * call. + */ + void Close() noexcept; + + /** + * @return protocol version the connection used + */ + HttpVersion GetVersion() noexcept; + + /** + * @return the value of the last aws error encountered by operations on this instance. + */ + int LastError() const noexcept { return m_lastError; } + + /** + * Create a new Https Connection to hostName:port, using `socketOptions` for tcp options and + * `tlsConnOptions` for TLS/SSL options. If `tlsConnOptions` is null http (plain-text) will be used. + * + * returns true on success, and false on failure. If false is returned, `onConnectionSetup` will not + * be invoked. On success, `onConnectionSetup` will be called, either with a connection, or an + * errorCode. + */ + static bool CreateConnection( + const HttpClientConnectionOptions &connectionOptions, + Allocator *allocator) noexcept; + + protected: + HttpClientConnection(aws_http_connection *m_connection, Allocator *allocator) noexcept; + aws_http_connection *m_connection; + + private: + Allocator *m_allocator; + int m_lastError; + + static void s_onClientConnectionSetup( + struct aws_http_connection *connection, + int error_code, + void *user_data) noexcept; + static void s_onClientConnectionShutdown( + struct aws_http_connection *connection, + int error_code, + void *user_data) noexcept; + }; + + } // namespace Http + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/http/HttpConnectionManager.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/http/HttpConnectionManager.h new file mode 100644 index 0000000000..0ecd9b48d7 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/http/HttpConnectionManager.h @@ -0,0 +1,127 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/http/HttpConnection.h> + +#include <atomic> +#include <condition_variable> +#include <future> +#include <mutex> + +struct aws_http_connection_manager; + +namespace Aws +{ + namespace Crt + { + namespace Http + { + /** + * Invoked when a connection from the pool is available. If a connection was successfully obtained + * the connection shared_ptr can be seated into your own copy of connection. If it failed, errorCode + * will be non-zero. + */ + using OnClientConnectionAvailable = + std::function<void(std::shared_ptr<HttpClientConnection>, int errorCode)>; + + /** + * Configuration struct containing all options related to connection manager behavior + */ + class AWS_CRT_CPP_API HttpClientConnectionManagerOptions + { + public: + HttpClientConnectionManagerOptions() noexcept; + HttpClientConnectionManagerOptions(const HttpClientConnectionManagerOptions &rhs) = default; + HttpClientConnectionManagerOptions(HttpClientConnectionManagerOptions &&rhs) = default; + + HttpClientConnectionManagerOptions &operator=(const HttpClientConnectionManagerOptions &rhs) = default; + HttpClientConnectionManagerOptions &operator=(HttpClientConnectionManagerOptions &&rhs) = default; + + /** + * The http connection options to use for each connection created by the manager + */ + HttpClientConnectionOptions ConnectionOptions; + + /** + * The maximum number of connections the manager is allowed to create/manage + */ + size_t MaxConnections; + + /** If set, initiate shutdown will return a future that will allow a user to block until the + * connection manager has completely released all resources. 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. If this is set, you MUST call InitiateShutdown() before releasing your last + * reference to the connection manager. + */ + bool EnableBlockingShutdown; + }; + + /** + * Manages a pool of connections to a specific endpoint using the same socket and tls options. + */ + class AWS_CRT_CPP_API HttpClientConnectionManager final + : public std::enable_shared_from_this<HttpClientConnectionManager> + { + public: + ~HttpClientConnectionManager(); + + /** + * Acquires a connection from the pool. onClientConnectionAvailable will be invoked upon an available + * connection. Returns true if the connection request was successfully queued, returns false if it + * failed. On failure, onClientConnectionAvailable will not be invoked. After receiving a connection, it + * will automatically be cleaned up when your last reference to the shared_ptr is released. + * + * @param onClientConnectionAvailable callback to invoke when a connection becomes available or the + * acquisition attempt terminates + * @return true if the acquisition was successfully kicked off, false otherwise (no callback) + */ + bool AcquireConnection(const OnClientConnectionAvailable &onClientConnectionAvailable) noexcept; + + /** + * Starts shutdown of the connection manager. Returns a future to the connection manager's shutdown + * process. If EnableBlockingDestruct was enabled on the connection manager options, calling get() on + * the returned future will block until the last connection is released. If the option is not set, get() + * will immediately return. + * @return future which will complete when shutdown has completed + */ + std::future<void> InitiateShutdown() noexcept; + + /** + * Factory function for connection managers + * + * @param connectionManagerOptions connection manager configuration data + * @param allocator allocator to use + * @return a new connection manager instance + */ + static std::shared_ptr<HttpClientConnectionManager> NewClientConnectionManager( + const HttpClientConnectionManagerOptions &connectionManagerOptions, + Allocator *allocator = ApiAllocator()) noexcept; + + private: + HttpClientConnectionManager( + const HttpClientConnectionManagerOptions &options, + Allocator *allocator = ApiAllocator()) noexcept; + + Allocator *m_allocator; + + aws_http_connection_manager *m_connectionManager; + + HttpClientConnectionManagerOptions m_options; + std::promise<void> m_shutdownPromise; + std::atomic<bool> m_releaseInvoked; + + static void s_onConnectionSetup( + aws_http_connection *connection, + int errorCode, + void *userData) noexcept; + + static void s_shutdownCompleted(void *userData) noexcept; + + friend class ManagedConnection; + }; + } // namespace Http + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/http/HttpProxyStrategy.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/http/HttpProxyStrategy.h new file mode 100644 index 0000000000..bf72490693 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/http/HttpProxyStrategy.h @@ -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> + +#include <memory> + +struct aws_http_proxy_strategy; + +namespace Aws +{ + namespace Crt + { + namespace Http + { + enum class AwsHttpProxyConnectionType; + + /** + * Configuration for a proxy strategy that performs basic authentication + */ + struct AWS_CRT_CPP_API HttpProxyStrategyBasicAuthConfig + { + HttpProxyStrategyBasicAuthConfig(); + + /** + * Basic auth can be applied either to forwarding or tunneling proxy connections, but we need + * to know the type ahead of time + */ + AwsHttpProxyConnectionType ConnectionType; + + /** + * Username to apply to the basic authentication process + */ + String Username; + + /** + * Password to apply to the basic authentication process + */ + String Password; + }; + + using KerberosGetTokenFunction = std::function<bool(String &)>; + using NtlmGetTokenFunction = std::function<bool(const String &, String &)>; + + /** + * Configuration for a proxy strategy that attempts to use kerberos and ntlm, based on authentication + * failure feedback from the proxy's responses to CONNECT attempts. The kerberos/ntlm callbacks are + * currently synchronous but invoked potentially from within event loop threads. This is not optimal + * but transitioning to fully async hasn't been a need yet. + * + * The adapative strategy will skip an authentication method whose callbacks are not supplied, so you + * can use this for purely kerberos or ntlm as well. + */ + struct AWS_CRT_CPP_API HttpProxyStrategyAdaptiveConfig + { + HttpProxyStrategyAdaptiveConfig() : KerberosGetToken(), NtlmGetCredential(), NtlmGetToken() {} + + /** + * User-supplied callback for fetching kerberos tokens + */ + KerberosGetTokenFunction KerberosGetToken; + + /** + * User-supplied callback for fetching an ntlm credential + */ + KerberosGetTokenFunction NtlmGetCredential; + + /** + * User-supplied callback for fetching an ntlm token + */ + NtlmGetTokenFunction NtlmGetToken; + }; + + /** + * Wrapper class for a C-level proxy strategy - an object that allows the user to transform or modify + * the authentication logic when connecting to a proxy. + */ + class AWS_CRT_CPP_API HttpProxyStrategy + { + public: + HttpProxyStrategy(struct aws_http_proxy_strategy *strategy); + virtual ~HttpProxyStrategy(); + + /// @private + struct aws_http_proxy_strategy *GetUnderlyingHandle() const noexcept { return m_strategy; } + + /** + * Creates a proxy strategy that performs basic authentication + * @param config basic authentication configuration options + * @param allocator allocator to use + * @return a new basic authentication proxy strategy + */ + static std::shared_ptr<HttpProxyStrategy> CreateBasicHttpProxyStrategy( + const HttpProxyStrategyBasicAuthConfig &config, + Allocator *allocator = ApiAllocator()); + + /** + * Creates a proxy strategy that, depending on configuration, can attempt kerberos and/or ntlm + * authentication when connecting to the proxy + * @param config the adaptive strategy configuration options + * @param allocator allocator to use + * @return a new adaptive proxy strategy + */ + static std::shared_ptr<HttpProxyStrategy> CreateAdaptiveHttpProxyStrategy( + const HttpProxyStrategyAdaptiveConfig &config, + Allocator *allocator = ApiAllocator()); + + protected: + struct aws_http_proxy_strategy *m_strategy; + }; + } // namespace Http + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/http/HttpRequestResponse.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/http/HttpRequestResponse.h new file mode 100644 index 0000000000..bf305e8b99 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/http/HttpRequestResponse.h @@ -0,0 +1,160 @@ +#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/Stream.h> + +struct aws_http_header; +struct aws_http_message; + +namespace Aws +{ + namespace Crt + { + namespace Mqtt + { + class MqttConnection; + } + namespace Mqtt5 + { + class Mqtt5Client; + } + namespace Http + { + using HttpHeader = aws_http_header; + + /** + * Base class representing a mutable http request or response. + */ + class AWS_CRT_CPP_API HttpMessage + { + public: + virtual ~HttpMessage(); + + HttpMessage(const HttpMessage &) = delete; + HttpMessage(HttpMessage &&) = delete; + HttpMessage &operator=(const HttpMessage &) = delete; + HttpMessage &operator=(HttpMessage &&) = delete; + + /** + * Gets the input stream representing the message body + */ + std::shared_ptr<Aws::Crt::Io::InputStream> GetBody() const noexcept; + + /** + * Sets the input stream representing the message body + * @param body the input stream representing the message body + * @return success/failure + */ + bool SetBody(const std::shared_ptr<Aws::Crt::Io::IStream> &body) noexcept; + + /** + * Sets the input stream representing the message body + * @param body the input stream representing the message body + * @return success/failure + */ + bool SetBody(const std::shared_ptr<Aws::Crt::Io::InputStream> &body) noexcept; + + /** + * Gets the number of headers contained in this request + * @return the number of headers contained in this request + */ + size_t GetHeaderCount() const noexcept; + + /** + * Gets a particular header in the request + * @param index index of the header to fetch + * @return an option containing the requested header if the index is in bounds + */ + Optional<HttpHeader> GetHeader(size_t index) const noexcept; + + /** + * Adds a header to the request + * @param header header to add + * @return success/failure + */ + bool AddHeader(const HttpHeader &header) noexcept; + + /** + * Removes a header from the request + * @param index index of the header to remove + * @return success/failure + */ + bool EraseHeader(size_t index) noexcept; + + /** + * @return true/false if the underlying object is valid + */ + operator bool() const noexcept { return m_message != nullptr; } + + /// @private + struct aws_http_message *GetUnderlyingMessage() const noexcept { return m_message; } + + protected: + HttpMessage(Allocator *allocator, struct aws_http_message *message) noexcept; + + Allocator *m_allocator; + struct aws_http_message *m_message; + std::shared_ptr<Aws::Crt::Io::InputStream> m_bodyStream; + }; + + /** + * Class representing a mutable http request. + */ + class AWS_CRT_CPP_API HttpRequest : public HttpMessage + { + friend class Mqtt::MqttConnection; + friend class Mqtt5::Mqtt5Client; + + public: + HttpRequest(Allocator *allocator = ApiAllocator()); + + /** + * @return the value of the Http method associated with this request + */ + Optional<ByteCursor> GetMethod() const noexcept; + + /** + * Sets the value of the Http method associated with this request + */ + bool SetMethod(ByteCursor method) noexcept; + + /** + * @return the value of the URI-path associated with this request + */ + Optional<ByteCursor> GetPath() const noexcept; + + /** + * Sets the value of the URI-path associated with this request + */ + bool SetPath(ByteCursor path) noexcept; + + protected: + HttpRequest(Allocator *allocator, struct aws_http_message *message); + }; + + /** + * Class representing a mutable http response. + */ + class AWS_CRT_CPP_API HttpResponse : public HttpMessage + { + public: + HttpResponse(Allocator *allocator = ApiAllocator()); + + /** + * @return the integral Http response code associated with this response + */ + Optional<int> GetResponseCode() const noexcept; + + /** + * Sets the integral Http response code associated with this response + */ + bool SetResponseCode(int response) noexcept; + }; + } // namespace Http + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/Bootstrap.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/Bootstrap.h new file mode 100644 index 0000000000..e1175f83ab --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/Bootstrap.h @@ -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 diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/ChannelHandler.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/ChannelHandler.h new file mode 100644 index 0000000000..7f5c8ecd99 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/ChannelHandler.h @@ -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 diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/EventLoopGroup.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/EventLoopGroup.h new file mode 100644 index 0000000000..0ef904f285 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/EventLoopGroup.h @@ -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 diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/HostResolver.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/HostResolver.h new file mode 100644 index 0000000000..dac0e60237 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/HostResolver.h @@ -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 diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/Pkcs11.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/Pkcs11.h new file mode 100644 index 0000000000..7f10baad83 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/Pkcs11.h @@ -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 diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/SocketOptions.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/SocketOptions.h new file mode 100644 index 0000000000..9f31250dde --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/SocketOptions.h @@ -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 diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/Stream.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/Stream.h new file mode 100644 index 0000000000..cf6c49fa3e --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/Stream.h @@ -0,0 +1,173 @@ +#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; + + /** + * @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; + + 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; + StreamStatus GetStatusImpl() const noexcept override; + int64_t GetLengthImpl() const noexcept override; + bool SeekImpl(OffsetType offsetType, StreamSeekBasis seekBasis) noexcept override; + + private: + std::shared_ptr<Aws::Crt::Io::IStream> m_stream; + }; + } // namespace Io + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/TlsOptions.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/TlsOptions.h new file mode 100644 index 0000000000..afb543a92a --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/TlsOptions.h @@ -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 diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/Uri.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/Uri.h new file mode 100644 index 0000000000..80833f2b12 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/io/Uri.h @@ -0,0 +1,102 @@ +#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. + */ + uint16_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; + }; + } // namespace Io + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/mqtt/Mqtt5Client.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/mqtt/Mqtt5Client.h new file mode 100644 index 0000000000..9968920197 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/mqtt/Mqtt5Client.h @@ -0,0 +1,770 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/http/HttpConnection.h> +#include <aws/crt/mqtt/Mqtt5Types.h> + +namespace Aws +{ + namespace Crt + { + namespace Mqtt5 + { + class ConnectPacket; + class ConnAckPacket; + class DisconnectPacket; + class Mqtt5Client; + class Mqtt5ClientOptions; + class NegotiatedSettings; + class PublishResult; + class PublishPacket; + class PubAckPacket; + class SubscribePacket; + class SubAckPacket; + class UnsubscribePacket; + class UnSubAckPacket; + + struct AWS_CRT_CPP_API ReconnectOptions + { + /** + * Controls how the reconnect delay is modified in order to smooth out the distribution of reconnection + * attempt timepoints for a large set of reconnecting clients. + */ + JitterMode m_reconnectMode; + + /** + * Minimum amount of time to wait to reconnect after a disconnect. Exponential backoff is performed + * with jitter after each connection failure. + */ + uint64_t m_minReconnectDelayMs; + + /** + * Maximum amount of time to wait to reconnect after a disconnect. Exponential backoff is performed + * with jitter after each connection failure. + */ + uint64_t m_maxReconnectDelayMs; + + /** + * Amount of time that must elapse with an established connection before the reconnect delay is reset to + * the minimum. This helps alleviate bandwidth-waste in fast reconnect cycles due to permission failures + * on operations. + */ + uint64_t m_minConnectedTimeToResetReconnectDelayMs; + }; + + /* Simple statistics about the current state of the client's queue of operations */ + struct AWS_CRT_CPP_API Mqtt5ClientOperationStatistics + { + /* + * total number of operations submitted to the client that have not yet been completed. Unacked + * operations are a subset of this. + */ + uint64_t incompleteOperationCount; + + /* + * total packet size of operations submitted to the client that have not yet been completed. Unacked + * operations are a subset of this. + */ + uint64_t incompleteOperationSize; + + /* + * total number of operations that have been sent to the server and are waiting for a corresponding ACK + * before they can be completed. + */ + uint64_t unackedOperationCount; + + /* + * total packet size of operations that have been sent to the server and are waiting for a corresponding + * ACK before they can be completed. + */ + uint64_t unackedOperationSize; + }; + + /** + * The data returned when AttemptingConnect is invoked in the LifecycleEvents callback. + * Currently empty, but may be used in the future for passing additional data. + */ + struct AWS_CRT_CPP_API OnAttemptingConnectEventData + { + OnAttemptingConnectEventData() {} + }; + + /** + * The data returned when OnConnectionFailure is invoked in the LifecycleEvents callback. + */ + struct AWS_CRT_CPP_API OnConnectionFailureEventData + { + OnConnectionFailureEventData() : errorCode(AWS_ERROR_SUCCESS), connAckPacket(nullptr) {} + + int errorCode; + std::shared_ptr<ConnAckPacket> connAckPacket; + }; + + /** + * The data returned when OnConnectionSuccess is invoked in the LifecycleEvents callback. + */ + struct AWS_CRT_CPP_API OnConnectionSuccessEventData + { + OnConnectionSuccessEventData() : connAckPacket(nullptr), negotiatedSettings(nullptr) {} + + std::shared_ptr<ConnAckPacket> connAckPacket; + std::shared_ptr<NegotiatedSettings> negotiatedSettings; + }; + + /** + * The data returned when OnDisconnect is invoked in the LifecycleEvents callback. + */ + struct AWS_CRT_CPP_API OnDisconnectionEventData + { + OnDisconnectionEventData() : errorCode(AWS_ERROR_SUCCESS), disconnectPacket(nullptr) {} + + int errorCode; + std::shared_ptr<DisconnectPacket> disconnectPacket; + }; + + /** + * The data returned when OnStopped is invoked in the LifecycleEvents callback. + * Currently empty, but may be used in the future for passing additional data. + */ + struct AWS_CRT_CPP_API OnStoppedEventData + { + OnStoppedEventData() {} + }; + + /** + * The data returned when a publish is made to a topic the MQTT5 client is subscribed to. + */ + struct AWS_CRT_CPP_API PublishReceivedEventData + { + PublishReceivedEventData() : publishPacket(nullptr) {} + std::shared_ptr<PublishPacket> publishPacket; + }; + + /** + * Type signature of the callback invoked when connection succeed + * Mandatory event fields: client, connack_data, settings + */ + using OnConnectionSuccessHandler = std::function<void(Mqtt5Client &, const OnConnectionSuccessEventData &)>; + + /** + * Type signature of the callback invoked when connection failed + */ + using OnConnectionFailureHandler = std::function<void(Mqtt5Client &, const OnConnectionFailureEventData &)>; + + /** + * Type signature of the callback invoked when the internal connection is shutdown + */ + using OnDisconnectionHandler = std::function<void(Mqtt5Client &, const OnDisconnectionEventData &)>; + + /** + * Type signature of the callback invoked when attempting connect to client + * Mandatory event fields: client + */ + using OnAttemptingConnectHandler = std::function<void(Mqtt5Client &, const OnAttemptingConnectEventData &)>; + + /** + * Type signature of the callback invoked when client connection stopped + * Mandatory event fields: client + */ + using OnStoppedHandler = std::function<void(Mqtt5Client &, const OnStoppedEventData &)>; + + /** + * Type signature of the callback invoked when a Disconnection Comlete + * + */ + using OnDisconnectCompletionHandler = std::function<void(std::shared_ptr<Mqtt5Client>, int)>; + + /** + * Type signature of the callback invoked when a Publish Complete + */ + using OnPublishCompletionHandler = + std::function<void(std::shared_ptr<Mqtt5Client>, int, std::shared_ptr<PublishResult>)>; + + /** + * Type signature of the callback invoked when a Subscribe Complete + */ + using OnSubscribeCompletionHandler = + std::function<void(std::shared_ptr<Mqtt5Client>, int, std::shared_ptr<SubAckPacket>)>; + + /** + * Type signature of the callback invoked when a Unsubscribe Complete + */ + using OnUnsubscribeCompletionHandler = + std::function<void(std::shared_ptr<Mqtt5Client>, int, std::shared_ptr<UnSubAckPacket>)>; + + /** + * Type signature of the callback invoked when a PacketPublish message received (OnMessageHandler) + */ + using OnPublishReceivedHandler = std::function<void(Mqtt5Client &, const PublishReceivedEventData &)>; + + /** + * Callback for users to invoke upon completion of, presumably asynchronous, OnWebSocketHandshakeIntercept + * callback's initiated process. + */ + using OnWebSocketHandshakeInterceptComplete = + std::function<void(const std::shared_ptr<Http::HttpRequest> &, int)>; + + /** + * Invoked during websocket handshake to give users opportunity to transform an http request for purposes + * such as signing/authorization etc... Returning from this function does not continue the websocket + * handshake since some work flows may be asynchronous. To accommodate that, onComplete must be invoked upon + * completion of the signing process. + */ + using OnWebSocketHandshakeIntercept = + std::function<void(std::shared_ptr<Http::HttpRequest>, const OnWebSocketHandshakeInterceptComplete &)>; + + /** + * An MQTT5 client. This is a move-only type. Unless otherwise specified, + * all function arguments need only to live through the duration of the + * function call. + */ + class AWS_CRT_CPP_API Mqtt5Client final : public std::enable_shared_from_this<Mqtt5Client> + { + public: + /** + * Factory function for mqtt5 client + * + * @param options: Mqtt5 Client Options + * @param allocator allocator to use + * @return a new mqtt5 client + */ + static std::shared_ptr<Mqtt5Client> NewMqtt5Client( + const Mqtt5ClientOptions &options, + Allocator *allocator = ApiAllocator()) noexcept; + + /** + * Get shared poitner of the Mqtt5Client. Mqtt5Client is inherited to enable_shared_from_this to help + * with memory safety. + * + * @return shared_ptr for the Mqtt5Client + */ + std::shared_ptr<Mqtt5Client> getptr() { return shared_from_this(); } + + /** + * @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; + + /** + * Notifies the MQTT5 client that you want it to attempt to connect to the configured endpoint. + * The client will attempt to stay connected using the properties of the reconnect-related parameters + * from the client configuration. + * + * @return bool: true if operation succeed, otherwise false. + */ + bool Start() const noexcept; + + /** + * Notifies the MQTT5 client that you want it to transition to the stopped state, disconnecting any + * existing connection and stopping subsequent reconnect attempts. + * + * @return bool: true if operation succeed, otherwise false + */ + bool Stop() noexcept; + + /** + * Notifies the MQTT5 client that you want it to transition to the stopped state, disconnecting any + * existing connection and stopping subsequent reconnect attempts. + * + * @param disconnectOptions (optional) properties of a DISCONNECT packet to send as part of the shutdown + * process + * + * @return bool: true if operation succeed, otherwise false + */ + bool Stop(std::shared_ptr<DisconnectPacket> disconnectOptions) noexcept; + + /** + * Tells the client to attempt to send a PUBLISH packet + * + * @param publishOptions: packet PUBLISH to send to the server + * @param onPublishCompletionCallback: callback on publish complete, default to NULL + * + * @return true if the publish operation succeed otherwise false + */ + bool Publish( + std::shared_ptr<PublishPacket> publishOptions, + OnPublishCompletionHandler onPublishCompletionCallback = NULL) noexcept; + + /** + * Tells the client to attempt to subscribe to one or more topic filters. + * + * @param subscribeOptions: SUBSCRIBE packet to send to the server + * @param onSubscribeCompletionCallback: callback on subscribe complete, default to NULL + * + * @return true if the subscription operation succeed otherwise false + */ + bool Subscribe( + std::shared_ptr<SubscribePacket> subscribeOptions, + OnSubscribeCompletionHandler onSubscribeCompletionCallback = NULL) noexcept; + + /** + * Tells the client to attempt to unsubscribe to one or more topic filters. + * + * @param unsubscribeOptions: UNSUBSCRIBE packet to send to the server + * @param onUnsubscribeCompletionCallback: callback on unsubscribe complete, default to NULL + * + * @return true if the unsubscription operation succeed otherwise false + */ + bool Unsubscribe( + std::shared_ptr<UnsubscribePacket> unsubscribeOptions, + OnUnsubscribeCompletionHandler onUnsubscribeCompletionCallback = NULL) noexcept; + + /** + * Get the statistics about the current state of the client's queue of operations + * + * @return Mqtt5ClientOperationStatistics + */ + const Mqtt5ClientOperationStatistics &GetOperationStatistics() noexcept; + + virtual ~Mqtt5Client(); + + private: + Mqtt5Client(const Mqtt5ClientOptions &options, Allocator *allocator = ApiAllocator()) noexcept; + + /* Static Callbacks */ + static void s_publishCompletionCallback( + enum aws_mqtt5_packet_type packet_type, + const void *packet, + int error_code, + void *complete_ctx); + + static void s_subscribeCompletionCallback( + const struct aws_mqtt5_packet_suback_view *puback, + int error_code, + void *complete_ctx); + + static void s_unsubscribeCompletionCallback( + const struct aws_mqtt5_packet_unsuback_view *puback, + int error_code, + void *complete_ctx); + + static void s_lifeCycleEventCallback(const aws_mqtt5_client_lifecycle_event *event); + + static void s_publishReceivedCallback(const aws_mqtt5_packet_publish_view *publish, void *user_data); + + static void s_onWebsocketHandshake( + aws_http_message *rawRequest, + void *user_data, + aws_mqtt5_transform_websocket_handshake_complete_fn *complete_fn, + void *complete_ctx); + + static void s_clientTerminationCompletion(void *complete_ctx); + + /* The handler is set by clientoptions */ + OnWebSocketHandshakeIntercept websocketInterceptor; + /** + * Callback handler trigged when client successfully establishes an MQTT connection + */ + OnConnectionSuccessHandler onConnectionSuccess; + + /** + * Callback handler trigged when client fails to establish an MQTT connection + */ + OnConnectionFailureHandler onConnectionFailure; + + /** + * Callback handler trigged when client's current MQTT connection is closed + */ + OnDisconnectionHandler onDisconnection; + + /** + * Callback handler trigged when client reaches the "Stopped" state + */ + OnStoppedHandler onStopped; + + /** + * Callback handler trigged when client begins an attempt to connect to the remote endpoint. + */ + OnAttemptingConnectHandler onAttemptingConnect; + + /** + * Callback handler trigged when an MQTT PUBLISH packet is received by the client + */ + OnPublishReceivedHandler onPublishReceived; + aws_mqtt5_client *m_client; + Allocator *m_allocator; + + Mqtt5ClientOperationStatistics m_operationStatistics; + std::condition_variable m_terminationCondition; + std::mutex m_terminationMutex; + bool m_terminationPredicate = false; + }; + + /** + * Configuration interface for mqtt5 clients + */ + class AWS_CRT_CPP_API Mqtt5ClientOptions final + { + + friend class Mqtt5Client; + + public: + /** + * Default constructior of Mqtt5ClientOptions + */ + Mqtt5ClientOptions(Crt::Allocator *allocator = ApiAllocator()) noexcept; + + /** + * Sets host to connect to. + * + * @param hostname endpoint to connect to + * + * @return this option object + */ + Mqtt5ClientOptions &withHostName(Crt::String hostname); + + /** + * Set port to connect to + * + * @param port port to connect to + * + * @return this option object + */ + Mqtt5ClientOptions &withPort(uint16_t port) noexcept; + + /** + * Set booststrap for mqtt5 client + * + * @param bootStrap bootstrap used for mqtt5 client. The default ClientBootstrap see + * Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap. + * + * @return this option object + */ + Mqtt5ClientOptions &withBootstrap(Io::ClientBootstrap *bootStrap) noexcept; + + /** + * Sets the aws socket options + * + * @param socketOptions Io::SocketOptions used to setup socket + * + * @return this option object + */ + Mqtt5ClientOptions &withSocketOptions(Io::SocketOptions socketOptions) noexcept; + + /** + * Sets the tls connection options + * + * @param tslOptions Io::TlsConnectionOptions + * + * @return this option object + */ + Mqtt5ClientOptions &withTlsConnectionOptions(const Io::TlsConnectionOptions &tslOptions) noexcept; + + /** + * Sets http proxy options. + * + * @param proxyOptions http proxy configuration for connection establishment + * + * @return this option object + */ + Mqtt5ClientOptions &withHttpProxyOptions( + const Crt::Http::HttpClientConnectionProxyOptions &proxyOptions) noexcept; + + /** + * Sets mqtt5 connection options + * + * @param packetConnect package connection options + * + * @return this option object + */ + Mqtt5ClientOptions &withConnectOptions(std::shared_ptr<ConnectPacket> packetConnect) noexcept; + + /** + * Sets session behavior. Overrides how the MQTT5 client should behave with respect to MQTT sessions. + * + * @param sessionBehavior + * + * @return this option object + */ + Mqtt5ClientOptions &withSessionBehavior(ClientSessionBehaviorType sessionBehavior) noexcept; + + /** + * Sets client extended validation and flow control, additional controls for client behavior with + * respect to operation validation and flow control; these checks go beyond the base MQTT5 spec to + * respect limits of specific MQTT brokers. + * + * @param clientExtendedValidationAndFlowControl + * + * @return this option object + */ + Mqtt5ClientOptions &withClientExtendedValidationAndFlowControl( + ClientExtendedValidationAndFlowControl clientExtendedValidationAndFlowControl) noexcept; + + /** + * Sets OfflineQueueBehavior, controls how disconnects affect the queued and in-progress operations + * tracked by the client. Also controls how new operations are handled while the client is not + * connected. In particular, if the client is not connected, then any operation that would be failed + * on disconnect (according to these rules) will also be rejected. + * + * @param offlineQueueBehavior + * + * @return this option object + */ + Mqtt5ClientOptions &withOfflineQueueBehavior( + ClientOperationQueueBehaviorType offlineQueueBehavior) noexcept; + + /** + * Sets ReconnectOptions. Reconnect options, includes retryJitterMode, min reconnect delay time and + * max reconnect delay time and reset reconnect delay time + * + * @param reconnectOptions + * + * @return this option object + */ + Mqtt5ClientOptions &withReconnectOptions(ReconnectOptions reconnectOptions) noexcept; + + /** + * Sets ping timeout (ms). Time interval to wait after sending a PINGREQ for a PINGRESP to arrive. + * If one does not arrive, the client will close the current connection. + * + * @param pingTimeoutMs + * + * @return this option object + */ + Mqtt5ClientOptions &withPingTimeoutMs(uint32_t pingTimeoutMs) noexcept; + + /** + * Sets Connack Timeout (ms). Time interval to wait after sending a CONNECT request for a CONNACK + * to arrive. If one does not arrive, the connection will be shut down. + * + * @param connackTimeoutMs + * + * @return this option object + */ + Mqtt5ClientOptions &withConnackTimeoutMs(uint32_t connackTimeoutMs) noexcept; + + /** + * Sets Operation Timeout(Seconds). Time interval to wait for an ack after sending a QoS 1+ PUBLISH, + * SUBSCRIBE, or UNSUBSCRIBE before failing the operation. + * + * @param ackTimeoutSeconds + * + * @return this option object + */ + Mqtt5ClientOptions &withAckTimeoutSeconds(uint32_t ackTimeoutSeconds) noexcept; + + /** + * Sets callback for transform HTTP request. + * This callback allows a custom transformation of the HTTP request that acts as the websocket + * handshake. Websockets will be used if this is set to a valid transformation callback. To use + * websockets but not perform a transformation, just set this as a trivial completion callback. If + * undefined, the connection will be made with direct MQTT. + * + * @param callback + * + * @return this option object + */ + Mqtt5ClientOptions &withWebsocketHandshakeTransformCallback( + OnWebSocketHandshakeIntercept callback) noexcept; + + /** + * Sets callback trigged when client successfully establishes an MQTT connection + * + * @param callback + * + * @return this option object + */ + Mqtt5ClientOptions &withClientConnectionSuccessCallback(OnConnectionSuccessHandler callback) noexcept; + + /** + * Sets callback trigged when client fails to establish an MQTT connection + * + * @param callback + * + * @return this option object + */ + Mqtt5ClientOptions &withClientConnectionFailureCallback(OnConnectionFailureHandler callback) noexcept; + + /** + * Sets callback trigged when client's current MQTT connection is closed + * + * @param callback + * + * @return this option object + */ + Mqtt5ClientOptions &withClientDisconnectionCallback(OnDisconnectionHandler callback) noexcept; + + /** + * Sets callback trigged when client reaches the "Stopped" state + * + * @param callback + * + * @return this option object + */ + Mqtt5ClientOptions &withClientStoppedCallback(OnStoppedHandler callback) noexcept; + + /** + * Sets callback trigged when client begins an attempt to connect to the remote endpoint. + * + * @param callback + * + * @return this option object + */ + Mqtt5ClientOptions &withClientAttemptingConnectCallback(OnAttemptingConnectHandler callback) noexcept; + + /** + * Sets callback trigged when a PUBLISH packet is received by the client + * + * @param callback + * + * @return this option object + */ + Mqtt5ClientOptions &withPublishReceivedCallback(OnPublishReceivedHandler callback) noexcept; + + /** + * Initializes the C aws_mqtt5_client_options from Mqtt5ClientOptions. For internal use + * + * @param raw_options - output parameter containing low level client options to be passed to the C + * interface + * + */ + bool initializeRawOptions(aws_mqtt5_client_options &raw_options) const noexcept; + + virtual ~Mqtt5ClientOptions(); + Mqtt5ClientOptions(const Mqtt5ClientOptions &) = delete; + Mqtt5ClientOptions(Mqtt5ClientOptions &&) = delete; + Mqtt5ClientOptions &operator=(const Mqtt5ClientOptions &) = delete; + Mqtt5ClientOptions &operator=(Mqtt5ClientOptions &&) = delete; + + private: + /** + * This callback allows a custom transformation of the HTTP request that acts as the websocket + * handshake. Websockets will be used if this is set to a valid transformation callback. To use + * websockets but not perform a transformation, just set this as a trivial completion callback. If + * undefined, the connection will be made with direct MQTT. + */ + OnWebSocketHandshakeIntercept websocketHandshakeTransform; + + /** + * Callback handler trigged when client successfully establishes an MQTT connection + */ + OnConnectionSuccessHandler onConnectionSuccess; + + /** + * Callback handler trigged when client fails to establish an MQTT connection + */ + OnConnectionFailureHandler onConnectionFailure; + + /** + * Callback handler trigged when client's current MQTT connection is closed + */ + OnDisconnectionHandler onDisconnection; + + /** + * Callback handler trigged when client reaches the "Stopped" state + * + * @param Mqtt5Client: The shared client + */ + OnStoppedHandler onStopped; + + /** + * Callback handler trigged when client begins an attempt to connect to the remote endpoint. + * + * @param Mqtt5Client: The shared client + */ + OnAttemptingConnectHandler onAttemptingConnect; + + /** + * Callback handler trigged when an MQTT PUBLISH packet is received by the client + * + * @param Mqtt5Client: The shared client + * @param PublishPacket: received Publish Packet + */ + OnPublishReceivedHandler onPublishReceived; + + /** + * Host name of the MQTT server to connect to. + */ + Crt::String m_hostName; + + /** + * Network port of the MQTT server to connect to. + */ + uint16_t m_port; + + /** + * Client bootstrap to use. In almost all cases, this can be left undefined. + */ + Io::ClientBootstrap *m_bootstrap; + + /** + * Controls socket properties of the underlying MQTT connections made by the client. Leave undefined to + * use defaults (no TCP keep alive, 10 second socket timeout). + */ + Crt::Io::SocketOptions m_socketOptions; + + /** + * TLS context for secure socket connections. + * If undefined, then a plaintext connection will be used. + */ + Crt::Optional<Crt::Io::TlsConnectionOptions> m_tlsConnectionOptions; + + /** + * Configures (tunneling) HTTP proxy usage when establishing MQTT connections + */ + Crt::Optional<Crt::Http::HttpClientConnectionProxyOptions> m_proxyOptions; + + /** + * All configurable options with respect to the CONNECT packet sent by the client, including the will. + * These connect properties will be used for every connection attempt made by the client. + */ + std::shared_ptr<ConnectPacket> m_connectOptions; + + /** + * Controls how the MQTT5 client should behave with respect to MQTT sessions. + */ + ClientSessionBehaviorType m_sessionBehavior; + + /** + * Additional controls for client behavior with respect to operation validation and flow control; these + * checks go beyond the base MQTT5 spec to respect limits of specific MQTT brokers. + */ + ClientExtendedValidationAndFlowControl m_extendedValidationAndFlowControlOptions; + + /** + * Controls how disconnects affect the queued and in-progress operations tracked by the client. Also + * controls how new operations are handled while the client is not connected. In particular, if the + * client is not connected, then any operation that would be failed on disconnect (according to these + * rules) will also be rejected. + */ + ClientOperationQueueBehaviorType m_offlineQueueBehavior; + + /** + * Reconnect options, includes retryJitterMode, min reconnect delay time and max reconnect delay time + */ + ReconnectOptions m_reconnectionOptions; + + /** + * Time interval to wait after sending a PINGREQ for a PINGRESP to arrive. If one does not arrive, the + * client will close the current connection. + */ + uint32_t m_pingTimeoutMs; + + /** + * Time interval to wait after sending a CONNECT request for a CONNACK to arrive. If one does not + * arrive, the connection will be shut down. + */ + uint32_t m_connackTimeoutMs; + + /** + * Time interval to wait for an ack after sending a QoS 1+ PUBLISH, SUBSCRIBE, or UNSUBSCRIBE before + * failing the operation. + */ + uint32_t m_ackTimeoutSec; + + /* Underlying Parameters */ + Crt::Allocator *m_allocator; + aws_http_proxy_options m_httpProxyOptionsStorage; + aws_mqtt5_packet_connect_view m_packetConnectViewStorage; + }; + + } // namespace Mqtt5 + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/mqtt/Mqtt5Packets.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/mqtt/Mqtt5Packets.h new file mode 100644 index 0000000000..9588d6e0ef --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/mqtt/Mqtt5Packets.h @@ -0,0 +1,2286 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/crt/mqtt/Mqtt5Client.h> +#include <aws/crt/mqtt/Mqtt5Packets.h> +#include <aws/crt/mqtt/Mqtt5Types.h> + +namespace Aws +{ + namespace Crt + { + namespace Mqtt5 + { + + /** + * Data model for MQTT5 user properties. + * + * A user property is a name-value pair of utf-8 strings that can be added to mqtt5 packets. + */ + class AWS_CRT_CPP_API UserProperty + { + public: + UserProperty(Crt::String key, Crt::String value) noexcept; + + const Crt::String &getName() const noexcept { return m_name; }; + const Crt::String &getValue() const noexcept { return m_value; } + + ~UserProperty() noexcept; + UserProperty(const UserProperty &toCopy) noexcept; + UserProperty(UserProperty &&toMove) noexcept; + UserProperty &operator=(const UserProperty &toCopy) noexcept; + UserProperty &operator=(UserProperty &&toMove) noexcept; + + private: + Crt::String m_name; + Crt::String m_value; + }; + + class AWS_CRT_CPP_API IPacket + { + public: + virtual PacketType getType() = 0; + }; + + /** + * Data model of an [MQTT5 + * PUBLISH](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901100) packet + */ + class AWS_CRT_CPP_API PublishPacket : public IPacket + { + public: + PublishPacket( + const aws_mqtt5_packet_publish_view &raw_options, + Allocator *allocator = ApiAllocator()) noexcept; + PublishPacket(Allocator *allocator = ApiAllocator()) noexcept; + PublishPacket( + Crt::String topic, + ByteCursor payload, + Mqtt5::QOS qos, + Allocator *allocator = ApiAllocator()) noexcept; + PacketType getType() override { return PacketType::AWS_MQTT5_PT_PUBLISH; }; + + /** + * Sets the payload for the publish message. + * + * See [MQTT5 Publish + * Payload](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901119) + * + * @param payload The payload for the publish message. + * @return The PublishPacket Object after setting the payload. + */ + PublishPacket &withPayload(ByteCursor payload) noexcept; + + /** + * Sets the MQTT quality of service level the message should be delivered with. + * + * See [MQTT5 QoS](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901103) + * + * @param packetQOS The MQTT quality of service level the message should be delivered with. + * @return The PublishPacket Object after setting the QOS. + */ + PublishPacket &withQOS(Mqtt5::QOS packetQOS) noexcept; + + /** + * Sets if this should be a retained message. + * + * See [MQTT5 Retain](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901104) + * + * @param retain if this is a retained message. + * @return The PublishPacket Object after setting the retain setting. + */ + PublishPacket &withRetain(bool retain) noexcept; + + /** + * Sets the topic this message should be published to. + * See [MQTT5 Topic Name](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901107) + * + * @param topic The topic this message should be published to. + * @return The PublishPacket Object after setting the topic. + */ + PublishPacket &withTopic(Crt::String topic) noexcept; + + /** + * Sets the property specifying the format of the payload data. The mqtt5 client does not enforce or use + * this value in a meaningful way. + * + * See [MQTT5 Payload Format + * Indicator](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901111) + * + * @param payloadFormat Property specifying the format of the payload data + * @return The PublishPacket Object after setting the payload format. + */ + PublishPacket &withPayloadFormatIndicator(PayloadFormatIndicator payloadFormat) noexcept; + + /** + * Sets the maximum amount of time allowed to elapse for message delivery before the server + * should instead delete the message (relative to a recipient). + * + * See [MQTT5 Message Expiry + * Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901112) + * + * @param second The maximum amount of time allowed to elapse for message delivery + * before the server should instead delete the message (relative to a recipient). + * @return The PublishPacket Object after setting the message expiry interval. + */ + PublishPacket &withMessageExpiryIntervalSec(uint32_t second) noexcept; + + /** + * Sets the opaque topic string intended to assist with request/response implementations. Not + * internally meaningful to MQTT5 or this client. + * + * See [MQTT5 Response + * Topic](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901114) + * @param responseTopic + * @return The PublishPacket Object after setting the response topic. + */ + PublishPacket &withResponseTopic(ByteCursor responseTopic) noexcept; + + /** + * Sets the opaque binary data used to correlate between publish messages, as a potential method for + * request-response implementation. Not internally meaningful to MQTT5. + * + * See [MQTT5 Correlation + * Data](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901115) + * + * @param correlationData Opaque binary data used to correlate between publish messages + * @return The PublishPacket Object after setting the correlation data. + */ + PublishPacket &withCorrelationData(ByteCursor correlationData) noexcept; + + /** + * Sets the list of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116) + * + * @param userProperties List of MQTT5 user properties included with the packet. + * @return The PublishPacket Object after setting the user properties + */ + PublishPacket &withUserProperties(const Vector<UserProperty> &userProperties) noexcept; + + /** + * Sets the list of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116) + * + * @param userProperties List of MQTT5 user properties included with the packet. + * @return The PublishPacket Object after setting the user properties + */ + PublishPacket &withUserProperties(Vector<UserProperty> &&userProperties) noexcept; + + /** + * Put a MQTT5 user property to the back of the packet user property vector/list + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116) + * + * @param property set of userProperty of MQTT5 user properties included with the packet. + * @return The PublishPacket Object after setting the user property + */ + PublishPacket &withUserProperty(UserProperty &&property) noexcept; + + bool initializeRawOptions(aws_mqtt5_packet_publish_view &raw_options) noexcept; + + /** + * The payload of the publish message. + * + * See [MQTT5 Publish + * Payload](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901119) + * + * @return The payload of the publish message. + */ + const ByteCursor &getPayload() const noexcept; + + /** + * Sent publishes - The MQTT quality of service level this message should be delivered with. + * + * Received publishes - The MQTT quality of service level this message was delivered at. + * + * See [MQTT5 QoS](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901103) + * + * @return The MQTT quality of service associated with this PUBLISH packet. + */ + Mqtt5::QOS getQOS() const noexcept; + + /** + * True if this is a retained message, false otherwise. + * + * Always set on received publishes. + * + * See [MQTT5 Retain](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901104) + * + * @return True if this is a retained message, false otherwise. + */ + bool getRetain() const noexcept; + + /** + * Sent publishes - The topic this message should be published to. + * + * Received publishes - The topic this message was published to. + * + * See [MQTT5 Topic Name](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901107) + * @return The topic associated with this PUBLISH packet. + */ + const Crt::String &getTopic() const noexcept; + + /** + * Property specifying the format of the payload data. The mqtt5 client does not enforce or use this + * value in a meaningful way. + * + * See [MQTT5 Payload Format + * Indicator](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901111) + * + * @return Property specifying the format of the payload data. + */ + const Crt::Optional<PayloadFormatIndicator> &getPayloadFormatIndicator() const noexcept; + + /** + * Sent publishes - indicates the maximum amount of time allowed to elapse for message delivery before + * the server should instead delete the message (relative to a recipient). + * + * Received publishes - indicates the remaining amount of time (from the server's perspective) before + * the message would have been deleted relative to the subscribing client. + * + * If left null, indicates no expiration timeout. + * + * See [MQTT5 Message Expiry + * Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901112) + * + * @return The message expiry interval associated with this PUBLISH packet. + */ + const Crt::Optional<uint32_t> &getMessageExpiryIntervalSec() const noexcept; + + /** + * Opaque topic string intended to assist with request/response implementations. Not internally + * meaningful to MQTT5 or this client. + * + * See [MQTT5 Response + * Topic](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901114) + * + * @return ByteCursor to topic string intended to assist with request/response implementations. + */ + const Crt::Optional<ByteCursor> &getResponseTopic() const noexcept; + + /** + * Opaque binary data used to correlate between publish messages, as a potential method for + * request-response implementation. Not internally meaningful to MQTT5. + * + * See [MQTT5 Correlation + * Data](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901115) + * + * @return ByteCursor to opaque binary data used to correlate between publish messages. + */ + const Crt::Optional<ByteCursor> &getCorrelationData() const noexcept; + + /** + * Sent publishes - ignored + * + * Received publishes - the subscription identifiers of all the subscriptions this message matched. + * + * See [MQTT5 Subscription + * Identifier](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901117) + * + * @return the subscription identifiers of all the subscriptions this message matched. + */ + const Crt::Vector<uint32_t> &getSubscriptionIdentifiers() const noexcept; + + /** + * Property specifying the content type of the payload. Not internally meaningful to MQTT5. + * + * See [MQTT5 Content Type](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901118) + * + * @return ByteCursor to opaque binary data to the content type of the payload. + */ + const Crt::Optional<ByteCursor> &getContentType() const noexcept; + + /** + * List of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116) + * + * @return List of MQTT5 user properties included with the packet. + */ + const Crt::Vector<UserProperty> &getUserProperties() const noexcept; + + virtual ~PublishPacket(); + PublishPacket(const PublishPacket &) = delete; + PublishPacket(PublishPacket &&) noexcept = delete; + PublishPacket &operator=(const PublishPacket &) = delete; + PublishPacket &operator=(PublishPacket &&) noexcept = delete; + + private: + Allocator *m_allocator; + + /** + * The payload of the publish message. + * + * See [MQTT5 Publish + * Payload](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901119) + */ + ByteCursor m_payload; + + /** + * Sent publishes - The MQTT quality of service level this message should be delivered with. + * + * Received publishes - The MQTT quality of service level this message was delivered at. + * + * See [MQTT5 QoS](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901103) + */ + Mqtt5::QOS m_qos; + + /** + * True if this is a retained message, false otherwise. + * + * Always set on received publishes, default to false + * + * See [MQTT5 Retain](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901104) + */ + bool m_retain; + + /** + * Sent publishes - The topic this message should be published to. + * + * Received publishes - The topic this message was published to. + * + * See [MQTT5 Topic Name](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901107) + */ + Crt::String m_topicName; + + /** + * Property specifying the format of the payload data. The mqtt5 client does not enforce or use this + * value in a meaningful way. + * + * See [MQTT5 Payload Format + * Indicator](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901111) + */ + Crt::Optional<PayloadFormatIndicator> m_payloadFormatIndicator; + + /** + * Sent publishes - indicates the maximum amount of time allowed to elapse for message delivery before + * the server should instead delete the message (relative to a recipient). + * + * Received publishes - indicates the remaining amount of time (from the server's perspective) before + * the message would have been deleted relative to the subscribing client. + * + * If left undefined, indicates no expiration timeout. + * + * See [MQTT5 Message Expiry + * Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901112) + */ + Crt::Optional<uint32_t> m_messageExpiryIntervalSec; + + /** + * Opaque topic string intended to assist with request/response implementations. Not internally + * meaningful to MQTT5 or this client. + * + * See [MQTT5 Response + * Topic](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901114) + */ + Crt::Optional<ByteCursor> m_responseTopic; + + /** + * Opaque binary data used to correlate between publish messages, as a potential method for + * request-response implementation. Not internally meaningful to MQTT5. + * + * See [MQTT5 Correlation + * Data](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901115) + */ + Crt::Optional<ByteCursor> m_correlationData; + + /** + * Set of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116) + */ + Crt::Vector<UserProperty> m_userProperties; + + /////////////////////////////////////////////////////////////////////////// + // The following parameters are ignored when building publish operations */ + /////////////////////////////////////////////////////////////////////////// + + /** + * Sent publishes - ignored + * + * Received publishes - the subscription identifiers of all the subscriptions this message matched. + * + * See [MQTT5 Subscription + * Identifier](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901117) + */ + Crt::Vector<uint32_t> m_subscriptionIdentifiers; + + /** + * Property specifying the content type of the payload. Not internally meaningful to MQTT5. + * + * See [MQTT5 Content Type](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901118) + */ + Crt::Optional<ByteCursor> m_contentType; + + /////////////////////////////////////////////////////////////////////////// + // Underlying data storage for internal use + /////////////////////////////////////////////////////////////////////////// + ByteBuf m_payloadStorage; + ByteBuf m_contentTypeStorage; + ByteBuf m_correlationDataStorage; + Crt::String m_responseTopicString; + struct aws_mqtt5_user_property *m_userPropertiesStorage; + }; + + /** + * Mqtt behavior settings that are dynamically negotiated as part of the CONNECT/CONNACK exchange. + * + * While you can infer all of these values from a combination of + * (1) defaults as specified in the mqtt5 spec + * (2) your CONNECT settings + * (3) the CONNACK from the broker + * + * the client instead does the combining for you and emits a NegotiatedSettings object with final, + * authoritative values. + * + * Negotiated settings are communicated with every successful connection establishment. + */ + class AWS_CRT_CPP_API NegotiatedSettings + { + public: + NegotiatedSettings( + const aws_mqtt5_negotiated_settings &negotiated_settings, + + Allocator *allocator = ApiAllocator()) noexcept; + + /** + * @return The maximum QoS allowed for publishes on this connection instance + */ + Mqtt5::QOS getMaximumQOS() const noexcept; + + /** + * @return The amount of time in seconds the server will retain the MQTT session after a disconnect. + */ + uint32_t getSessionExpiryIntervalSec() const noexcept; + + /** + * @return The number of in-flight QoS 1 and QoS 2 publications the server is willing to process + * concurrently. + */ + uint16_t getReceiveMaximumFromServer() const noexcept; + + /** + * @return The maximum packet size the server is willing to accept. + */ + uint32_t getMaximumPacketSizeBytes() const noexcept; + + /** + * The maximum amount of time in seconds between client packets. The client should use PINGREQs to + * ensure this limit is not breached. The server will disconnect the client for inactivity if no MQTT + * packet is received in a time interval equal to 1.5 x this value. + * + * @return The maximum amount of time in seconds between client packets. + */ + uint16_t getServerKeepAlive() const noexcept; + + /** + * @return Whether the server supports retained messages. + */ + bool getRetainAvailable() const noexcept; + + /** + * @return Whether the server supports wildcard subscriptions. + */ + bool getWildcardSubscriptionsAvaliable() const noexcept; + + /** + * @return Whether the server supports subscription identifiers + */ + bool getSubscriptionIdentifiersAvaliable() const noexcept; + + /** + * @return Whether the server supports shared subscriptions + */ + bool getSharedSubscriptionsAvaliable() const noexcept; + + /** + * @return Whether the client has rejoined an existing session. + */ + bool getRejoinedSession() const noexcept; + + /** + * The final client id in use by the newly-established connection. This will be the configured client + * id if one was given in the configuration, otherwise, if no client id was specified, this will be the + * client id assigned by the server. Reconnection attempts will always use the auto-assigned client id, + * allowing for auto-assigned session resumption. + * + * @return The final client id in use by the newly-established connection + */ + const Crt::String &getClientId() const noexcept; + + virtual ~NegotiatedSettings(){}; + NegotiatedSettings(const NegotiatedSettings &) = delete; + NegotiatedSettings(NegotiatedSettings &&) noexcept = delete; + NegotiatedSettings &operator=(const NegotiatedSettings &) = delete; + NegotiatedSettings &operator=(NegotiatedSettings &&) noexcept = delete; + + private: + /** + * The maximum QoS allowed for publishes on this connection instance + */ + Mqtt5::QOS m_maximumQOS; + + /** + * The amount of time in seconds the server will retain the MQTT session after a disconnect. + */ + uint32_t m_sessionExpiryIntervalSec; + + /** + * The number of in-flight QoS 1 and QoS2 publications the server is willing to process concurrently. + */ + uint16_t m_receiveMaximumFromServer; + + /** + * The maximum packet size the server is willing to accept. + */ + uint32_t m_maximumPacketSizeBytes; + + /** + * The maximum amount of time in seconds between client packets. The client should use PINGREQs to + * ensure this limit is not breached. The server will disconnect the client for inactivity if no MQTT + * packet is received in a time interval equal to 1.5 x this value. + */ + uint16_t m_serverKeepAliveSec; + + /** + * Whether the server supports retained messages. + */ + bool m_retainAvailable; + + /** + * Whether the server supports wildcard subscriptions. + */ + bool m_wildcardSubscriptionsAvaliable; + + /** + * Whether the server supports subscription identifiers + */ + bool m_subscriptionIdentifiersAvaliable; + + /** + * Whether the server supports shared subscriptions + */ + bool m_sharedSubscriptionsAvaliable; + + /** + * Whether the client has rejoined an existing session. + */ + bool m_rejoinedSession; + + /** + * The final client id in use by the newly-established connection. This will be the configured client + * id if one was given in the configuration, otherwise, if no client id was specified, this will be the + * client id assigned by the server. Reconnection attempts will always use the auto-assigned client id, + * allowing for auto-assigned session resumption. + */ + Crt::String m_clientId; + }; + + /** + * Data model of an [MQTT5 + * CONNECT](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901033) packet. + */ + class AWS_CRT_CPP_API ConnectPacket : public IPacket + { + public: + /* Default constructor */ + ConnectPacket(Allocator *allocator = ApiAllocator()) noexcept; + + /* The packet type */ + PacketType getType() override { return PacketType::AWS_MQTT5_PT_CONNECT; }; + + /** + * Sets the maximum time interval, in seconds, that is permitted to elapse between the point at which + * the client finishes transmitting one MQTT packet and the point it starts sending the next. The + * client will use PINGREQ packets to maintain this property. + * + * If the responding CONNACK contains a keep alive property value, then that is the negotiated keep + * alive value. Otherwise, the keep alive sent by the client is the negotiated value. + * + * See [MQTT5 Keep Alive](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901045) + * + * NOTE: The keepAliveIntervalSeconds HAS to be larger than the pingTimeoutMs time set in the + * Mqtt5ClientOptions. + * + * @param keepAliveInteralSeconds the maximum time interval, in seconds, that is permitted to elapse + * between the point at which the client finishes transmitting one MQTT packet and the point it starts + * sending the next. + * @return The ConnectPacket Object after setting the keep alive interval. + */ + ConnectPacket &withKeepAliveIntervalSec(uint16_t keepAliveInteralSeconds) noexcept; + + /** + * Sets the unique string identifying the client to the server. Used to restore session state between + * connections. + * + * If left empty, the broker will auto-assign a unique client id. When reconnecting, the mqtt5 client + * will always use the auto-assigned client id. + * + * See [MQTT5 Client + * Identifier](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901059) + * + * @param clientId A unique string identifying the client to the server. + * @return The ConnectPacket Object after setting the client ID. + */ + ConnectPacket &withClientId(Crt::String clientId) noexcept; + + /** + * Sets the string value that the server may use for client authentication and authorization. + * + * See [MQTT5 User Name](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901071) + * + * @param username The string value that the server may use for client authentication and authorization. + * @return The ConnectPacket Object after setting the username. + */ + ConnectPacket &withUserName(Crt::String username) noexcept; + + /** + * Sets the opaque binary data that the server may use for client authentication and authorization. + * + * See [MQTT5 Password](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901072) + * + * @param password Opaque binary data that the server may use for client authentication and + * authorization. + * @return The ConnectPacket Object after setting the password. + */ + ConnectPacket &withPassword(ByteCursor password) noexcept; + + /** + * Sets the time interval, in seconds, that the client requests the server to persist this connection's + * MQTT session state for. Has no meaning if the client has not been configured to rejoin sessions. + * Must be non-zero in order to successfully rejoin a session. + * + * If the responding CONNACK contains a session expiry property value, then that is the negotiated + * session expiry value. Otherwise, the session expiry sent by the client is the negotiated value. + * + * See [MQTT5 Session Expiry + * Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901048) + * + * @param sessionExpiryIntervalSeconds A time interval, in seconds, that the client requests the server + * to persist this connection's MQTT session state for. + * @return The ConnectPacket Object after setting the session expiry interval. + */ + ConnectPacket &withSessionExpiryIntervalSec(uint32_t sessionExpiryIntervalSeconds) noexcept; + + /** + * Sets whether requests that the server send response information in the subsequent CONNACK. This + * response information may be used to set up request-response implementations over MQTT, but doing so + * is outside the scope of the MQTT5 spec and client. + * + * See [MQTT5 Request Response + * Information](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901052) + * + * @param requestResponseInformation If true, requests that the server send response information in the + * subsequent CONNACK. + * @return The ConnectPacket Object after setting the request response information. + */ + ConnectPacket &withRequestResponseInformation(bool requestResponseInformation) noexcept; + + /** + * Sets whether requests that the server send additional diagnostic information (via response string or + * user properties) in DISCONNECT or CONNACK packets from the server. + * + * See [MQTT5 Request Problem + * Information](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901053) + * + * @param requestProblemInformation If true, requests that the server send additional diagnostic + * information (via response string or user properties) in DISCONNECT or CONNACK packets from the + * server. + * @return The ConnectPacket Object after setting the request problem information. + */ + ConnectPacket &withRequestProblemInformation(bool requestProblemInformation) noexcept; + + /** + * Sets the maximum number of in-flight QoS 1 and 2 messages the client is willing to handle. If + * omitted, then no limit is requested. + * + * See [MQTT5 Receive + * Maximum](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901049) + * + * @param receiveMaximum The maximum number of in-flight QoS 1 and 2 messages the client is willing to + * handle. + * @return The ConnectPacket Object after setting the receive maximum. + */ + ConnectPacket &withReceiveMaximum(uint16_t receiveMaximum) noexcept; + + /** + * Sets the maximum packet size the client is willing to handle. If + * omitted, then no limit beyond the natural limits of MQTT packet size is requested. + * + * See [MQTT5 Maximum Packet + * Size](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901050) + * + * @param maximumPacketSizeBytes The maximum packet size the client is willing to handle + * @return The ConnectPacket Object after setting the maximum packet size. + */ + ConnectPacket &withMaximumPacketSizeBytes(uint32_t maximumPacketSizeBytes) noexcept; + + /** + * Sets the time interval, in seconds, that the server should wait (for a session reconnection) before + * sending the will message associated with the connection's session. If omitted, the server + * will send the will when the associated session is destroyed. If the session is destroyed before a + * will delay interval has elapsed, then the will must be sent at the time of session destruction. + * + * See [MQTT5 Will Delay + * Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901062) + * + * @param willDelayIntervalSeconds A time interval, in seconds, that the server should wait (for a + * session reconnection) before sending the will message associated with the connection's session. + * @return The ConnectPacket Object after setting the will message delay interval. + */ + ConnectPacket &withWillDelayIntervalSec(uint32_t willDelayIntervalSeconds) noexcept; + + /** + * Sets the definition of a message to be published when the connection's session is destroyed by the + * server or when the will delay interval has elapsed, whichever comes first. If null, then nothing + * will be sent. + * + * See [MQTT5 Will](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901040) + * + * @param will The message to be published when the connection's session is destroyed by the server or + * when the will delay interval has elapsed, whichever comes first. + * @return The ConnectPacket Object after setting the will message. + */ + ConnectPacket &withWill(std::shared_ptr<PublishPacket> will) noexcept; + + /** + * Sets the list of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901054) + * + * @param userProperties List of MQTT5 user properties included with the packet. + * @return The ConnectPacket Object after setting the user properties. + */ + ConnectPacket &withUserProperties(const Vector<UserProperty> &userProperties) noexcept; + + /** + * Sets the list of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901054) + * + * @param userProperties List of MQTT5 user properties included with the packet. + * @return The ConnectPacket Object after setting the user properties. + */ + ConnectPacket &withUserProperties(Vector<UserProperty> &&userProperties) noexcept; + + /** + * Put a MQTT5 user property to the back of the packet user property vector/list + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116) + * + * @param property set of userProperty of MQTT5 user properties included with the packet. + * @return The ConnectPacket Object after setting the user property + */ + ConnectPacket &withUserProperty(UserProperty &&property) noexcept; + + /******************************************** + * Access Functions + ********************************************/ + + /** + * The maximum time interval, in seconds, that is permitted to elapse between the point at which the + * client finishes transmitting one MQTT packet and the point it starts sending the next. The client + * will use PINGREQ packets to maintain this property. + * + * If the responding CONNACK contains a keep alive property value, then that is the negotiated keep + * alive value. Otherwise, the keep alive sent by the client is the negotiated value. + * + * See [MQTT5 Keep Alive](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901045) + * + * @return The maximum time interval, in seconds, that is permitted to elapse between the point at which + * the client finishes transmitting one MQTT packet and the point it starts sending the next. + */ + uint16_t getKeepAliveIntervalSec() const noexcept; + + /** + * A unique string identifying the client to the server. Used to restore session state between + * connections. + * + * If left empty, the broker will auto-assign a unique client id. When reconnecting, the mqtt5 client + * will always use the auto-assigned client id. + * + * See [MQTT5 Client + * Identifier](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901059) + * + * @return A unique string identifying the client to the server. + */ + const Crt::String &getClientId() const noexcept; + + /** + * A string value that the server may use for client authentication and authorization. + * + * See [MQTT5 User Name](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901071) + * + * @return A string value that the server may use for client authentication and authorization. + */ + const Crt::Optional<Crt::String> &getUsername() const noexcept; + + /** + * Opaque binary data that the server may use for client authentication and authorization. + * + * See [MQTT5 Password](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901072) + * + * @return Opaque binary data that the server may use for client authentication and authorization. + */ + const Crt::Optional<Crt::ByteCursor> &getPassword() const noexcept; + + /** + * A time interval, in seconds, that the client requests the server to persist this connection's MQTT + * session state for. Has no meaning if the client has not been configured to rejoin sessions. Must be + * non-zero in order to successfully rejoin a session. + * + * If the responding CONNACK contains a session expiry property value, then that is the negotiated + * session expiry value. Otherwise, the session expiry sent by the client is the negotiated value. + * + * See [MQTT5 Session Expiry + * Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901048) + * + * @return A time interval, in seconds, that the client requests the server to persist this connection's + * MQTT session state for. + */ + const Crt::Optional<uint32_t> &getSessionExpiryIntervalSec() const noexcept; + + /** + * If true, requests that the server send response information in the subsequent CONNACK. This response + * information may be used to set up request-response implementations over MQTT, but doing so is outside + * the scope of the MQTT5 spec and client. + * + * See [MQTT5 Request Response + * Information](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901052) + * + * @return If true, requests that the server send response information in the subsequent CONNACK. + */ + const Crt::Optional<bool> &getRequestResponseInformation() const noexcept; + + /** + * If true, requests that the server send additional diagnostic information (via response string or + * user properties) in DISCONNECT or CONNACK packets from the server. + * + * See [MQTT5 Request Problem + * Information](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901053) + * + * @return If true, requests that the server send additional diagnostic information (via response string + * or user properties) in DISCONNECT or CONNACK packets from the server. + */ + const Crt::Optional<bool> &getRequestProblemInformation() const noexcept; + + /** + * Notifies the server of the maximum number of in-flight QoS 1 and 2 messages the client is willing to + * handle. If omitted or null, then no limit is requested. + * + * See [MQTT5 Receive + * Maximum](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901049) + * + * @return The maximum number of in-flight QoS 1 and 2 messages the client is willing to handle. + */ + const Crt::Optional<uint16_t> &getReceiveMaximum() const noexcept; + + /** + * Notifies the server of the maximum packet size the client is willing to handle. If + * omitted or null, then no limit beyond the natural limits of MQTT packet size is requested. + * + * See [MQTT5 Maximum Packet + * Size](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901050) + * + * @return The maximum packet size the client is willing to handle + */ + const Crt::Optional<uint32_t> &getMaximumPacketSizeBytes() const noexcept; + + /** + * A time interval, in seconds, that the server should wait (for a session reconnection) before sending + * the will message associated with the connection's session. If omitted or null, the server will send + * the will when the associated session is destroyed. If the session is destroyed before a will delay + * interval has elapsed, then the will must be sent at the time of session destruction. + * + * See [MQTT5 Will Delay + * Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901062) + * + * @return A time interval, in seconds, that the server should wait (for a session reconnection) before + * sending the will message associated with the connection's session. + */ + const Crt::Optional<uint32_t> &getWillDelayIntervalSec() const noexcept; + + /** + * The definition of a message to be published when the connection's session is destroyed by the server + * or when the will delay interval has elapsed, whichever comes first. If null, then nothing will be + * sent. + * + * See [MQTT5 Will](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901040) + * + * @return The message to be published when the connection's session is destroyed by the server or when + * the will delay interval has elapsed, whichever comes first. + */ + const Crt::Optional<std::shared_ptr<PublishPacket>> &getWill() const noexcept; + + /** + * List of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901054) + * + * @return List of MQTT5 user properties included with the packet. + */ + const Crt::Vector<UserProperty> &getUserProperties() const noexcept; + + /** + * Intended for internal use only. Initializes the C aws_mqtt5_packet_connack_view + * from PacketConnect + * + * @param raw_options - output parameter containing low level client options to be passed to the C + * @param allocator - memory Allocator + * + */ + bool initializeRawOptions(aws_mqtt5_packet_connect_view &raw_options, Allocator *allocator) noexcept; + + virtual ~ConnectPacket(); + ConnectPacket(const ConnectPacket &) = delete; + ConnectPacket(ConnectPacket &&) noexcept = delete; + ConnectPacket &operator=(const ConnectPacket &) = delete; + ConnectPacket &operator=(ConnectPacket &&) noexcept = delete; + + private: + Allocator *m_allocator; + + /** + * The maximum time interval, in seconds, that is permitted to elapse between the point at which the + * client finishes transmitting one MQTT packet and the point it starts sending the next. The client + * will use PINGREQ packets to maintain this property. + * + * If the responding CONNACK contains a keep alive property value, then that is the negotiated keep + * alive value. Otherwise, the keep alive sent by the client is the negotiated value. + * + * See [MQTT5 Keep Alive](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901045) + */ + uint16_t m_keepAliveIntervalSec; + + /** + * A unique string identifying the client to the server. Used to restore session state between + * connections. + * + * If left empty, the broker will auto-assign a unique client id. When reconnecting, the mqtt5 client + * will always use the auto-assigned client id. + * + * See [MQTT5 Client + * Identifier](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901059) + */ + Crt::String m_clientId; + + /** + * A string value that the server may use for client authentication and authorization. + * + * See [MQTT5 User Name](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901071) + */ + Crt::Optional<Crt::String> m_username; + + /** + * Opaque binary data that the server may use for client authentication and authorization. + * + * See [MQTT5 Password](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901072) + */ + Crt::Optional<ByteCursor> m_password; + + /** + * A time interval, in seconds, that the client requests the server to persist this connection's MQTT + * session state for. Has no meaning if the client has not been configured to rejoin sessions. Must be + * non-zero in order to successfully rejoin a session. + * + * If the responding CONNACK contains a session expiry property value, then that is the negotiated + * session expiry value. Otherwise, the session expiry sent by the client is the negotiated value. + * + * See [MQTT5 Session Expiry + * Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901048) + */ + Crt::Optional<uint32_t> m_sessionExpiryIntervalSec; + + /** + * If set to true, requests that the server send response information in the subsequent CONNACK. This + * response information may be used to set up request-response implementations over MQTT, but doing so + * is outside the scope of the MQTT5 spec and client. + * + * See [MQTT5 Request Response + * Information](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901052) + */ + Crt::Optional<bool> m_requestResponseInformation; + + /** + * If set to true, requests that the server send additional diagnostic information (via response string + * or user properties) in DISCONNECT or CONNACK packets from the server. + * + * See [MQTT5 Request Problem + * Information](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901053) + */ + Crt::Optional<bool> m_requestProblemInformation; + + /** + * Notifies the server of the maximum number of in-flight Qos 1 and 2 messages the client is willing to + * handle. If omitted, then no limit is requested. + * + * See [MQTT5 Receive + * Maximum](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901049) + */ + Crt::Optional<uint16_t> m_receiveMaximum; + + /** + * Notifies the server of the maximum packet size the client is willing to handle. If + * omitted, then no limit beyond the natural limits of MQTT packet size is requested. + * + * See [MQTT5 Maximum Packet + * Size](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901050) + */ + Crt::Optional<uint32_t> m_maximumPacketSizeBytes; + + /** + * A time interval, in seconds, that the server should wait (for a session reconnection) before sending + * the will message associated with the connection's session. If omitted, the server will send the will + * when the associated session is destroyed. If the session is destroyed before a will delay interval + * has elapsed, then the will must be sent at the time of session destruction. + * + * See [MQTT5 Will Delay + * Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901062) + */ + Crt::Optional<uint32_t> m_willDelayIntervalSeconds; + + /** + * The definition of a message to be published when the connection's session is destroyed by the server + * or when the will delay interval has elapsed, whichever comes first. If undefined, then nothing will + * be sent. + * + * See [MQTT5 Will](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901040) + */ + Crt::Optional<std::shared_ptr<PublishPacket>> m_will; + + /** + * Set of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901054) + */ + Crt::Vector<UserProperty> m_userProperties; + + /////////////////////////////////////////////////////////////////////////// + // Underlying data storage for internal use + /////////////////////////////////////////////////////////////////////////// + struct aws_byte_cursor m_usernameCursor; + struct aws_byte_buf m_passowrdStorage; + struct aws_mqtt5_packet_publish_view m_willStorage; + struct aws_mqtt5_user_property *m_userPropertiesStorage; + uint8_t m_requestResponseInformationStorage; + uint8_t m_requestProblemInformationStorage; + }; + + /** + * Data model of an [MQTT5 + * CONNACK](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901074) packet. + */ + class AWS_CRT_CPP_API ConnAckPacket : public IPacket + { + public: + ConnAckPacket( + const aws_mqtt5_packet_connack_view &packet, + Allocator *allocator = ApiAllocator()) noexcept; + + /* The packet type */ + PacketType getType() override { return PacketType::AWS_MQTT5_PT_CONNACK; }; + + /** + * True if the client rejoined an existing session on the server, false otherwise. + * + * See [MQTT5 Session + * Present](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901078) + * + * @return True if the client rejoined an existing session on the server, false otherwise. + */ + bool getSessionPresent() const noexcept; + + /** + * Indicates either success or the reason for failure for the connection attempt. + * + * See [MQTT5 Connect Reason + * Code](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901079) + * + * @return Code indicating either success or the reason for failure for the connection attempt. + */ + ConnectReasonCode getReasonCode() const noexcept; + + /** + * A time interval, in seconds, that the server will persist this connection's MQTT session state + * for. If present, this value overrides any session expiry specified in the preceding CONNECT packet. + * + * See [MQTT5 Session Expiry + * Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901082) + * + * @return A time interval, in seconds, that the server will persist this connection's MQTT session + * state for. + */ + const Crt::Optional<uint32_t> &getSessionExpiryInterval() const noexcept; + + /** + * The maximum amount of in-flight QoS 1 or 2 messages that the server is willing to handle at once. If + * omitted or null, the limit is based on the valid MQTT packet id space (65535). + * + * See [MQTT5 Receive + * Maximum](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901083) + * + * @return The maximum amount of in-flight QoS 1 or 2 messages that the server is willing to handle at + * once. + */ + const Crt::Optional<uint16_t> &getReceiveMaximum() const noexcept; + + /** + * The maximum message delivery quality of service that the server will allow on this connection. + * + * See [MQTT5 Maximum QoS](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901084) + * + * @return The maximum message delivery quality of service that the server will allow on this + * connection. + */ + const Crt::Optional<QOS> &getMaximumQOS() const noexcept; + + /** + * Indicates whether the server supports retained messages. If null, retained messages are + * supported. + * + * See [MQTT5 Retain + * Available](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901085) + * + * @return Whether the server supports retained messages + */ + const Crt::Optional<bool> &getRetainAvailable() const noexcept; + + /** + * Specifies the maximum packet size, in bytes, that the server is willing to accept. If null, there + * is no limit beyond what is imposed by the MQTT spec itself. + * + * See [MQTT5 Maximum Packet + * Size](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901086) + * + * @return The maximum packet size, in bytes, that the server is willing to accept. + */ + const Crt::Optional<uint32_t> &getMaximumPacketSize() const noexcept; + + /** + * Specifies a client identifier assigned to this connection by the server. Only valid when the client + * id of the preceding CONNECT packet was left empty. + * + * See [MQTT5 Assigned Client + * Identifier](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901087) + * + * @return Client identifier assigned to this connection by the server + */ + const Crt::Optional<String> &getAssignedClientIdentifier() const noexcept; + + /** + * Specifies the maximum topic alias value that the server will accept from the client. + * + * See [MQTT5 Topic Alias + * Maximum](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901088) + * + * @return maximum topic alias + */ + const Crt::Optional<uint16_t> getTopicAliasMaximum() const noexcept; + + /** + * Additional diagnostic information about the result of the connection attempt. + * + * See [MQTT5 Reason + * String](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901089) + * + * @return Additional diagnostic information about the result of the connection attempt. + */ + const Crt::Optional<String> &getReasonString() const noexcept; + + /** + * List of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901090) + * + * @return List of MQTT5 user properties included with the packet. + */ + const Vector<UserProperty> &getUserProperty() const noexcept; + + /** + * Indicates whether the server supports wildcard subscriptions. If null, wildcard subscriptions + * are supported. + * + * See [MQTT5 Wildcard Subscriptions + * Available](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901091) + * + * @return Whether the server supports wildcard subscriptions. + */ + const Crt::Optional<bool> &getWildcardSubscriptionsAvaliable() const noexcept; + + /** + * Indicates whether the server supports subscription identifiers. If null, subscription identifiers + * are supported. + * + * See [MQTT5 Subscription Identifiers + * Available](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901092) + * + * @return whether the server supports subscription identifiers. + */ + const Crt::Optional<bool> &getSubscriptionIdentifiersAvaliable() const noexcept; + + /** + * Indicates whether the server supports shared subscription topic filters. If null, shared + * subscriptions are supported. + * + * See [MQTT5 Shared Subscriptions + * Available](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901093) + * + * @return whether the server supports shared subscription topic filters. + */ + const Crt::Optional<bool> &getSharedSubscriptionsAvaliable() const noexcept; + + /** + * Server-requested override of the keep alive interval, in seconds. If null, the keep alive value sent + * by the client should be used. + * + * See [MQTT5 Server Keep + * Alive](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901094) + * + * @return Server-requested override of the keep alive interval, in seconds + */ + const Crt::Optional<uint16_t> &getServerKeepAlive() const noexcept; + + /** + * A value that can be used in the creation of a response topic associated with this connection. + * MQTT5-based request/response is outside the purview of the MQTT5 spec and this client. + * + * See [MQTT5 Response + * Information](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901095) + * + * @return A value that can be used in the creation of a response topic associated with this connection. + */ + const Crt::Optional<String> &getResponseInformation() const noexcept; + + /** + * Property indicating an alternate server that the client may temporarily or permanently attempt + * to connect to instead of the configured endpoint. Will only be set if the reason code indicates + * another server may be used (ServerMoved, UseAnotherServer). + * + * See [MQTT5 Server + * Reference](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901096) + * + * @return Property indicating an alternate server that the client may temporarily or permanently + * attempt to connect to instead of the configured endpoint. + */ + const Crt::Optional<String> &getServerReference() const noexcept; + + virtual ~ConnAckPacket(){}; + ConnAckPacket(const ConnAckPacket &) = delete; + ConnAckPacket(ConnAckPacket &&) noexcept = delete; + ConnAckPacket &operator=(const ConnAckPacket &) = delete; + ConnAckPacket &operator=(ConnAckPacket &&) noexcept = delete; + + private: + /** + * True if the client rejoined an existing session on the server, false otherwise. + * + * See [MQTT5 Session + * Present](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901078) + */ + bool m_sessionPresent; + + /** + * Indicates either success or the reason for failure for the connection attempt. + * + * See [MQTT5 Connect Reason + * Code](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901079) + */ + ConnectReasonCode m_reasonCode; + + /** + * A time interval, in seconds, that the server will persist this connection's MQTT session state + * for. If present, this value overrides any session expiry specified in the preceding CONNECT packet. + * + * See [MQTT5 Session Expiry + * Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901082) + */ + Crt::Optional<uint32_t> m_sessionExpiryInterval; + + /** + * The maximum amount of in-flight QoS 1 or 2 messages that the server is willing to handle at once. If + * omitted, the limit is based on the valid MQTT packet id space (65535). + * + * See [MQTT5 Receive + * Maximum](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901083) + */ + Crt::Optional<uint16_t> m_receiveMaximum; + + /** + * The maximum message delivery quality of service that the server will allow on this connection. + * + * See [MQTT5 Maximum QoS](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901084) + */ + Crt::Optional<QOS> m_maximumQOS; + + /** + * Indicates whether the server supports retained messages. If undefined, retained messages are + * supported. + * + * See [MQTT5 Retain + * Available](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901085) + */ + Crt::Optional<bool> m_retainAvailable; + + /** + * Specifies the maximum packet size, in bytes, that the server is willing to accept. If undefined, + * there is no limit beyond what is imposed by the MQTT spec itself. + * + * See [MQTT5 Maximum Packet + * Size](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901086) + */ + Crt::Optional<uint32_t> m_maximumPacketSize; + + /** + * Specifies a client identifier assigned to this connection by the server. Only valid when the client + * id of the preceding CONNECT packet was left empty. + * + * See [MQTT5 Assigned Client + * Identifier](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901087) + */ + Crt::Optional<String> m_assignedClientIdentifier; + + /** + * Specifies the maximum topic alias value that the server will accept from the client. + * + * See [MQTT5 Topic Alias + * Maximum](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901088) + */ + Crt::Optional<uint16_t> m_topicAliasMaximum; + + /** + * Additional diagnostic information about the result of the connection attempt. + * + * See [MQTT5 Reason + * String](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901089) + */ + Crt::Optional<String> m_reasonString; + + /** + * Indicates whether the server supports wildcard subscriptions. If undefined, wildcard subscriptions + * are supported. + * + * See [MQTT5 Wildcard Subscriptions + * Available](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901091) + */ + Crt::Optional<bool> m_wildcardSubscriptionsAvaliable; + + /** + * Indicates whether the server supports subscription identifiers. If undefined, subscription + * identifiers are supported. + * + * See [MQTT5 Subscription Identifiers + * Available](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901092) + */ + Crt::Optional<bool> m_subscriptionIdentifiersAvaliable; + + /** + * Indicates whether the server supports shared subscription topic filters. If undefined, shared + * subscriptions are supported. + * + * See [MQTT5 Shared Subscriptions + * Available](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901093) + */ + Crt::Optional<bool> m_sharedSubscriptionsAvaliable; + + /** + * Server-requested override of the keep alive interval, in seconds. If undefined, the keep alive value + * sent by the client should be used. + * + * See [MQTT5 Server Keep + * Alive](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901094) + */ + Crt::Optional<uint16_t> m_serverKeepAlive; + + /** + * A value that can be used in the creation of a response topic associated with this connection. + * MQTT5-based request/response is outside the purview of the MQTT5 spec and this client. + * + * See [MQTT5 Response + * Information](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901095) + */ + Crt::Optional<String> m_responseInformation; + + /** + * Property indicating an alternate server that the client may temporarily or permanently attempt + * to connect to instead of the configured endpoint. Will only be set if the reason code indicates + * another server may be used (ServerMoved, UseAnotherServer). + * + * See [MQTT5 Server + * Reference](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901096) + */ + Crt::Optional<String> m_serverReference; + + /** + * Set of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901090) + */ + Vector<UserProperty> m_userProperties; + }; + + /** + * Data model of an [MQTT5 + * DISCONNECT](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901205) packet. + */ + class AWS_CRT_CPP_API DisconnectPacket : public IPacket + { + public: + DisconnectPacket(Allocator *allocator = ApiAllocator()) noexcept; + DisconnectPacket( + const aws_mqtt5_packet_disconnect_view &raw_options, + Allocator *allocator = ApiAllocator()) noexcept; + /* The packet type */ + PacketType getType() override { return PacketType::AWS_MQTT5_PT_DISCONNECT; }; + + bool initializeRawOptions(aws_mqtt5_packet_disconnect_view &raw_options) noexcept; + + /** + * Sets the value indicating the reason that the sender is closing the connection + * + * See [MQTT5 Disconnect Reason + * Code](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901208) + * + * @param reasonCode Value indicating the reason that the sender is closing the connection + * @return The DisconnectPacket Object after setting the reason code. + */ + DisconnectPacket &withReasonCode(const DisconnectReasonCode reasonCode) noexcept; + + /** + * Sets the change to the session expiry interval negotiated at connection time as part of the + * disconnect. Only valid for DISCONNECT packets sent from client to server. It is not valid to + * attempt to change session expiry from zero to a non-zero value. + * + * See [MQTT5 Session Expiry + * Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901211) + * + * @param sessionExpiryIntervalSeconds + * @return The DisconnectPacket Object after setting the session expiry interval. + */ + DisconnectPacket &withSessionExpiryIntervalSec(const uint32_t sessionExpiryIntervalSeconds) noexcept; + + /** + * Sets the additional diagnostic information about the reason that the sender is closing the connection + * + * See [MQTT5 Reason + * String](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901212) + * + * @param reasonString Additional diagnostic information about the reason that the sender is closing the + * connection + * @return The DisconnectPacket Object after setting the reason string. + */ + DisconnectPacket &withReasonString(Crt::String reasonString) noexcept; + + /** + * Sets the property indicating an alternate server that the client may temporarily or permanently + * attempt to connect to instead of the configured endpoint. Will only be set if the reason code + * indicates another server may be used (ServerMoved, UseAnotherServer). + * + * See [MQTT5 Server + * Reference](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901214) + * + * @param serverReference Property indicating an alternate server that the client may temporarily or + * permanently attempt to connect to instead of the configured endpoint. + * @return The DisconnectPacket Object after setting the server reference. + */ + DisconnectPacket &withServerReference(Crt::String serverReference) noexcept; + + /** + * Sets the list of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901213) + * + * @param userProperties List of MQTT5 user properties included with the packet. + * @return The DisconnectPacket Object after setting the user properties. + */ + DisconnectPacket &withUserProperties(const Vector<UserProperty> &userProperties) noexcept; + + /** + * Sets the list of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901213) + * + * @param userProperties List of MQTT5 user properties included with the packet. + * @return The DisconnectPacket Object after setting the user properties. + */ + DisconnectPacket &withUserProperties(Vector<UserProperty> &&userProperties) noexcept; + + /** + * Put a MQTT5 user property to the back of the packet user property vector/list + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116) + * + * @param property set of userProperty of MQTT5 user properties included with the packet. + * @return The ConnectPacket Object after setting the user property + */ + DisconnectPacket &withUserProperty(UserProperty &&property) noexcept; + + /** + * Value indicating the reason that the sender is closing the connection + * + * See [MQTT5 Disconnect Reason + * Code](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901208) + * + * @return Value indicating the reason that the sender is closing the connection + */ + DisconnectReasonCode getReasonCode() const noexcept; + + /** + * A change to the session expiry interval negotiated at connection time as part of the disconnect. Only + * valid for DISCONNECT packets sent from client to server. It is not valid to attempt to change + * session expiry from zero to a non-zero value. + * + * See [MQTT5 Session Expiry + * Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901211) + * + * @return A change to the session expiry interval negotiated at connection time as part of the + * disconnect. + */ + const Crt::Optional<uint32_t> &getSessionExpiryIntervalSec() const noexcept; + + /** + * Additional diagnostic information about the reason that the sender is closing the connection + * + * See [MQTT5 Reason + * String](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901212) + * + * @return Additional diagnostic information about the reason that the sender is closing the connection + */ + const Crt::Optional<Crt::String> &getReasonString() const noexcept; + + /** + * Property indicating an alternate server that the client may temporarily or permanently attempt + * to connect to instead of the configured endpoint. Will only be set if the reason code indicates + * another server may be used (ServerMoved, UseAnotherServer). + * + * See [MQTT5 Server + * Reference](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901214) + * + * @return Property indicating an alternate server that the client may temporarily or permanently + * attempt to connect to instead of the configured endpoint. + */ + const Crt::Optional<Crt::String> &getServerReference() const noexcept; + + /** + * List of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901213) + * + * @return List of MQTT5 user properties included with the packet. + */ + const Crt::Vector<UserProperty> &getUserProperties() const noexcept; + + virtual ~DisconnectPacket(); + DisconnectPacket(const DisconnectPacket &) = delete; + DisconnectPacket(DisconnectPacket &&) noexcept = delete; + DisconnectPacket &operator=(const DisconnectPacket &) = delete; + DisconnectPacket &operator=(DisconnectPacket &&) noexcept = delete; + + private: + Crt::Allocator *m_allocator; + + /** + * Value indicating the reason that the sender is closing the connection + * + * See [MQTT5 Disconnect Reason + * Code](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901208) + */ + DisconnectReasonCode m_reasonCode; + + /** + * Requests a change to the session expiry interval negotiated at connection time as part of the + * disconnect. Only valid for DISCONNECT packets sent from client to server. It is not valid to + * attempt to change session expiry from zero to a non-zero value. + * + * See [MQTT5 Session Expiry + * Interval](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901211) + */ + Crt::Optional<uint32_t> m_sessionExpiryIntervalSec; + + /** + * Additional diagnostic information about the reason that the sender is closing the connection + * + * See [MQTT5 Reason + * String](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901212) + */ + Crt::Optional<Crt::String> m_reasonString; + + /** + * Property indicating an alternate server that the client may temporarily or permanently attempt + * to connect to instead of the configured endpoint. Will only be set if the reason code indicates + * another server may be used (ServerMoved, UseAnotherServer). + * + * See [MQTT5 Server + * Reference](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901214) + */ + Crt::Optional<Crt::String> m_serverReference; + + /** + * Set of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901213) + */ + Crt::Vector<UserProperty> m_userProperties; + + /////////////////////////////////////////////////////////////////////////// + // Underlying data storage for internal use + /////////////////////////////////////////////////////////////////////////// + struct aws_byte_cursor m_reasonStringCursor; + struct aws_byte_cursor m_serverReferenceCursor; + struct aws_mqtt5_user_property *m_userPropertiesStorage; + }; + + /** + * Data model of an [MQTT5 + * PUBACK](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901121) packet + */ + class AWS_CRT_CPP_API PubAckPacket : public IPacket + { + public: + PubAckPacket( + const aws_mqtt5_packet_puback_view &packet, + Allocator *allocator = ApiAllocator()) noexcept; + + PacketType getType() override { return PacketType::AWS_MQTT5_PT_PUBACK; }; + + /** + * Success indicator or failure reason for the associated PUBLISH packet. + * + * See [MQTT5 PUBACK Reason + * Code](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901124) + * + * @return Success indicator or failure reason for the associated PUBLISH packet. + */ + PubAckReasonCode getReasonCode() const noexcept; + + /** + * Additional diagnostic information about the result of the PUBLISH attempt. + * + * See [MQTT5 Reason + * String](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901127) + * + * @return Additional diagnostic information about the result of the PUBLISH attempt. + */ + const Crt::Optional<Crt::String> &getReasonString() const noexcept; + + /** + * List of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901128) + * + * @return List of MQTT5 user properties included with the packet. + */ + const Crt::Vector<UserProperty> &getUserProperties() const noexcept; + + virtual ~PubAckPacket(){}; + PubAckPacket(const PubAckPacket &toCopy) noexcept = delete; + PubAckPacket(PubAckPacket &&toMove) noexcept = delete; + PubAckPacket &operator=(const PubAckPacket &toCopy) noexcept = delete; + PubAckPacket &operator=(PubAckPacket &&toMove) noexcept = delete; + + private: + /** + * Success indicator or failure reason for the associated PUBLISH packet. + * + * See [MQTT5 PUBACK Reason + * Code](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901124) + */ + PubAckReasonCode m_reasonCode; + + /** + * Additional diagnostic information about the result of the PUBLISH attempt. + * + * See [MQTT5 Reason + * String](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901127) + */ + Crt::Optional<Crt::String> m_reasonString; + + /** + * Set of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901128) + */ + Crt::Vector<UserProperty> m_userProperties; + }; + + /** + * PublishResult returned with onPublishCompletionCallback after Publish get called + * + * Publish with QoS0: Ack will be nullptr + * QoS1: Ack will contains a PubAckPacket + */ + class AWS_CRT_CPP_API PublishResult + { + public: + PublishResult(); // QoS 0 success + PublishResult(std::shared_ptr<PubAckPacket> puback); // Qos 1 success + PublishResult(int errorCode); // any failure + + /** + * Get if the publish succeed or not + * + * @return true if error code == 0 and publish succeed + */ + bool wasSuccessful() const { return m_errorCode == 0; }; + + /** + * Get the error code value + * + * @return the error code + */ + int getErrorCode() const { return m_errorCode; }; + + /** + * Get Publish ack packet + * + * @return std::shared_ptr<IPacket> contains a PubAckPacket if client Publish with QoS1, otherwise + * nullptr. + */ + std::shared_ptr<IPacket> getAck() const { return m_ack; }; + + ~PublishResult() noexcept; + PublishResult(const PublishResult &toCopy) noexcept = delete; + PublishResult(PublishResult &&toMove) noexcept = delete; + PublishResult &operator=(const PublishResult &toCopy) noexcept = delete; + PublishResult &operator=(PublishResult &&toMove) noexcept = delete; + + private: + std::shared_ptr<IPacket> m_ack; + int m_errorCode; + }; + + /** + * Configures a single subscription within a Subscribe operation + * + * See [MQTT5 Subscription + * Options](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901169) + */ + class AWS_CRT_CPP_API Subscription + { + + public: + Subscription(Allocator *allocator = ApiAllocator()); + Subscription(Crt::String topicFilter, Mqtt5::QOS qos, Allocator *allocator = ApiAllocator()); + + /** + * Sets topic filter to subscribe to + * + * See [MQTT5 Subscription + * Options](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901169) + * + * @param topicFilter string + * @return The Subscription Object after setting the reason string. + */ + Subscription &withTopicFilter(Crt::String topicFilter) noexcept; + + /** + * Sets Maximum QoS on which the subscriber will accept publish messages. Negotiated QoS may be + * different. + * + * See [MQTT5 Subscription + * Options](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901169) + * + * @param QOS + * @return The Subscription Object after setting the reason string. + */ + Subscription &withQOS(Mqtt5::QOS QOS) noexcept; + + /** + * Sets should the server not send publishes to a client when that client was the one who sent the + * publish? The value will be default to false. + * + * See [MQTT5 Subscription + * Options](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901169) + * + * @param noLocal bool + * @return The Subscription Object after setting the reason string. + */ + Subscription &withNoLocal(bool noLocal) noexcept; + + /** + * Sets should the server not send publishes to a client when that client was the one who sent the + * publish? The value will be default to false. + * + * See [MQTT5 Subscription + * Options](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901169) + * + * @param retain bool + * @return The Subscription Object after setting the reason string. + */ + Subscription &withRetain(bool retain) noexcept; + + /** + * Sets should messages sent due to this subscription keep the retain flag preserved on the message? + * The value will be default to false. + * + * See [MQTT5 Subscription + * Options](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901169) + * + * @param retainHandlingType + * @return The Subscription Object after setting the reason string. + */ + Subscription &withRetainHandlingType(RetainHandlingType retainHandlingType) noexcept; + + bool initializeRawOptions(aws_mqtt5_subscription_view &raw_options) const noexcept; + + virtual ~Subscription(){}; + Subscription(const Subscription &) noexcept; + Subscription(Subscription &&) noexcept; + Subscription &operator=(const Subscription &) noexcept; + Subscription &operator=(Subscription &&) noexcept; + + private: + Allocator *m_allocator; + + /** + * Topic filter to subscribe to + * + * See [MQTT5 Subscription + * Options](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901169) + */ + Crt::String m_topicFilter; + + /** + * Maximum QoS on which the subscriber will accept publish messages. Negotiated QoS may be different. + * + * See [MQTT5 Subscription + * Options](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901169) + */ + Mqtt5::QOS m_qos; + + /** + * Should the server not send publishes to a client when that client was the one who sent the publish? + * If undefined, this is assumed to be false. + * + * See [MQTT5 Subscription + * Options](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901169) + */ + bool m_noLocal; + + /** + * Should messages sent due to this subscription keep the retain flag preserved on the message? If + * undefined, this is assumed to be false. + * + * See [MQTT5 Subscription + * Options](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901169) + */ + bool m_retain; + + /** + * Should retained messages on matching topics be sent in reaction to this subscription? If undefined, + * this is assumed to be RetainHandlingType.SendOnSubscribe. + * + * See [MQTT5 Subscription + * Options](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901169) + */ + RetainHandlingType m_retainHnadlingType; + }; + + /** + * Data model of an [MQTT5 + * SUBSCRIBE](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901161) packet. + */ + class AWS_CRT_CPP_API SubscribePacket : public IPacket + { + public: + SubscribePacket(Allocator *allocator = ApiAllocator()) noexcept; + + /* The packet type */ + PacketType getType() override { return PacketType::AWS_MQTT5_PT_SUBSCRIBE; }; + + /** + * Sets the list of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116) + * + * @param userProperties List of MQTT5 user properties included with the packet. + * @return the SubscribePacket Object after setting the reason string. + */ + SubscribePacket &withUserProperties(const Vector<UserProperty> &userProperties) noexcept; + + /** + * Sets the list of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116) + * + * @param userProperties List of MQTT5 user properties included with the packet. + * @return the SubscribePacket Object after setting the reason string. + */ + SubscribePacket &withUserProperties(Vector<UserProperty> &&userProperties) noexcept; + + /** + * Put a MQTT5 user property to the back of the packet user property vector/list + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116) + * + * @param property userProperty of MQTT5 user properties included with the packet. + * @return The SubscribePacket Object after setting the user property + */ + SubscribePacket &withUserProperty(UserProperty &&property) noexcept; + + /** + * Sets the value to associate with all subscriptions in this request. Publish packets that + * match a subscription in this request should include this identifier in the resulting message. + * + * See [MQTT5 Subscription + * Identifier](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901166) + * + * @param subscriptionIdentifier A positive long to associate with all subscriptions in this request. + * @return The SubscribePacket Object after setting the subscription identifier. + */ + SubscribePacket &withSubscriptionIdentifier(uint32_t subscriptionIdentifier) noexcept; + + /** + * Sets a list of subscriptions within the SUBSCRIBE packet. + * + * @param subscriptions vector of subscriptions to add within the SUBSCRIBE packet. + * + * @return The SubscribePacket Object after setting the subscription. + */ + SubscribePacket &withSubscriptions(const Vector<Subscription> &subscriptions) noexcept; + + /** + * Sets a list of subscriptions within the SUBSCRIBE packet. + * + * @param subscriptions vector of subscriptions to add within the SUBSCRIBE packet. + * + * @return The SubscribePacket Object after setting the subscription. + */ + SubscribePacket &withSubscriptions(Crt::Vector<Subscription> &&subscriptions) noexcept; + + /** + * Sets a single subscription within the SUBSCRIBE packet. + * + * @param subscription The subscription to add within the SUBSCRIBE packet. + * + * @return The SubscribePacket Object after setting the subscription. + */ + SubscribePacket &withSubscription(Subscription &&subscription) noexcept; + + bool initializeRawOptions(aws_mqtt5_packet_subscribe_view &raw_options) noexcept; + + virtual ~SubscribePacket(); + SubscribePacket(const SubscribePacket &) noexcept = delete; + SubscribePacket(SubscribePacket &&) noexcept = delete; + SubscribePacket &operator=(const SubscribePacket &) noexcept = delete; + SubscribePacket &operator=(SubscribePacket &&) noexcept = delete; + + private: + Allocator *m_allocator; + + /** + * List of topic filter subscriptions that the client wishes to listen to + * + * See [MQTT5 Subscribe + * Payload](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901168) + */ + Crt::Vector<Subscription> m_subscriptions; + + /** + * A positive integer to associate with all subscriptions in this request. Publish packets that match + * a subscription in this request should include this identifier in the resulting message. + * + * See [MQTT5 Subscription + * Identifier](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901166) + */ + Crt::Optional<uint32_t> m_subscriptionIdentifier; + + /** + * Set of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901167) + */ + Crt::Vector<UserProperty> m_userProperties; + + /////////////////////////////////////////////////////////////////////////// + // Underlying data storage for internal use + /////////////////////////////////////////////////////////////////////////// + struct aws_mqtt5_subscription_view *m_subscriptionViewStorage; + struct aws_mqtt5_user_property *m_userPropertiesStorage; + }; + + /** + * Data model of an [MQTT5 + * SUBACK](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901171) packet. + */ + class AWS_CRT_CPP_API SubAckPacket : public IPacket + { + public: + SubAckPacket( + const aws_mqtt5_packet_suback_view &packet, + Allocator *allocator = ApiAllocator()) noexcept; + + /* The packet type */ + PacketType getType() override { return PacketType::AWS_MQTT5_PT_SUBACK; }; + + /** + * Returns additional diagnostic information about the result of the SUBSCRIBE attempt. + * + * See [MQTT5 Reason + * String](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901176) + * + * @return Additional diagnostic information about the result of the SUBSCRIBE attempt. + */ + const Crt::Optional<Crt::String> &getReasonString() const noexcept; + + /** + * Returns list of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901177) + * + * @return List of MQTT5 user properties included with the packet. + */ + const Crt::Vector<UserProperty> &getUserProperties() const noexcept; + + /** + * Returns list of reason codes indicating the result of each individual subscription entry in the + * associated SUBSCRIBE packet. + * + * See [MQTT5 Suback + * Payload](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901178) + * + * @return list of reason codes indicating the result of each individual subscription entry in the + * associated SUBSCRIBE packet. + */ + const Crt::Vector<SubAckReasonCode> &getReasonCodes() const noexcept; + + virtual ~SubAckPacket() { m_userProperties.clear(); }; + SubAckPacket(const SubAckPacket &) noexcept = delete; + SubAckPacket(SubAckPacket &&) noexcept = delete; + SubAckPacket &operator=(const SubAckPacket &) noexcept = delete; + SubAckPacket &operator=(SubAckPacket &&) noexcept = delete; + + private: + /** + * A list of reason codes indicating the result of each individual subscription entry in the + * associated SUBSCRIBE packet. + * + * See [MQTT5 Suback + * Payload](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901178) + */ + Crt::Vector<SubAckReasonCode> m_reasonCodes; + + /** + * Additional diagnostic information about the result of the SUBSCRIBE attempt. + * + * See [MQTT5 Reason + * String](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901176) + */ + Crt::Optional<Crt::String> m_reasonString; + + /** + * Set of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901177) + */ + Crt::Vector<UserProperty> m_userProperties; + }; + + /** + * Data model of an [MQTT5 + * UNSUBSCRIBE](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901179) packet. + */ + class AWS_CRT_CPP_API UnsubscribePacket : public IPacket + { + public: + UnsubscribePacket(Allocator *allocator = ApiAllocator()) noexcept; + + /* The packet type */ + PacketType getType() override { return PacketType::AWS_MQTT5_PT_UNSUBSCRIBE; }; + + /** + * Push back a topic filter that the client wishes to unsubscribe from. + * + * @param topicFilter that the client wishes to unsubscribe from + * + * @return The UnsubscribePacket Object after setting the subscription. + */ + UnsubscribePacket &withTopicFilter(Crt::String topicFilter) noexcept; + + /** + * Sets list of topic filter that the client wishes to unsubscribe from. + * + * @param topicFilters vector of subscription topic filters that the client wishes to unsubscribe from + * + * @return The UnsubscribePacket Object after setting the subscription. + */ + UnsubscribePacket &withTopicFilters(Crt::Vector<String> topicFilters) noexcept; + + /** + * Sets the list of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901184) + * + * @param userProperties List of MQTT5 user properties included with the packet. + * @return The UnsubscribePacketBuilder after setting the user properties. + */ + UnsubscribePacket &withUserProperties(const Vector<UserProperty> &userProperties) noexcept; + + /** + * Sets the list of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901184) + * + * @param userProperties List of MQTT5 user properties included with the packet. + * @return The UnsubscribePacketBuilder after setting the user properties. + */ + UnsubscribePacket &withUserProperties(Vector<UserProperty> &&userProperties) noexcept; + + /** + * Put a MQTT5 user property to the back of the packet user property vector/list + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901116) + * + * @param property set of userProperty of MQTT5 user properties included with the packet. + * @return The PublishPacket Object after setting the user property + */ + UnsubscribePacket &withUserProperty(UserProperty &&property) noexcept; + + bool initializeRawOptions(aws_mqtt5_packet_unsubscribe_view &raw_options) noexcept; + + virtual ~UnsubscribePacket(); + UnsubscribePacket(const UnsubscribePacket &) noexcept = delete; + UnsubscribePacket(UnsubscribePacket &&) noexcept = delete; + UnsubscribePacket &operator=(const UnsubscribePacket &) noexcept = delete; + UnsubscribePacket &operator=(UnsubscribePacket &&) noexcept = delete; + + private: + Allocator *m_allocator; + + /** + * List of topic filters that the client wishes to unsubscribe from. + * + * See [MQTT5 Unsubscribe + * Payload](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901185) + */ + Crt::Vector<String> m_topicFilters; + + /** + * Set of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901184) + */ + Crt::Vector<UserProperty> m_userProperties; + + /////////////////////////////////////////////////////////////////////////// + // Underlying data storage for internal use + /////////////////////////////////////////////////////////////////////////// + struct aws_array_list m_topicFiltersList; + struct aws_mqtt5_user_property *m_userPropertiesStorage; + }; + + /** + * Data model of an [MQTT5 + * UNSUBACK](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901187) packet. + */ + class AWS_CRT_CPP_API UnSubAckPacket : public IPacket + { + public: + UnSubAckPacket( + const aws_mqtt5_packet_unsuback_view &packet, + Allocator *allocator = ApiAllocator()) noexcept; + + /* The packet type */ + PacketType getType() override { return PacketType::AWS_MQTT5_PT_UNSUBACK; }; + + /** + * Returns additional diagnostic information about the result of the UNSUBSCRIBE attempt. + * + * See [MQTT5 Reason + * String](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901192) + * + * @return Additional diagnostic information about the result of the UNSUBSCRIBE attempt. + */ + const Crt::Optional<Crt::String> &getReasonString() const noexcept; + + /** + * Returns list of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901193) + * + * @return List of MQTT5 user properties included with the packet. + */ + const Crt::Vector<UserProperty> &getUserProperties() const noexcept; + + /** + * Returns a list of reason codes indicating the result of unsubscribing from each individual topic + * filter entry in the associated UNSUBSCRIBE packet. + * + * See [MQTT5 Unsuback + * Payload](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901194) + * + * @return A list of reason codes indicating the result of unsubscribing from each individual topic + * filter entry in the associated UNSUBSCRIBE packet. + */ + const Crt::Vector<UnSubAckReasonCode> &getReasonCodes() const noexcept; + + virtual ~UnSubAckPacket() { m_userProperties.clear(); }; + UnSubAckPacket(const UnSubAckPacket &) noexcept = delete; + UnSubAckPacket(UnSubAckPacket &&) noexcept = delete; + UnSubAckPacket &operator=(const UnSubAckPacket &) noexcept = delete; + UnSubAckPacket &operator=(UnSubAckPacket &&) noexcept = delete; + + private: + /** + * Additional diagnostic information about the result of the UNSUBSCRIBE attempt. + * + * See [MQTT5 Reason + * String](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901192) + */ + Crt::Optional<Crt::String> m_reasonString; + + /** + * Set of MQTT5 user properties included with the packet. + * + * See [MQTT5 User + * Property](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901193) + */ + Crt::Vector<UserProperty> m_userProperties; + + /** + * A list of reason codes indicating the result of unsubscribing from each individual topic filter entry + * in the associated UNSUBSCRIBE packet. + * + * See [MQTT5 Unsuback + * Payload](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901194) + */ + Crt::Vector<UnSubAckReasonCode> m_reasonCodes; + }; + + } // namespace Mqtt5 + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/mqtt/Mqtt5Types.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/mqtt/Mqtt5Types.h new file mode 100644 index 0000000000..b7aa4f3568 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/mqtt/Mqtt5Types.h @@ -0,0 +1,120 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/mqtt/v5/mqtt5_client.h> +#include <aws/mqtt/v5/mqtt5_types.h> + +namespace Aws +{ + namespace Crt + { + namespace Mqtt5 + { + /** + * MQTT message delivery quality of service. + * + * Enum values match [MQTT5 + * spec](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901234) encoding values. + */ + using QOS = aws_mqtt5_qos; + + /** + * Server return code for connect attempts. + * + * Enum values match [MQTT5 + * spec](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901079) encoding values. + */ + using ConnectReasonCode = aws_mqtt5_connect_reason_code; + + /** + * Reason code inside DISCONNECT packets. Helps determine why a connection was terminated. + * + * Enum values match [MQTT5 + * spec](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901208) encoding values. + */ + using DisconnectReasonCode = aws_mqtt5_disconnect_reason_code; + + /** + * Reason code inside PUBACK packets + * + * Data model of an [MQTT5 + * PUBACK](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901121) packet + */ + using PubAckReasonCode = aws_mqtt5_puback_reason_code; + + /** + * Reason code inside PUBACK packets that indicates the result of the associated PUBLISH request. + * + * Enum values match [MQTT5 + * spec](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901124) encoding values. + */ + using SubAckReasonCode = aws_mqtt5_suback_reason_code; + + /** + * Reason codes inside UNSUBACK packet payloads that specify the results for each topic filter in the + * associated UNSUBSCRIBE packet. + * + * Enum values match [MQTT5 + * spec](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901194) encoding values. + */ + using UnSubAckReasonCode = aws_mqtt5_unsuback_reason_code; + + /** + * Controls how the MQTT5 client should behave with respect to MQTT sessions. + */ + using ClientSessionBehaviorType = aws_mqtt5_client_session_behavior_type; + + /** + * Additional controls for client behavior with respect to operation validation and flow control; these + * checks go beyond the MQTT5 spec to respect limits of specific MQTT brokers. + */ + using ClientExtendedValidationAndFlowControl = aws_mqtt5_extended_validation_and_flow_control_options; + + /** + * Controls how disconnects affect the queued and in-progress operations tracked by the client. Also + * controls how operations are handled while the client is not connected. In particular, if the client is + * not connected, then any operation that would be failed on disconnect (according to these rules) will be + * rejected. + */ + using ClientOperationQueueBehaviorType = aws_mqtt5_client_operation_queue_behavior_type; + + /** + * Controls how the reconnect delay is modified in order to smooth out the distribution of reconnection + * attempt timepoints for a large set of reconnecting clients. + * + * See [Exponential Backoff and + * Jitter](https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/) + */ + using JitterMode = aws_exponential_backoff_jitter_mode; + + /** + * Optional property describing a PUBLISH payload's format. + * + * Enum values match [MQTT5 + * spec](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901111) encoding values. + */ + using PayloadFormatIndicator = aws_mqtt5_payload_format_indicator; + + /** + * Configures how retained messages should be handled when subscribing with a topic filter that matches + * topics with associated retained messages. + * + * Enum values match [MQTT5 + * spec](https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901169) encoding values. + */ + using RetainHandlingType = aws_mqtt5_retain_handling_type; + + /** + * Type of mqtt packet. + * Enum values match mqtt spec encoding values. + * + * https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901022 + */ + using PacketType = aws_mqtt5_packet_type; + + } // namespace Mqtt5 + + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/mqtt/MqttClient.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/mqtt/MqttClient.h new file mode 100644 index 0000000000..03293237cb --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/crt/mqtt/MqttClient.h @@ -0,0 +1,532 @@ +#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/StlAllocator.h> +#include <aws/crt/Types.h> +#include <aws/crt/http/HttpConnection.h> +#include <aws/crt/io/SocketOptions.h> +#include <aws/crt/io/TlsOptions.h> + +#include <aws/mqtt/client.h> + +#include <atomic> +#include <functional> +#include <memory> + +namespace Aws +{ + namespace Crt + { + namespace Io + { + class ClientBootstrap; + } + + namespace Http + { + class HttpRequest; + } + + namespace Mqtt + { + class MqttClient; + class MqttConnection; + + /** + * Invoked Upon Connection loss. + */ + using OnConnectionInterruptedHandler = std::function<void(MqttConnection &connection, int error)>; + + /** + * Invoked Upon Connection resumed. + */ + using OnConnectionResumedHandler = + std::function<void(MqttConnection &connection, ReturnCode connectCode, bool sessionPresent)>; + + /** + * Invoked when a connack message is received, or an error occurred. + */ + using OnConnectionCompletedHandler = std::function< + void(MqttConnection &connection, int errorCode, ReturnCode returnCode, bool sessionPresent)>; + + /** + * Invoked when a suback message is received. + */ + using OnSubAckHandler = std::function< + void(MqttConnection &connection, uint16_t packetId, const String &topic, QOS qos, int errorCode)>; + + /** + * Invoked when a suback message for multiple topics is received. + */ + using OnMultiSubAckHandler = std::function<void( + MqttConnection &connection, + uint16_t packetId, + const Vector<String> &topics, + QOS qos, + int errorCode)>; + + /** + * Invoked when a disconnect message has been sent. + */ + using OnDisconnectHandler = std::function<void(MqttConnection &connection)>; + + /** + * Invoked upon receipt of a Publish message on a subscribed topic. + * @param connection The connection object + * @param topic The information channel to which the payload data was published. + * @param payload The payload data. + * @param dup DUP flag. If true, this might be re-delivery of an earlier + * attempt to send the message. + * @param qos Quality of Service used to deliver the message. + * @param retain Retain flag. If true, the message was sent as a result of + * a new subscription being made by the client. + */ + using OnMessageReceivedHandler = std::function<void( + MqttConnection &connection, + const String &topic, + const ByteBuf &payload, + bool dup, + QOS qos, + bool retain)>; + + /** + * @deprecated Use OnMessageReceivedHandler + */ + using OnPublishReceivedHandler = + std::function<void(MqttConnection &connection, const String &topic, const ByteBuf &payload)>; + + /** + * Invoked when an operation completes. For QoS 0, this is when the packet is passed to the tls + * layer. For QoS 1 (and 2, in theory) this is when the final ack packet is received from the server. + */ + using OnOperationCompleteHandler = + std::function<void(MqttConnection &connection, uint16_t packetId, int errorCode)>; + + /** + * Callback for users to invoke upon completion of, presumably asynchronous, OnWebSocketHandshakeIntercept + * callback's initiated process. + */ + using OnWebSocketHandshakeInterceptComplete = + std::function<void(const std::shared_ptr<Http::HttpRequest> &, int errorCode)>; + + /** + * Invoked during websocket handshake to give users opportunity to transform an http request for purposes + * such as signing/authorization etc... Returning from this function does not continue the websocket + * handshake since some work flows may be asynchronous. To accommodate that, onComplete must be invoked upon + * completion of the signing process. + */ + using OnWebSocketHandshakeIntercept = std::function< + void(std::shared_ptr<Http::HttpRequest> req, const OnWebSocketHandshakeInterceptComplete &onComplete)>; + + /* Simple statistics about the current state of the client's queue of operations */ + struct AWS_CRT_CPP_API MqttConnectionOperationStatistics + { + /* + * total number of operations submitted to the connection that have not yet been completed. Unacked + * operations are a subset of this. + */ + uint64_t incompleteOperationCount; + + /* + * total packet size of operations submitted to the connection that have not yet been completed. Unacked + * operations are a subset of this. + */ + uint64_t incompleteOperationSize; + + /* + * total number of operations that have been sent to the server and are waiting for a corresponding ACK + * before they can be completed. + */ + uint64_t unackedOperationCount; + + /* + * total packet size of operations that have been sent to the server and are waiting for a corresponding + * ACK before they can be completed. + */ + uint64_t unackedOperationSize; + }; + + /** + * Represents a persistent Mqtt Connection. The memory is owned by MqttClient. + * To get a new instance of this class, see MqttClient::NewConnection. Unless + * specified all function arguments need only to live through the duration of the + * function call. + */ + class AWS_CRT_CPP_API MqttConnection final + { + friend class MqttClient; + + public: + ~MqttConnection(); + MqttConnection(const MqttConnection &) = delete; + MqttConnection(MqttConnection &&) = delete; + MqttConnection &operator=(const MqttConnection &) = delete; + MqttConnection &operator=(MqttConnection &&) = 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; + + /** + * Sets LastWill for the connection. + * @param topic topic the will message should be published to + * @param qos QOS the will message should be published with + * @param retain true if the will publish should be treated as a retained publish + * @param payload payload of the will message + * @return success/failure in setting the will + */ + bool SetWill(const char *topic, QOS qos, bool retain, const ByteBuf &payload) noexcept; + + /** + * Sets login credentials for the connection. The must get set before the Connect call + * if it is to be used. + * @param userName user name to add to the MQTT CONNECT packet + * @param password password to add to the MQTT CONNECT packet + * @return success/failure + */ + bool SetLogin(const char *userName, const char *password) noexcept; + + /** + * @deprecated Sets websocket proxy options. Replaced by SetHttpProxyOptions. + */ + bool SetWebsocketProxyOptions(const Http::HttpClientConnectionProxyOptions &proxyOptions) noexcept; + + /** + * Sets http proxy options. In order to use an http proxy with mqtt either + * (1) Websockets are used + * (2) Mqtt-over-tls is used and the ALPN list of the tls context contains a tag that resolves to mqtt + * + * @param proxyOptions proxy configuration for making the mqtt connection + * + * @return success/failure + */ + bool SetHttpProxyOptions(const Http::HttpClientConnectionProxyOptions &proxyOptions) noexcept; + + /** + * Customize time to wait between reconnect attempts. + * The time will start at min and multiply by 2 until max is reached. + * The time resets back to min after a successful connection. + * This function should only be called before Connect(). + * + * @param min_seconds minimum time to wait before attempting a reconnect + * @param max_seconds maximum time to wait before attempting a reconnect + * + * @return success/failure + */ + bool SetReconnectTimeout(uint64_t min_seconds, uint64_t max_seconds) noexcept; + + /** + * Initiates the connection, OnConnectionCompleted will + * be invoked in an event-loop thread. + * + * @param clientId client identifier to use when establishing the mqtt connection + * @param cleanSession false to attempt to rejoin an existing session for the client id, true to skip + * and start with a new session + * @param keepAliveTimeSecs time interval to space mqtt pings apart by + * @param pingTimeoutMs timeout in milliseconds before the keep alive ping is considered to have failed + * @param protocolOperationTimeoutMs timeout in milliseconds to give up waiting for a response packet + * for an operation. Necessary due to throttling properties on certain server implementations that do + * not return an ACK for throttled operations. + * + * @return true if the connection attempt was successfully started (implying a callback will be invoked + * with the eventual result), false if it could not be started (no callback will happen) + */ + bool Connect( + const char *clientId, + bool cleanSession, + uint16_t keepAliveTimeSecs = 0, + uint32_t pingTimeoutMs = 0, + uint32_t protocolOperationTimeoutMs = 0) noexcept; + + /** + * Initiates disconnect, OnDisconnectHandler will be invoked in an event-loop thread. + * @return success/failure in initiating disconnect + */ + bool Disconnect() noexcept; + + /// @private + aws_mqtt_client_connection *GetUnderlyingConnection() noexcept; + + /** + * Subscribes to topicFilter. OnMessageReceivedHandler will be invoked from an event-loop + * thread upon an incoming Publish message. OnSubAckHandler will be invoked + * upon receipt of a suback message. + * + * @param topicFilter topic filter to subscribe to + * @param qos maximum qos client is willing to receive matching messages on + * @param onMessage callback to invoke when a message is received based on matching this filter + * @param onSubAck callback to invoke with the server's response to the subscribe request + * + * @return packet id of the subscribe request, or 0 if the attempt failed synchronously + */ + uint16_t Subscribe( + const char *topicFilter, + QOS qos, + OnMessageReceivedHandler &&onMessage, + OnSubAckHandler &&onSubAck) noexcept; + + /** + * @deprecated Use alternate Subscribe() + */ + uint16_t Subscribe( + const char *topicFilter, + QOS qos, + OnPublishReceivedHandler &&onPublish, + OnSubAckHandler &&onSubAck) noexcept; + + /** + * Subscribes to multiple topicFilters. OnMessageReceivedHandler will be invoked from an event-loop + * thread upon an incoming Publish message. OnMultiSubAckHandler will be invoked + * upon receipt of a suback message. + * + * @param topicFilters list of pairs of topic filters and message callbacks to invoke on a matching + * publish + * @param qos maximum qos client is willing to receive matching messages on + * @param onOpComplete callback to invoke with the server's response to the subscribe request + * + * @return packet id of the subscribe request, or 0 if the attempt failed synchronously + */ + uint16_t Subscribe( + const Vector<std::pair<const char *, OnMessageReceivedHandler>> &topicFilters, + QOS qos, + OnMultiSubAckHandler &&onOpComplete) noexcept; + + /** + * @deprecated Use alternate Subscribe() + */ + uint16_t Subscribe( + const Vector<std::pair<const char *, OnPublishReceivedHandler>> &topicFilters, + QOS qos, + OnMultiSubAckHandler &&onOpComplete) noexcept; + + /** + * Installs a handler for all incoming publish messages, regardless of if Subscribe has been + * called on the topic. + * + * @param onMessage callback to invoke for all received messages + * @return success/failure + */ + bool SetOnMessageHandler(OnMessageReceivedHandler &&onMessage) noexcept; + + /** + * @deprecated Use alternate SetOnMessageHandler() + */ + bool SetOnMessageHandler(OnPublishReceivedHandler &&onPublish) noexcept; + + /** + * Unsubscribes from topicFilter. OnOperationCompleteHandler will be invoked upon receipt of + * an unsuback message. + * + * @param topicFilter topic filter to unsubscribe the session from + * @param onOpComplete callback to invoke on receipt of the server's UNSUBACK message + * + * @return packet id of the unsubscribe request, or 0 if the attempt failed synchronously + */ + uint16_t Unsubscribe(const char *topicFilter, OnOperationCompleteHandler &&onOpComplete) noexcept; + + /** + * Publishes to a topic. + * + * @param topic topic to publish to + * @param qos QOS to publish the message with + * @param retain should this message replace the current retained message of the topic? + * @param payload payload of the message + * @param onOpComplete completion callback to invoke when the operation is complete. If QoS is 0, then + * the callback is invoked when the message is passed to the tls handler, otherwise it's invoked + * on receipt of the final response from the server. + * + * @return packet id of the publish request, or 0 if the attempt failed synchronously + */ + uint16_t Publish( + const char *topic, + QOS qos, + bool retain, + const ByteBuf &payload, + OnOperationCompleteHandler &&onOpComplete) noexcept; + + /** + * Get the statistics about the current state of the connection's queue of operations + * + * @return MqttConnectionOperationStatistics + */ + const MqttConnectionOperationStatistics &GetOperationStatistics() noexcept; + + OnConnectionInterruptedHandler OnConnectionInterrupted; + OnConnectionResumedHandler OnConnectionResumed; + OnConnectionCompletedHandler OnConnectionCompleted; + OnDisconnectHandler OnDisconnect; + OnWebSocketHandshakeIntercept WebsocketInterceptor; + + private: + aws_mqtt_client *m_owningClient; + aws_mqtt_client_connection *m_underlyingConnection; + String m_hostName; + uint16_t m_port; + Crt::Io::TlsContext m_tlsContext; + Io::TlsConnectionOptions m_tlsOptions; + Io::SocketOptions m_socketOptions; + Crt::Optional<Http::HttpClientConnectionProxyOptions> m_proxyOptions; + void *m_onAnyCbData; + bool m_useTls; + bool m_useWebsocket; + MqttConnectionOperationStatistics m_operationStatistics; + + MqttConnection( + aws_mqtt_client *client, + const char *hostName, + uint16_t port, + const Io::SocketOptions &socketOptions, + const Crt::Io::TlsContext &tlsContext, + bool useWebsocket) noexcept; + + MqttConnection( + aws_mqtt_client *client, + const char *hostName, + uint16_t port, + const Io::SocketOptions &socketOptions, + bool useWebsocket) noexcept; + + static void s_onConnectionInterrupted(aws_mqtt_client_connection *, int errorCode, void *userData); + static void s_onConnectionCompleted( + aws_mqtt_client_connection *, + int errorCode, + enum aws_mqtt_connect_return_code returnCode, + bool sessionPresent, + void *userData); + static void s_onConnectionResumed( + aws_mqtt_client_connection *, + ReturnCode returnCode, + bool sessionPresent, + void *userData); + + static void s_onDisconnect(aws_mqtt_client_connection *connection, void *userData); + static void s_onPublish( + aws_mqtt_client_connection *connection, + const aws_byte_cursor *topic, + const aws_byte_cursor *payload, + bool dup, + enum aws_mqtt_qos qos, + bool retain, + void *user_data); + + static void s_onSubAck( + aws_mqtt_client_connection *connection, + uint16_t packetId, + const struct aws_byte_cursor *topic, + enum aws_mqtt_qos qos, + int error_code, + void *userdata); + static void s_onMultiSubAck( + aws_mqtt_client_connection *connection, + uint16_t packetId, + const struct aws_array_list *topic_subacks, + int error_code, + void *userdata); + static void s_onOpComplete( + aws_mqtt_client_connection *connection, + uint16_t packetId, + int errorCode, + void *userdata); + + static void s_onWebsocketHandshake( + struct aws_http_message *request, + void *user_data, + aws_mqtt_transform_websocket_handshake_complete_fn *complete_fn, + void *complete_ctx); + + static void s_connectionInit( + MqttConnection *self, + const char *hostName, + uint16_t port, + const Io::SocketOptions &socketOptions); + }; + + /** + * An MQTT client. This is a move-only type. Unless otherwise specified, + * all function arguments need only to live through the duration of the + * function call. + */ + class AWS_CRT_CPP_API MqttClient final + { + public: + /** + * Initialize an MqttClient using bootstrap and allocator + */ + MqttClient(Io::ClientBootstrap &bootstrap, Allocator *allocator = ApiAllocator()) noexcept; + + /** + * Initialize an MqttClient using a allocator and the default ClientBootstrap + * + * For more information on the default ClientBootstrap see + * Aws::Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap + */ + MqttClient(Allocator *allocator = ApiAllocator()) noexcept; + + ~MqttClient(); + MqttClient(const MqttClient &) = delete; + MqttClient(MqttClient &&) noexcept; + MqttClient &operator=(const MqttClient &) = delete; + MqttClient &operator=(MqttClient &&) noexcept; + + /** + * @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; + + /** + * Create a new connection object using TLS from the client. The client must outlive + * all of its connection instances. + * + * @param hostName endpoint to connect to + * @param port port to connect to + * @param socketOptions socket options to use when establishing the connection + * @param tlsContext tls context to use with the connection + * @param useWebsocket should the connection use websockets or should it use direct mqtt? + * + * @return a new connection object. Connect() will still need to be called after all further + * configuration is finished. + */ + std::shared_ptr<MqttConnection> NewConnection( + const char *hostName, + uint16_t port, + const Io::SocketOptions &socketOptions, + const Crt::Io::TlsContext &tlsContext, + bool useWebsocket = false) noexcept; + + /** + * Create a new connection object over plain text from the client. The client must outlive + * all of its connection instances. + * @param hostName endpoint to connect to + * @param port port to connect to + * @param socketOptions socket options to use when establishing the connection + * @param useWebsocket should the connection use websockets or should it use direct mqtt? + * + * @return a new connection object. Connect() will still need to be called after all further + * configuration is finished. + */ + std::shared_ptr<MqttConnection> NewConnection( + const char *hostName, + uint16_t port, + const Io::SocketOptions &socketOptions, + bool useWebsocket = false) noexcept; + + private: + aws_mqtt_client *m_client; + }; + } // namespace Mqtt + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/iot/Mqtt5Client.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/iot/Mqtt5Client.h new file mode 100644 index 0000000000..4c22917013 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/iot/Mqtt5Client.h @@ -0,0 +1,548 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/Config.h> +#include <aws/crt/Exports.h> +#include <aws/crt/auth/Sigv4Signing.h> +#include <aws/crt/mqtt/Mqtt5Client.h> +#include <aws/iot/MqttCommon.h> + +#if !BYO_CRYPTO + +namespace Aws +{ + using namespace Crt::Mqtt5; + + namespace Io + { + class ClientBootstrap; + class SocketOptions; + class TlsContextOptions; + class WebsocketConfig; + } // namespace Io + + namespace Iot + { + + /** + * Class encapsulating configuration for establishing an Aws IoT Mqtt5 Connectin with custom authorizer + */ + class AWS_CRT_CPP_API Mqtt5CustomAuthConfig + { + public: + /** + * Create a custom authorizer configuration + */ + Mqtt5CustomAuthConfig(Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + virtual ~Mqtt5CustomAuthConfig(); + + Mqtt5CustomAuthConfig(const Mqtt5CustomAuthConfig &rhs); + Mqtt5CustomAuthConfig(Mqtt5CustomAuthConfig &&rhs) = delete; + + Mqtt5CustomAuthConfig &operator=(const Mqtt5CustomAuthConfig &rhs); + Mqtt5CustomAuthConfig &operator=(Mqtt5CustomAuthConfig &&rhs) = delete; + + Mqtt5CustomAuthConfig &WithAuthorizerName(Crt::String authName); + Mqtt5CustomAuthConfig &WithUsername(Crt::String username); + Mqtt5CustomAuthConfig &WithPassword(Crt::ByteCursor password); + Mqtt5CustomAuthConfig &WithTokenKeyName(Crt::String tokenKeyName); + Mqtt5CustomAuthConfig &WithTokenValue(Crt::String tokenValue); + Mqtt5CustomAuthConfig &WithTokenSignature(Crt::String tokenSignature); + + const Crt::Optional<Crt::String> &GetAuthorizerName(); + const Crt::Optional<Crt::String> &GetUsername(); + const Crt::Optional<Crt::ByteCursor> &GetPassword(); + const Crt::Optional<Crt::String> &GetTokenKeyName(); + const Crt::Optional<Crt::String> &GetTokenValue(); + const Crt::Optional<Crt::String> &GetTokenSignature(); + + private: + /** + * Name of the custom authorizer to use. + * + * Required if the endpoint does not have a default custom authorizer associated with it. It is strongly + * suggested to URL-encode this value; the SDK will not do so for you. + */ + Crt::Optional<Crt::String> m_authorizerName; + + /** + * The username to use with the custom authorizer. Query-string elements of this property value will be + * unioned with the query-string elements implied by other properties in this object. + * + * For example, if you set this to: + * + * 'MyUsername?someKey=someValue' + * + * and use {@link authorizerName} to specify the authorizer, the final username would look like: + * + * `MyUsername?someKey=someValue&x-amz-customauthorizer-name=<your authorizer's name>&...` + */ + Crt::Optional<Crt::String> m_username; + + /** + * The password to use with the custom authorizer. Becomes the MQTT5 CONNECT packet's password property. + * AWS IoT Core will base64 encode this binary data before passing it to the authorizer's lambda function. + */ + Crt::Optional<Crt::ByteCursor> m_password; + + /** + * Key used to extract the custom authorizer token from MQTT username query-string properties. + * + * Required if the custom authorizer has signing enabled. It is strongly suggested to URL-encode this + * value; the SDK will not do so for you. + */ + Crt::Optional<Crt::String> m_tokenKeyName; + + /** + * An opaque token value. This value must be signed by the private key associated with the custom authorizer + * and the result placed in the {@link tokenSignature} property. + * + * Required if the custom authorizer has signing enabled. + */ + Crt::Optional<Crt::String> m_tokenValue; + + /** + * The digital signature of the token value in the {@link tokenValue} property. The signature must be based + * on the private key associated with the custom authorizer. The signature must be base64 encoded. + * + * Required if the custom authorizer has signing enabled. It is strongly suggested to URL-encode this + * value; the SDK will not do so for you. + */ + Crt::Optional<Crt::String> m_tokenSignature; + + Crt::ByteBuf m_passwordStorage; + Crt::Allocator *m_allocator; + }; + + /** + * Represents a unique configuration for mqtt5 client and connection. Helps to setup Mqtt5ClientOptionsBuilder + * for mqtt5 client. + */ + class AWS_CRT_CPP_API Mqtt5ClientBuilder final + { + public: + /** + * Set the builder up for MTLS using certPath and pkeyPath. These are files on disk and must be in the + * PEM format. + * + * @param hostName - AWS IoT endpoint to connect to + * @param certPath path to the X509 certificate (pem file) to use + * @param pkeyPath path to the private key (pem file) to use + * @param allocator memory allocator to use + * + * @return Mqtt5ClientBuilder + */ + static Mqtt5ClientBuilder *NewMqtt5ClientBuilderWithMtlsFromPath( + const Crt::String hostName, + const char *certPath, + const char *pkeyPath, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Sets the builder up for MTLS using cert and pkey. These are in-memory buffers and must be in the PEM + * format. + * + * @param hostName - AWS IoT endpoint to connect to + * @param certPath buffer containing the X509 certificate in a PEM format + * @param pkeyPath buffer containing the private key in a PEM format + * @param allocator memory allocator to use + * + * @return Mqtt5ClientBuilder + */ + static Mqtt5ClientBuilder *NewMqtt5ClientBuilderWithMtlsFromMemory( + const Crt::String hostName, + const Crt::ByteCursor &certPath, + const Crt::ByteCursor &pkeyPath, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Sets the builder up for MTLS, using a PKCS#11 library for private key operations. + * + * NOTE: This only works on Unix devices. + * + * @param hostName - AWS IoT endpoint to connect to + * @param pkcs11Options PKCS#11 options + * @param allocator memory allocator to use + * + * @return Mqtt5ClientBuilder + */ + static Mqtt5ClientBuilder *NewMqtt5ClientBuilderWithMtlsPkcs11( + const Crt::String hostName, + const Crt::Io::TlsContextPkcs11Options &pkcs11Options, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Sets the builder up for MTLS, using a certificate in a Windows certificate store. + * + * NOTE: This only works on Windows. + * + * @param hostName - AWS IoT endpoint to connect to + * @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 memory allocator to use + * + * @return Mqtt5ClientBuilder + */ + static Mqtt5ClientBuilder *NewMqtt5ClientBuilderWithWindowsCertStorePath( + const Crt::String hostName, + const char *windowsCertStorePath, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Sets the builder up for Websocket connection. + * + * @param hostName - AWS IoT endpoint to connect to + * @param config websocket configuration information + * @param allocator memory allocator to use + * + * Mqtt5ClientBuilder + */ + static Mqtt5ClientBuilder *NewMqtt5ClientBuilderWithWebsocket( + const Crt::String hostName, + const WebsocketConfig &config, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Sets the builder up for connection using authorization configuration. + * + * @param hostName - AWS IoT endpoint to connect to + * @param customAuthConfig custom authorization configuration information + * @param allocator memory allocator to use + * + * Mqtt5ClientBuilder + */ + static Mqtt5ClientBuilder *NewMqtt5ClientBuilderWithCustomAuthorizer( + const Crt::String hostName, + const Mqtt5CustomAuthConfig &customAuthConfig, + Crt::Allocator *allocator) noexcept; + + /** + * Sets the builder up for connection using authorization configuration using Websockets. + * + * @param hostName - AWS IoT endpoint to connect to + * @param customAuthConfig custom authorization configuration information + * @param config websocket configuration information + * @param allocator memory allocator to use + * + * Mqtt5ClientBuilder + */ + static Mqtt5ClientBuilder *NewMqtt5ClientBuilderWithCustomAuthorizerWebsocket( + const Crt::String hostName, + const Mqtt5CustomAuthConfig &customAuthConfig, + const WebsocketConfig &config, + Crt::Allocator *allocator) noexcept; + + /** + * Sets the host to connect to. + * + * @param hostname endpoint to connect to + * + * @return this option object + */ + Mqtt5ClientBuilder &withHostName(Crt::String hostname); + + /** + * Set port to connect to + * + * @param port port to connect to + * + * @return this option object + */ + Mqtt5ClientBuilder &withPort(uint16_t port) noexcept; + + /** + * Sets the certificate authority for the endpoint you're connecting to. This is a path to a file on disk + * and must be in PEM format. + * + * @param caPath path to the CA file in PEM format + * + * @return this builder object + */ + Mqtt5ClientBuilder &WithCertificateAuthority(const char *caPath) noexcept; + + /** + * Sets the certificate authority for the endpoint you're connecting to. This is an in-memory buffer and + * must be in PEM format. + * + * @param cert buffer containing the CA certificate in a PEM format + * + * @return this builder object + */ + Mqtt5ClientBuilder &WithCertificateAuthority(const Crt::ByteCursor &cert) noexcept; + + /** + * Sets http proxy options. + * + * @param proxyOptions http proxy configuration for connection establishment + * + * @return this option object + */ + Mqtt5ClientBuilder &withHttpProxyOptions( + const Crt::Http::HttpClientConnectionProxyOptions &proxyOptions) noexcept; + + /** + * Sets the custom authorizer settings. This function will modify the username, port, and TLS options. + * + * @return this builder object + */ + Mqtt5ClientBuilder &WithCustomAuthorizer(const Iot::Mqtt5CustomAuthConfig &config) noexcept; + + /** + * Sets mqtt5 connection options + * + * @param packetConnect package connection options + * + * @return this option object + */ + Mqtt5ClientBuilder &withConnectOptions(std::shared_ptr<ConnectPacket> packetConnect) noexcept; + + /** + * Sets session behavior. Overrides how the MQTT5 client should behave with respect to MQTT sessions. + * + * @param sessionBehavior how the MQTT5 client should behave with respect to MQTT sessions. + * + * @return this option object + */ + Mqtt5ClientBuilder &withSessionBehavior(ClientSessionBehaviorType sessionBehavior) noexcept; + + /** + * Sets client extended validation and flow control, additional controls for client behavior with + * respect to operation validation and flow control; these checks go beyond the base MQTT5 spec to + * respect limits of specific MQTT brokers. + * + * @param clientExtendedValidationAndFlowControl + * + * @return this option object + */ + Mqtt5ClientBuilder &withClientExtendedValidationAndFlowControl( + ClientExtendedValidationAndFlowControl clientExtendedValidationAndFlowControl) noexcept; + + /** + * Sets OfflineQueueBehavior, controls how disconnects affect the queued and in-progress operations + * tracked by the client. Also controls how new operations are handled while the client is not + * connected. In particular, if the client is not connected, then any operation that would be failed + * on disconnect (according to these rules) will also be rejected. + * + * @param offlineQueueBehavior + * + * @return this option object + */ + Mqtt5ClientBuilder &withOfflineQueueBehavior( + ClientOperationQueueBehaviorType offlineQueueBehavior) noexcept; + + /** + * Sets ReconnectOptions. Reconnect options includes retryJitterMode, min reconnect delay time and + * max reconnect delay time + * + * @param reconnectOptions + * + * @return this option object + */ + Mqtt5ClientBuilder &withReconnectOptions(ReconnectOptions reconnectOptions) noexcept; + + /** + * Sets minConnectedTimeToResetReconnectDelayMs, amount of time that must elapse with an established + * connection before the reconnect delay is reset to the minimum. This helps alleviate bandwidth-waste + * in fast reconnect cycles due to permission failures on operations. + * + * @param minConnectedTimeToResetReconnectDelayMs + * + * @return this option object + */ + Mqtt5ClientBuilder &withMinConnectedTimeToResetReconnectDelayMs( + uint64_t minConnectedTimeToResetReconnectDelayMs) noexcept; + + /** + * Sets ping timeout (ms). Time interval to wait after sending a PINGREQ for a PINGRESP to arrive. + * If one does not arrive, the client will close the current connection. + * + * @param pingTimeoutMs + * + * @return this option object + */ + Mqtt5ClientBuilder &withPingTimeoutMs(uint32_t pingTimeoutMs) noexcept; + + /** + * Sets Connack Timeout (ms). Time interval to wait after sending a CONNECT request for a CONNACK + * to arrive. If one does not arrive, the connection will be shut down. + * + * @param connackTimeoutMs + * + * @return this option object + */ + Mqtt5ClientBuilder &withConnackTimeoutMs(uint32_t connackTimeoutMs) noexcept; + + /** + * Sets Operation Timeout(Seconds). Time interval to wait for an ack after sending a QoS 1+ PUBLISH, + * SUBSCRIBE, or UNSUBSCRIBE before failing the operation. + * + * @param ackTimeoutSeconds + * + * @return this option object + */ + Mqtt5ClientBuilder &withAckTimeoutSeconds(uint32_t ackTimeoutSeconds) noexcept; + + /** + * Overrides the default SDK Name to send as a metric in the MQTT CONNECT packet. + * + * @param sdkName string to use as the SDK name parameter in the connection string + * + * @return this builder object + */ + Mqtt5ClientBuilder &WithSdkName(const Crt::String &sdkName); + + /** + * Overrides the default SDK Version to send as a metric in the MQTT CONNECT packet. + * + * @param sdkVersion string to use as the SDK version parameter in the connection string + * + * @return this builder object + */ + Mqtt5ClientBuilder &WithSdkVersion(const Crt::String &sdkVersion); + + /** + * Builds a client configuration object from the set options. + * + * @return a new client connection config instance + */ + std::shared_ptr<Mqtt5Client> Build() noexcept; + + /** + * @return true if the instance is in a valid state, false otherwise. + */ + explicit operator bool() const noexcept { return m_lastError == 0; } + + /** + * @return the value of the last aws error encountered by operations on this instance. + */ + int LastError() const noexcept { return m_lastError ? m_lastError : AWS_ERROR_UNKNOWN; } + + virtual ~Mqtt5ClientBuilder() + { + if (m_options) + { + delete m_options; + } + }; + Mqtt5ClientBuilder(const Mqtt5ClientBuilder &) = delete; + Mqtt5ClientBuilder(Mqtt5ClientBuilder &&) = delete; + Mqtt5ClientBuilder &operator=(const Mqtt5ClientBuilder &) = delete; + Mqtt5ClientBuilder &operator=(Mqtt5ClientBuilder &&) = delete; + + /** + * Setup callback trigged when client successfully establishes an MQTT connection + * + * @param callback + * + * @return this option object + */ + Mqtt5ClientBuilder &withClientConnectionSuccessCallback(OnConnectionSuccessHandler callback) noexcept; + + /** + * Setup callback trigged when client fails to establish an MQTT connection + * + * @param callback + * + * @return this option object + */ + Mqtt5ClientBuilder &withClientConnectionFailureCallback(OnConnectionFailureHandler callback) noexcept; + + /** + * Setup callback handler trigged when client's current MQTT connection is closed + * + * @param callback + * + * @return this option object + */ + Mqtt5ClientBuilder &withClientDisconnectionCallback(OnDisconnectionHandler callback) noexcept; + + /** + * Setup callback handler trigged when client reaches the "Stopped" state + * + * @param callback + * + * @return this option object + */ + Mqtt5ClientBuilder &withClientStoppedCallback(OnStoppedHandler callback) noexcept; + + /** + * Setup callback handler trigged when client begins an attempt to connect to the remote endpoint. + * + * @param callback + * + * @return this option object + */ + Mqtt5ClientBuilder &withClientAttemptingConnectCallback(OnAttemptingConnectHandler callback) noexcept; + + /** + * Setup callback handler trigged when an MQTT PUBLISH packet is received by the client + * + * @param callback + * + * @return this option object + */ + Mqtt5ClientBuilder &withPublishReceivedCallback(OnPublishReceivedHandler callback) noexcept; + + private: + // Common setup shared by all valid constructors + Mqtt5ClientBuilder(Crt::Allocator *allocator) noexcept; + // Common setup shared by all valid constructors + Mqtt5ClientBuilder(int error, Crt::Allocator *allocator) noexcept; + + Crt::Allocator *m_allocator; + + /** + * Network port of the MQTT server to connect to. + */ + uint16_t m_port; + + /** + * Client bootstrap to use. In almost all cases, this can be left undefined. + */ + Io::ClientBootstrap *m_bootstrap; + + /** + * TLS context for secure socket connections. + * If undefined, then a plaintext connection will be used. + */ + Crt::Optional<Crt::Io::TlsContextOptions> m_tlsConnectionOptions; + + /** + * Configures (tunneling) HTTP proxy usage when establishing MQTT connections + */ + Crt::Optional<Crt::Http::HttpClientConnectionProxyOptions> m_proxyOptions; + + /** + * Websocket related options. The clinet with use websocket for connection when set. + */ + Crt::Optional<WebsocketConfig> m_websocketConfig; + + /** + * Custom Authorizer Configuration + */ + Crt::Optional<Mqtt5CustomAuthConfig> m_customAuthConfig; + + /** + * All configurable options with respect to the CONNECT packet sent by the client, including the will. + * These connect properties will be used for every connection attempt made by the client. + */ + std::shared_ptr<ConnectPacket> m_connectOptions; + + Crt::Mqtt5::Mqtt5ClientOptions *m_options; + + /* Error */ + int m_lastError; + + bool m_enableMetricsCollection; + + Crt::String m_sdkName = "CPPv2"; + Crt::String m_sdkVersion = AWS_CRT_CPP_VERSION; + }; + + } // namespace Iot +} // namespace Aws + +#endif // !BYO_CRYPTO diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/iot/MqttClient.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/iot/MqttClient.h new file mode 100644 index 0000000000..7fc2d60e8a --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/iot/MqttClient.h @@ -0,0 +1,450 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/Config.h> +#include <aws/crt/Exports.h> +#include <aws/crt/auth/Sigv4Signing.h> +#include <aws/crt/mqtt/MqttClient.h> +#include <aws/iot/MqttCommon.h> + +#if !BYO_CRYPTO + +namespace Aws +{ + namespace Iot + { + class MqttClient; + + /** + * Represents a unique configuration for connecting to a single AWS IoT endpoint. You can use a single instance + * of this class PER endpoint you want to connect to. This object must live through the lifetime of your + * connection. + */ + class AWS_CRT_CPP_API MqttClientConnectionConfig final + { + public: + static MqttClientConnectionConfig CreateInvalid(int lastError) noexcept; + + /** + * Creates a client configuration for use with making new AWS Iot specific MQTT Connections with MTLS. + * + * @param endpoint endpoint to connect to + * @param port port to connect to + * @param socketOptions socket options to use when establishing the connection + * @param tlsContext tls context that should be used for all connections sourced from this config + */ + MqttClientConnectionConfig( + const Crt::String &endpoint, + uint16_t port, + const Crt::Io::SocketOptions &socketOptions, + Crt::Io::TlsContext &&tlsContext); + + /** + * Creates a client configuration for use with making new AWS Iot specific MQTT Connections with web + * sockets. interceptor: a callback invoked during web socket handshake giving you the opportunity to mutate + * the request for authorization/signing purposes. If not specified, it's assumed you don't need to sign the + * request. proxyOptions: optional, if you want to use a proxy with websockets, specify the configuration + * options here. + * + * If proxy options are used, the tlsContext is applied to the connection to the remote endpoint, NOT the + * proxy. To make a tls connection to the proxy itself, you'll want to specify tls options in proxyOptions. + * + * @param endpoint endpoint to connect to + * @param port port to connect to + * @param socketOptions socket options to use when establishing the connection + * @param tlsContext tls context that should be used for all connections sourced from this config + * @param interceptor websocket upgrade handshake transformation function + * @param proxyOptions proxy configuration options + */ + MqttClientConnectionConfig( + const Crt::String &endpoint, + uint16_t port, + const Crt::Io::SocketOptions &socketOptions, + Crt::Io::TlsContext &&tlsContext, + Crt::Mqtt::OnWebSocketHandshakeIntercept &&interceptor, + const Crt::Optional<Crt::Http::HttpClientConnectionProxyOptions> &proxyOptions); + + /** + * @return true if the instance is in a valid state, false otherwise. + */ + explicit operator bool() const noexcept { return m_context ? true : false; } + + /** + * @return the value of the last aws error encountered by operations on this instance. + */ + int LastError() const noexcept { return m_lastError; } + + private: + MqttClientConnectionConfig(int lastError) noexcept; + + MqttClientConnectionConfig( + const Crt::String &endpoint, + uint16_t port, + const Crt::Io::SocketOptions &socketOptions, + Crt::Io::TlsContext &&tlsContext, + const Crt::Optional<Crt::Http::HttpClientConnectionProxyOptions> &proxyOptions); + + Crt::String m_endpoint; + uint16_t m_port; + Crt::Io::TlsContext m_context; + Crt::Io::SocketOptions m_socketOptions; + Crt::Mqtt::OnWebSocketHandshakeIntercept m_webSocketInterceptor; + Crt::String m_username; + Crt::String m_password; + Crt::Optional<Crt::Http::HttpClientConnectionProxyOptions> m_proxyOptions; + int m_lastError; + + friend class MqttClient; + friend class MqttClientConnectionConfigBuilder; + }; + + /** + * Represents configuration parameters for building a MqttClientConnectionConfig object. You can use a single + * instance of this class PER MqttClientConnectionConfig you want to generate. If you want to generate a config + * for a different endpoint or port etc... you need a new instance of this class. + */ + class AWS_CRT_CPP_API MqttClientConnectionConfigBuilder final + { + public: + MqttClientConnectionConfigBuilder(); + + /** + * Sets the builder up for MTLS using certPath and pkeyPath. These are files on disk and must be in the PEM + * format. + * + * @param certPath path to the X509 certificate (pem file) to use + * @param pkeyPath path to the private key (pem file) to use + * @param allocator memory allocator to use + */ + MqttClientConnectionConfigBuilder( + const char *certPath, + const char *pkeyPath, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Sets the builder up for MTLS using cert and pkey. These are in-memory buffers and must be in the PEM + * format. + * + * @param cert buffer containing the X509 certificate in a PEM format + * @param pkey buffer containing the private key in a PEM format + * @param allocator memory allocator to use + */ + MqttClientConnectionConfigBuilder( + const Crt::ByteCursor &cert, + const Crt::ByteCursor &pkey, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Sets the builder up for 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 + */ + MqttClientConnectionConfigBuilder( + const Crt::Io::TlsContextPkcs11Options &pkcs11Options, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Sets the builder up for MTLS, using a 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 memory allocator to use + */ + MqttClientConnectionConfigBuilder( + const char *windowsCertStorePath, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Sets the builder up for Websocket connection. + * + * @param config websocket configuration information + * @param allocator memory allocator to use + */ + MqttClientConnectionConfigBuilder( + const WebsocketConfig &config, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Creates a new builder with default Tls options. This requires setting the connection details manually. + * + * @return a new builder with default Tls options + */ + static MqttClientConnectionConfigBuilder NewDefaultBuilder() noexcept; + + /** + * Sets endpoint to connect to. + * + * @param endpoint endpoint to connect to + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithEndpoint(const Crt::String &endpoint); + + /** + * Sets endpoint to connect to. + * + * @param endpoint endpoint to connect to + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithEndpoint(Crt::String &&endpoint); + + /** + * Overrides the default port. By default, if ALPN is supported, 443 will be used. Otherwise 8883 will be + * used. If you specify 443 and ALPN is not supported, we will still attempt to connect over 443 without + * ALPN. + * + * @param port port to connect to + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithPortOverride(uint16_t port) noexcept; + + /** + * Sets the certificate authority for the endpoint you're connecting to. This is a path to a file on disk + * and must be in PEM format. + * + * @param caPath path to the CA file in PEM format + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithCertificateAuthority(const char *caPath) noexcept; + + /** + * Sets the certificate authority for the endpoint you're connecting to. This is an in-memory buffer and + * must be in PEM format. + * + * @param cert buffer containing the CA certificate in a PEM format + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithCertificateAuthority(const Crt::ByteCursor &cert) noexcept; + + /** + * TCP option: Enables TCP keep alive. Defaults to off. + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithTcpKeepAlive() noexcept; + + /** + * TCP option: Sets the connect timeout. Defaults to 3 seconds. + * + * @param connectTimeoutMs socket connection timeout + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithTcpConnectTimeout(uint32_t connectTimeoutMs) noexcept; + + /** + * TCP option: Sets time before keep alive probes are sent. Defaults to kernel defaults + * + * @param keepAliveTimeoutSecs time interval of no activity, in seconds, before keep alive probes + * get sent + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithTcpKeepAliveTimeout(uint16_t keepAliveTimeoutSecs) noexcept; + + /** + * TCP option: Sets the frequency of sending keep alive probes in seconds once the keep alive timeout + * expires. Defaults to kernel defaults. + * + * @param keepAliveIntervalSecs the frequency of sending keep alive probes in seconds once the keep alive + * timeout expires + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithTcpKeepAliveInterval(uint16_t keepAliveIntervalSecs) noexcept; + + /** + * TCP option: Sets the amount of keep alive probes allowed to fail before the connection is terminated. + * Defaults to kernel defaults. + * + * @param maxProbes the amount of keep alive probes allowed to fail before the connection is terminated + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithTcpKeepAliveMaxProbes(uint16_t maxProbes) noexcept; + + /** + * Sets the minimum tls version that is acceptable for connection establishment + * + * @param minimumTlsVersion minimum tls version allowed in client connections + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithMinimumTlsVersion(aws_tls_versions minimumTlsVersion) noexcept; + + /** + * Sets http proxy options. + * + * @param proxyOptions proxy configuration options for connection establishment + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithHttpProxyOptions( + const Crt::Http::HttpClientConnectionProxyOptions &proxyOptions) noexcept; + + /** + * Whether to send the SDK name and version number in the MQTT CONNECT packet. + * Default is True. + * + * @param enabled true to send SDK version/name in the connect for metrics gathering purposes + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithMetricsCollection(bool enabled); + + /** + * Overrides the default SDK Name to send as a metric in the MQTT CONNECT packet. + * + * @param sdkName string to use as the SDK name parameter in the connection string + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithSdkName(const Crt::String &sdkName); + + /** + * Overrides the default SDK Version to send as a metric in the MQTT CONNECT packet. + * + * @param sdkVersion string to use as the SDK version parameter in the connection string + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithSdkVersion(const Crt::String &sdkVersion); + + /** + * Sets the custom authorizer settings. This function will modify the username, port, and TLS options. + * + * @param username The username to use with the custom authorizer. If an empty string is passed, it will + * check to see if a username has already been set (via WithUsername function). If no + * username is set then no username will be passed with the MQTT connection. + * @param authorizerName The name of the custom authorizer. If an empty string is passed, then + * 'x-amz-customauthorizer-name' will not be added with the MQTT connection. + * @param authorizerSignature The signature of the custom authorizer. If an empty string is passed, then + * 'x-amz-customauthorizer-signature' will not be added with the MQTT connection. + * @param password The password to use with the custom authorizer. If null is passed, then no password will + * be set. + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithCustomAuthorizer( + const Crt::String &username, + const Crt::String &authorizerName, + const Crt::String &authorizerSignature, + const Crt::String &password) noexcept; + + /** + * Sets username for the connection + * + * @param username the username that will be passed with the MQTT connection + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithUsername(const Crt::String &username) noexcept; + + /** + * Sets password for the connection + * + * @param password the password that will be passed with the MQTT connection + * + * @return this builder object + */ + MqttClientConnectionConfigBuilder &WithPassword(const Crt::String &password) noexcept; + + /** + * Builds a client configuration object from the set options. + * + * @return a new client connection config instance + */ + MqttClientConnectionConfig Build() noexcept; + + /** + * @return true if the instance is in a valid state, false otherwise. + */ + explicit operator bool() const noexcept { return m_lastError == 0; } + + /** + * @return the value of the last aws error encountered by operations on this instance. + */ + int LastError() const noexcept { return m_lastError ? m_lastError : AWS_ERROR_UNKNOWN; } + + private: + // Common setup shared by all valid constructors + MqttClientConnectionConfigBuilder(Crt::Allocator *allocator) noexcept; + + // Helper function to add parameters to the username in the WithCustomAuthorizer function + Crt::String AddToUsernameParameter( + Crt::String currentUsername, + Crt::String parameterValue, + Crt::String parameterPreText); + + Crt::Allocator *m_allocator; + Crt::String m_endpoint; + uint16_t m_portOverride; + Crt::Io::SocketOptions m_socketOptions; + Crt::Io::TlsContextOptions m_contextOptions; + Crt::Optional<WebsocketConfig> m_websocketConfig; + Crt::Optional<Crt::Http::HttpClientConnectionProxyOptions> m_proxyOptions; + bool m_enableMetricsCollection = true; + Crt::String m_sdkName = "CPPv2"; + Crt::String m_sdkVersion; + Crt::String m_username = ""; + Crt::String m_password = ""; + bool m_isUsingCustomAuthorizer = false; + + int m_lastError; + }; + + /** + * AWS IOT specific Mqtt Client. Sets defaults for using the AWS IOT service. You'll need an instance of + * MqttClientConnectionConfig to use. Once NewConnection returns, you use it's return value identically + * to how you would use Aws::Crt::Mqtt::MqttConnection + */ + class AWS_CRT_CPP_API MqttClient final + { + public: + MqttClient(Crt::Io::ClientBootstrap &bootstrap, Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Constructs a new Mqtt Client object using the static default ClientBootstrap. + * + * For more information on the default ClientBootstrap see + * Aws::Crt::ApiHandle::GetOrCreateDefaultClientBootstrap + */ + MqttClient(Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Creates a new mqtt connection from a connection configuration object + * @param config mqtt connection configuration + * @return a new mqtt connection + */ + std::shared_ptr<Crt::Mqtt::MqttConnection> NewConnection(const MqttClientConnectionConfig &config) noexcept; + + /** + * @return the value of the last aws error encountered by operations on this instance. + */ + int LastError() const noexcept { return m_client.LastError(); } + + /** + * @return true if the instance is in a valid state, false otherwise. + */ + explicit operator bool() const noexcept { return m_client ? true : false; } + + private: + Crt::Mqtt::MqttClient m_client; + int m_lastError; + }; + } // namespace Iot +} // namespace Aws + +#endif // !BYO_CRYPTO diff --git a/contrib/restricted/aws/aws-crt-cpp/include/aws/iot/MqttCommon.h b/contrib/restricted/aws/aws-crt-cpp/include/aws/iot/MqttCommon.h new file mode 100644 index 0000000000..80c4f14084 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/include/aws/iot/MqttCommon.h @@ -0,0 +1,103 @@ +#pragma once +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/Config.h> +#include <aws/crt/Exports.h> +#include <aws/crt/auth/Sigv4Signing.h> +#include <aws/crt/mqtt/MqttClient.h> + +#if !BYO_CRYPTO + +namespace Aws +{ + namespace Iot + { + + using CreateSigningConfig = std::function<std::shared_ptr<Crt::Auth::ISigningConfig>(void)>; + + /** + * Class encapsulating configuration for establishing an Aws IoT mqtt connection via websockets + */ + struct AWS_CRT_CPP_API WebsocketConfig + { + /** + * Create a websocket configuration for use with the default credentials provider chain. Signing region + * will be used for Sigv4 signature calculations. + * + * @param signingRegion Aws region that is being connected to. Required in order to properly sign the + * handshake upgrade request + * @param bootstrap client bootstrap to establish any connections needed by the default credentials + * provider chain which will get built for the user + * @param allocator memory allocator to use + */ + WebsocketConfig( + const Crt::String &signingRegion, + Crt::Io::ClientBootstrap *bootstrap, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Create a websocket configuration for use with the default credentials provider chain and default + * ClientBootstrap. Signing region will be used for Sigv4 signature calculations. + * + * For more information on the default ClientBootstrap see + * Aws::Crt::ApiHandle::GetOrCreateDefaultClientBootstrap + * + * @param signingRegion Aws region that is being connected to. Required in order to properly sign the + * handshake upgrade request + * @param allocator memory allocator to use + */ + WebsocketConfig(const Crt::String &signingRegion, Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Create a websocket configuration for use with a custom credentials provider. Signing region will be used + * for Sigv4 signature calculations. + * + * @param signingRegion Aws region that is being connected to. Required in order to properly sign the + * handshake upgrade request + * @param credentialsProvider credentials provider to source AWS credentials from + * @param allocator memory allocator to use + */ + WebsocketConfig( + const Crt::String &signingRegion, + const std::shared_ptr<Crt::Auth::ICredentialsProvider> &credentialsProvider, + Crt::Allocator *allocator = Crt::ApiAllocator()) noexcept; + + /** + * Create a websocket configuration for use with a custom credentials provider, and a custom signer. + * + * You'll need to provide a function for use with creating a signing Config and pass it to + * createSigningConfig. + * + * This is useful for cases use with: + * https://docs.aws.amazon.com/iot/latest/developerguide/custom-auth.html + * + * @param credentialsProvider credentials provider + * @param signer HTTP request signer + * @param createSigningConfig function that creates a signing config + */ + WebsocketConfig( + const std::shared_ptr<Crt::Auth::ICredentialsProvider> &credentialsProvider, + const std::shared_ptr<Crt::Auth::IHttpRequestSigner> &signer, + CreateSigningConfig createSigningConfig) noexcept; + + std::shared_ptr<Crt::Auth::ICredentialsProvider> CredentialsProvider; + std::shared_ptr<Crt::Auth::IHttpRequestSigner> Signer; + CreateSigningConfig CreateSigningConfigCb; + + /** + * @deprecated Specify ProxyOptions to use a proxy with your websocket connection. + * + * If MqttClientConnectionConfigBuilder::m_proxyOptions is valid, then that will be used over + * this value. + */ + Crt::Optional<Crt::Http::HttpClientConnectionProxyOptions> ProxyOptions; + Crt::String SigningRegion; + Crt::String ServiceName; + }; + + } // namespace Iot +} // namespace Aws + +#endif // !BYO_CRYPTO diff --git a/contrib/restricted/aws/aws-crt-cpp/source/Allocator.cpp b/contrib/restricted/aws/aws-crt-cpp/source/Allocator.cpp new file mode 100644 index 0000000000..a27071ef5e --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/Allocator.cpp @@ -0,0 +1,21 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/Allocator.h> + +namespace Aws +{ + namespace Crt + { + + Allocator *DefaultAllocatorImplementation() noexcept { return aws_default_allocator(); } + + Allocator *DefaultAllocator() noexcept { return DefaultAllocatorImplementation(); } + + Allocator *g_allocator = Aws::Crt::DefaultAllocatorImplementation(); + + Allocator *ApiAllocator() noexcept { return g_allocator; } + + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/Api.cpp b/contrib/restricted/aws/aws-crt-cpp/source/Api.cpp new file mode 100644 index 0000000000..d7a7a03cf5 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/Api.cpp @@ -0,0 +1,405 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/Api.h> +#include <aws/crt/StlAllocator.h> +#include <aws/crt/external/cJSON.h> +#include <aws/crt/io/TlsOptions.h> + +#include <aws/auth/auth.h> +#include <aws/common/ref_count.h> +#include <aws/event-stream/event_stream.h> +#include <aws/http/http.h> +#include <aws/mqtt/mqtt.h> +#include <aws/s3/s3.h> + +#include <thread> + +namespace Aws +{ + namespace Crt + { + static Crypto::CreateHashCallback s_BYOCryptoNewMD5Callback; + static Crypto::CreateHashCallback s_BYOCryptoNewSHA256Callback; + static Crypto::CreateHMACCallback s_BYOCryptoNewSHA256HMACCallback; + static Io::NewClientTlsHandlerCallback s_BYOCryptoNewClientTlsHandlerCallback; + static Io::NewTlsContextImplCallback s_BYOCryptoNewTlsContextImplCallback; + static Io::DeleteTlsContextImplCallback s_BYOCryptoDeleteTlsContextImplCallback; + static Io::IsTlsAlpnSupportedCallback s_BYOCryptoIsTlsAlpnSupportedCallback; + + Io::ClientBootstrap *ApiHandle::s_static_bootstrap = nullptr; + Io::EventLoopGroup *ApiHandle::s_static_event_loop_group = nullptr; + int ApiHandle::s_host_resolver_default_max_hosts = 8; + Io::HostResolver *ApiHandle::s_static_default_host_resolver = nullptr; + std::mutex ApiHandle::s_lock_client_bootstrap; + std::mutex ApiHandle::s_lock_event_loop_group; + std::mutex ApiHandle::s_lock_default_host_resolver; + + static void *s_cJSONAlloc(size_t sz) { return aws_mem_acquire(ApiAllocator(), sz); } + + static void s_cJSONFree(void *ptr) { return aws_mem_release(ApiAllocator(), ptr); } + + static void s_initApi(Allocator *allocator) + { + // sets up the StlAllocator for use. + g_allocator = allocator; + aws_mqtt_library_init(allocator); + aws_s3_library_init(allocator); + aws_event_stream_library_init(allocator); + aws_sdkutils_library_init(allocator); + + cJSON_Hooks hooks; + hooks.malloc_fn = s_cJSONAlloc; + hooks.free_fn = s_cJSONFree; + cJSON_InitHooks(&hooks); + } + + ApiHandle::ApiHandle(Allocator *allocator) noexcept + : m_logger(), m_shutdownBehavior(ApiHandleShutdownBehavior::Blocking) + { + s_initApi(allocator); + } + + ApiHandle::ApiHandle() noexcept : m_logger(), m_shutdownBehavior(ApiHandleShutdownBehavior::Blocking) + { + s_initApi(DefaultAllocator()); + } + + ApiHandle::~ApiHandle() + { + ReleaseStaticDefaultClientBootstrap(); + ReleaseStaticDefaultEventLoopGroup(); + ReleaseStaticDefaultHostResolver(); + + if (m_shutdownBehavior == ApiHandleShutdownBehavior::Blocking) + { + aws_thread_join_all_managed(); + } + + if (aws_logger_get() == &m_logger) + { + aws_logger_set(NULL); + aws_logger_clean_up(&m_logger); + } + + g_allocator = nullptr; + aws_s3_library_clean_up(); + aws_mqtt_library_clean_up(); + aws_event_stream_library_clean_up(); + aws_sdkutils_library_clean_up(); + + s_BYOCryptoNewMD5Callback = nullptr; + s_BYOCryptoNewSHA256Callback = nullptr; + s_BYOCryptoNewSHA256HMACCallback = nullptr; + s_BYOCryptoNewClientTlsHandlerCallback = nullptr; + s_BYOCryptoNewTlsContextImplCallback = nullptr; + s_BYOCryptoDeleteTlsContextImplCallback = nullptr; + s_BYOCryptoIsTlsAlpnSupportedCallback = nullptr; + } + + void ApiHandle::InitializeLogging(Aws::Crt::LogLevel level, const char *filename) + { + struct aws_logger_standard_options options; + AWS_ZERO_STRUCT(options); + + options.level = (enum aws_log_level)level; + options.filename = filename; + + InitializeLoggingCommon(options); + } + + void ApiHandle::InitializeLogging(Aws::Crt::LogLevel level, FILE *fp) + { + struct aws_logger_standard_options options; + AWS_ZERO_STRUCT(options); + + options.level = (enum aws_log_level)level; + options.file = fp; + + InitializeLoggingCommon(options); + } + + void ApiHandle::InitializeLoggingCommon(struct aws_logger_standard_options &options) + { + if (aws_logger_get() == &m_logger) + { + aws_logger_set(NULL); + aws_logger_clean_up(&m_logger); + if (options.level == AWS_LL_NONE) + { + AWS_ZERO_STRUCT(m_logger); + return; + } + } + + if (aws_logger_init_standard(&m_logger, ApiAllocator(), &options)) + { + return; + } + + aws_logger_set(&m_logger); + } + + void ApiHandle::SetShutdownBehavior(ApiHandleShutdownBehavior behavior) { m_shutdownBehavior = behavior; } + +#if BYO_CRYPTO + static struct aws_hash *s_MD5New(struct aws_allocator *allocator) + { + if (!s_BYOCryptoNewMD5Callback) + { + AWS_LOGF_ERROR( + AWS_LS_IO_TLS, "Must call ApiHandle::SetBYOCryptoNewMD5Callback() before MD5 hash can be created"); + aws_raise_error(AWS_ERROR_UNIMPLEMENTED); + return nullptr; + } + + auto hash = s_BYOCryptoNewMD5Callback(AWS_MD5_LEN, allocator); + if (!hash) + { + return nullptr; + } + return hash->SeatForCInterop(hash); + } + + void ApiHandle::SetBYOCryptoNewMD5Callback(Crypto::CreateHashCallback &&callback) + { + s_BYOCryptoNewMD5Callback = std::move(callback); + aws_set_md5_new_fn(s_MD5New); + } + + static struct aws_hash *s_Sha256New(struct aws_allocator *allocator) + { + if (!s_BYOCryptoNewSHA256Callback) + { + AWS_LOGF_ERROR( + AWS_LS_IO_TLS, + "Must call ApiHandle::SetBYOCryptoNewSHA256Callback() before SHA256 hash can be created"); + aws_raise_error(AWS_ERROR_UNIMPLEMENTED); + return nullptr; + } + + auto hash = s_BYOCryptoNewSHA256Callback(AWS_SHA256_LEN, allocator); + if (!hash) + { + return nullptr; + } + return hash->SeatForCInterop(hash); + } + + void ApiHandle::SetBYOCryptoNewSHA256Callback(Crypto::CreateHashCallback &&callback) + { + s_BYOCryptoNewSHA256Callback = std::move(callback); + aws_set_sha256_new_fn(s_Sha256New); + } + + static struct aws_hmac *s_sha256HMACNew(struct aws_allocator *allocator, const struct aws_byte_cursor *secret) + { + if (!s_BYOCryptoNewSHA256HMACCallback) + { + AWS_LOGF_ERROR( + AWS_LS_IO_TLS, + "Must call ApiHandle::SetBYOCryptoNewSHA256HMACCallback() before SHA256 HMAC can be created"); + aws_raise_error(AWS_ERROR_UNIMPLEMENTED); + return nullptr; + } + + auto hmac = s_BYOCryptoNewSHA256HMACCallback(AWS_SHA256_HMAC_LEN, *secret, allocator); + if (!hmac) + { + return nullptr; + } + return hmac->SeatForCInterop(hmac); + } + + void ApiHandle::SetBYOCryptoNewSHA256HMACCallback(Crypto::CreateHMACCallback &&callback) + { + s_BYOCryptoNewSHA256HMACCallback = std::move(callback); + aws_set_sha256_hmac_new_fn(s_sha256HMACNew); + } + + static struct aws_channel_handler *s_NewClientTlsHandler( + struct aws_allocator *allocator, + struct aws_tls_connection_options *options, + struct aws_channel_slot *slot, + void *) + { + if (!s_BYOCryptoNewClientTlsHandlerCallback) + { + AWS_LOGF_ERROR( + AWS_LS_IO_TLS, + "Must call ApiHandle::SetBYOCryptoClientTlsCallback() before client TLS handler can be created"); + aws_raise_error(AWS_ERROR_UNIMPLEMENTED); + return nullptr; + } + + auto clientHandlerSelfReferencing = s_BYOCryptoNewClientTlsHandlerCallback(slot, *options, allocator); + if (!clientHandlerSelfReferencing) + { + return nullptr; + } + return clientHandlerSelfReferencing->SeatForCInterop(clientHandlerSelfReferencing); + } + + static int s_ClientTlsHandlerStartNegotiation(struct aws_channel_handler *handler, void *) + { + auto *clientHandler = reinterpret_cast<Io::ClientTlsChannelHandler *>(handler->impl); + if (clientHandler->ChannelsThreadIsCallersThread()) + { + clientHandler->StartNegotiation(); + } + else + { + clientHandler->ScheduleTask([clientHandler](Io::TaskStatus) { clientHandler->StartNegotiation(); }); + } + return AWS_OP_SUCCESS; + } + + void ApiHandle::SetBYOCryptoClientTlsCallback(Io::NewClientTlsHandlerCallback &&callback) + { + s_BYOCryptoNewClientTlsHandlerCallback = std::move(callback); + struct aws_tls_byo_crypto_setup_options setupOptions; + setupOptions.new_handler_fn = s_NewClientTlsHandler; + setupOptions.start_negotiation_fn = s_ClientTlsHandlerStartNegotiation; + setupOptions.user_data = nullptr; + aws_tls_byo_crypto_set_client_setup_options(&setupOptions); + } + + void ApiHandle::SetBYOCryptoTlsContextCallbacks( + Io::NewTlsContextImplCallback &&newCallback, + Io::DeleteTlsContextImplCallback &&deleteCallback, + Io::IsTlsAlpnSupportedCallback &&alpnCallback) + { + s_BYOCryptoNewTlsContextImplCallback = newCallback; + s_BYOCryptoDeleteTlsContextImplCallback = deleteCallback; + s_BYOCryptoIsTlsAlpnSupportedCallback = alpnCallback; + } + +#else // BYO_CRYPTO + void ApiHandle::SetBYOCryptoNewMD5Callback(Crypto::CreateHashCallback &&) + { + AWS_LOGF_WARN(AWS_LS_IO_TLS, "SetBYOCryptoNewMD5Callback() has no effect unless compiled with BYO_CRYPTO"); + } + + void ApiHandle::SetBYOCryptoNewSHA256Callback(Crypto::CreateHashCallback &&) + { + AWS_LOGF_WARN( + AWS_LS_IO_TLS, "SetBYOCryptoNewSHA256Callback() has no effect unless compiled with BYO_CRYPTO"); + } + + void ApiHandle::SetBYOCryptoNewSHA256HMACCallback(Crypto::CreateHMACCallback &&) + { + AWS_LOGF_WARN( + AWS_LS_IO_TLS, "SetBYOCryptoNewSHA256HMACCallback() has no effect unless compiled with BYO_CRYPTO"); + } + + void ApiHandle::SetBYOCryptoClientTlsCallback(Io::NewClientTlsHandlerCallback &&) + { + AWS_LOGF_WARN( + AWS_LS_IO_TLS, "SetBYOCryptoClientTlsCallback() has no effect unless compiled with BYO_CRYPTO"); + } + + void ApiHandle::SetBYOCryptoTlsContextCallbacks( + Io::NewTlsContextImplCallback &&, + Io::DeleteTlsContextImplCallback &&, + Io::IsTlsAlpnSupportedCallback &&) + { + AWS_LOGF_WARN( + AWS_LS_IO_TLS, "SetBYOCryptoClientTlsCallback() has no effect unless compiled with BYO_CRYPTO"); + } +#endif // BYO_CRYPTO + + Io::ClientBootstrap *ApiHandle::GetOrCreateStaticDefaultClientBootstrap() + { + std::lock_guard<std::mutex> lock(s_lock_client_bootstrap); + if (s_static_bootstrap == nullptr) + { + s_static_bootstrap = Aws::Crt::New<Io::ClientBootstrap>( + ApiAllocator(), *GetOrCreateStaticDefaultEventLoopGroup(), *GetOrCreateStaticDefaultHostResolver()); + } + return s_static_bootstrap; + } + + Io::EventLoopGroup *ApiHandle::GetOrCreateStaticDefaultEventLoopGroup() + { + std::lock_guard<std::mutex> lock(s_lock_event_loop_group); + if (s_static_event_loop_group == nullptr) + { + s_static_event_loop_group = Aws::Crt::New<Io::EventLoopGroup>(ApiAllocator(), (uint16_t)0); + } + return s_static_event_loop_group; + } + + Io::HostResolver *ApiHandle::GetOrCreateStaticDefaultHostResolver() + { + std::lock_guard<std::mutex> lock(s_lock_default_host_resolver); + if (s_static_default_host_resolver == nullptr) + { + s_static_default_host_resolver = Aws::Crt::New<Io::DefaultHostResolver>( + ApiAllocator(), *GetOrCreateStaticDefaultEventLoopGroup(), 1, s_host_resolver_default_max_hosts); + } + return s_static_default_host_resolver; + } + + void ApiHandle::ReleaseStaticDefaultClientBootstrap() + { + std::lock_guard<std::mutex> lock(s_lock_client_bootstrap); + if (s_static_bootstrap != nullptr) + { + Aws::Crt::Delete(s_static_bootstrap, ApiAllocator()); + s_static_bootstrap = nullptr; + } + } + + void ApiHandle::ReleaseStaticDefaultEventLoopGroup() + { + std::lock_guard<std::mutex> lock(s_lock_event_loop_group); + if (s_static_event_loop_group != nullptr) + { + Aws::Crt::Delete(s_static_event_loop_group, ApiAllocator()); + s_static_event_loop_group = nullptr; + } + } + + void ApiHandle::ReleaseStaticDefaultHostResolver() + { + std::lock_guard<std::mutex> lock(s_lock_default_host_resolver); + if (s_static_default_host_resolver != nullptr) + { + Aws::Crt::Delete(s_static_default_host_resolver, ApiAllocator()); + s_static_default_host_resolver = nullptr; + } + } + + const Io::NewTlsContextImplCallback &ApiHandle::GetBYOCryptoNewTlsContextImplCallback() + { + return s_BYOCryptoNewTlsContextImplCallback; + } + + const Io::DeleteTlsContextImplCallback &ApiHandle::GetBYOCryptoDeleteTlsContextImplCallback() + { + return s_BYOCryptoDeleteTlsContextImplCallback; + } + + const Io::IsTlsAlpnSupportedCallback &ApiHandle::GetBYOCryptoIsTlsAlpnSupportedCallback() + { + return s_BYOCryptoIsTlsAlpnSupportedCallback; + } + + const char *ErrorDebugString(int error) noexcept { return aws_error_debug_str(error); } + + int LastError() noexcept { return aws_last_error(); } + + int LastErrorOrUnknown() noexcept + { + int last_error = aws_last_error(); + if (last_error == AWS_ERROR_SUCCESS) + { + last_error = AWS_ERROR_UNKNOWN; + } + + return last_error; + } + + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/DateTime.cpp b/contrib/restricted/aws/aws-crt-cpp/source/DateTime.cpp new file mode 100644 index 0000000000..8e550cc91f --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/DateTime.cpp @@ -0,0 +1,200 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/DateTime.h> + +#include <chrono> + +namespace Aws +{ + namespace Crt + { + DateTime::DateTime() noexcept : m_good(true) + { + std::chrono::system_clock::time_point time; + aws_date_time_init_epoch_millis( + &m_date_time, + static_cast<uint64_t>( + std::chrono::duration_cast<std::chrono::milliseconds>(time.time_since_epoch()).count())); + } + + DateTime::DateTime(const std::chrono::system_clock::time_point &timepointToAssign) noexcept : m_good(true) + { + aws_date_time_init_epoch_millis( + &m_date_time, + static_cast<uint64_t>( + std::chrono::duration_cast<std::chrono::milliseconds>(timepointToAssign.time_since_epoch()) + .count())); + } + + DateTime::DateTime(uint64_t millisSinceEpoch) noexcept : m_good(true) + { + aws_date_time_init_epoch_millis(&m_date_time, millisSinceEpoch); + } + + DateTime::DateTime(double epoch_millis) noexcept : m_good(true) + { + aws_date_time_init_epoch_secs(&m_date_time, epoch_millis); + } + + DateTime::DateTime(const char *timestamp, DateFormat format) noexcept + { + ByteBuf timeStampBuf = ByteBufFromCString(timestamp); + + m_good = + (aws_date_time_init_from_str(&m_date_time, &timeStampBuf, static_cast<aws_date_format>(format)) == + AWS_ERROR_SUCCESS); + } + + bool DateTime::operator==(const DateTime &other) const noexcept + { + return aws_date_time_diff(&m_date_time, &other.m_date_time) == 0; + } + + bool DateTime::operator<(const DateTime &other) const noexcept + { + return aws_date_time_diff(&m_date_time, &other.m_date_time) < 0; + } + + bool DateTime::operator>(const DateTime &other) const noexcept + { + return aws_date_time_diff(&m_date_time, &other.m_date_time) > 0; + } + + bool DateTime::operator!=(const DateTime &other) const noexcept { return !(*this == other); } + + bool DateTime::operator<=(const DateTime &other) const noexcept + { + return aws_date_time_diff(&m_date_time, &other.m_date_time) <= 0; + } + + bool DateTime::operator>=(const DateTime &other) const noexcept + { + return aws_date_time_diff(&m_date_time, &other.m_date_time) >= 0; + } + + DateTime DateTime::operator+(const std::chrono::milliseconds &a) const noexcept + { + auto currentTime = aws_date_time_as_millis(&m_date_time); + currentTime += a.count(); + return {currentTime}; + } + + DateTime DateTime::operator-(const std::chrono::milliseconds &a) const noexcept + { + auto currentTime = aws_date_time_as_millis(&m_date_time); + currentTime -= a.count(); + return {currentTime}; + } + + DateTime &DateTime::operator=(double secondsSinceEpoch) noexcept + { + aws_date_time_init_epoch_secs(&m_date_time, secondsSinceEpoch); + m_good = true; + return *this; + } + + DateTime &DateTime::operator=(uint64_t millisSinceEpoch) noexcept + { + aws_date_time_init_epoch_millis(&m_date_time, millisSinceEpoch); + m_good = true; + return *this; + } + + DateTime &DateTime::operator=(const std::chrono::system_clock::time_point &timepointToAssign) noexcept + { + aws_date_time_init_epoch_millis( + &m_date_time, + static_cast<uint64_t>( + std::chrono::duration_cast<std::chrono::milliseconds>(timepointToAssign.time_since_epoch()) + .count())); + m_good = true; + return *this; + } + + DateTime &DateTime::operator=(const char *timestamp) noexcept + { + ByteBuf timeStampBuf = aws_byte_buf_from_c_str(timestamp); + + m_good = aws_date_time_init_from_str( + &m_date_time, &timeStampBuf, static_cast<aws_date_format>(DateFormat::AutoDetect)) == + AWS_ERROR_SUCCESS; + return *this; + } + + DateTime::operator bool() const noexcept { return m_good; } + + int DateTime::GetLastError() const noexcept { return aws_last_error(); } + + bool DateTime::ToLocalTimeString(DateFormat format, ByteBuf &outputBuf) const noexcept + { + return ( + aws_date_time_to_local_time_str(&m_date_time, static_cast<aws_date_format>(format), &outputBuf) == + AWS_ERROR_SUCCESS); + } + + bool DateTime::ToGmtString(DateFormat format, ByteBuf &outputBuf) const noexcept + { + return ( + aws_date_time_to_utc_time_str(&m_date_time, static_cast<aws_date_format>(format), &outputBuf) == + AWS_ERROR_SUCCESS); + } + + double DateTime::SecondsWithMSPrecision() const noexcept { return aws_date_time_as_epoch_secs(&m_date_time); } + + uint64_t DateTime::Millis() const noexcept { return aws_date_time_as_millis(&m_date_time); } + + std::chrono::system_clock::time_point DateTime::UnderlyingTimestamp() const noexcept + { + return std::chrono::system_clock::from_time_t(m_date_time.timestamp); + } + + uint16_t DateTime::GetYear(bool localTime) const noexcept + { + return aws_date_time_year(&m_date_time, localTime); + } + + Month DateTime::GetMonth(bool localTime) const noexcept + { + return static_cast<Month>(aws_date_time_month(&m_date_time, localTime)); + } + + uint8_t DateTime::GetDay(bool localTime) const noexcept + { + return aws_date_time_month_day(&m_date_time, localTime); + } + + DayOfWeek DateTime::GetDayOfWeek(bool localTime) const noexcept + { + return static_cast<DayOfWeek>(aws_date_time_day_of_week(&m_date_time, localTime)); + } + + uint8_t DateTime::GetHour(bool localTime) const noexcept { return aws_date_time_hour(&m_date_time, localTime); } + + uint8_t DateTime::GetMinute(bool localTime) const noexcept + { + return aws_date_time_minute(&m_date_time, localTime); + } + + uint8_t DateTime::GetSecond(bool localTime) const noexcept + { + return aws_date_time_second(&m_date_time, localTime); + } + + bool DateTime::IsDST(bool localTime) const noexcept { return aws_date_time_dst(&m_date_time, localTime); } + + DateTime DateTime::Now() noexcept + { + DateTime dateTime; + aws_date_time_init_now(&dateTime.m_date_time); + return dateTime; + } + + std::chrono::milliseconds DateTime::operator-(const DateTime &other) const noexcept + { + auto diff = aws_date_time_diff(&m_date_time, &other.m_date_time); + return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::seconds(diff)); + } + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/ImdsClient.cpp b/contrib/restricted/aws/aws-crt-cpp/source/ImdsClient.cpp new file mode 100644 index 0000000000..cb9b1f9431 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/ImdsClient.cpp @@ -0,0 +1,457 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/auth/aws_imds_client.h> +#include <aws/auth/credentials.h> +#include <aws/crt/Api.h> +#include <aws/crt/ImdsClient.h> +#include <aws/crt/auth/Credentials.h> +#include <aws/crt/http/HttpConnection.h> +#include <aws/crt/io/Bootstrap.h> + +namespace Aws +{ + namespace Crt + { + namespace Imds + { + IamProfile::IamProfile(const IamProfileView &other) + : lastUpdated(other.lastUpdated), + instanceProfileArn(other.instanceProfileArn.data(), other.instanceProfileArn.size()), + instanceProfileId(other.instanceProfileId.data(), other.instanceProfileId.size()) + { + } + + IamProfile &IamProfile::operator=(const IamProfileView &other) + { + lastUpdated = other.lastUpdated; + instanceProfileArn = String(other.instanceProfileArn.data(), other.instanceProfileArn.size()); + instanceProfileId = String(other.instanceProfileId.data(), other.instanceProfileId.size()); + return *this; + } + + InstanceInfo::InstanceInfo(const InstanceInfoView &other) + : availabilityZone(other.availabilityZone.data(), other.availabilityZone.size()), + privateIp(other.privateIp.data(), other.privateIp.size()), + version(other.version.data(), other.version.size()), + instanceId(other.instanceId.data(), other.instanceId.size()), + instanceType(other.instanceType.data(), other.instanceType.size()), + accountId(other.accountId.data(), other.accountId.size()), + imageId(other.imageId.data(), other.imageId.size()), pendingTime(other.pendingTime), + architecture(other.architecture.data(), other.architecture.size()), + kernelId(other.kernelId.data(), other.kernelId.size()), + ramdiskId(other.ramdiskId.data(), other.ramdiskId.size()), + region(other.region.data(), other.region.size()) + { + for (const auto &m : other.marketplaceProductCodes) + { + marketplaceProductCodes.emplace_back(m.data(), m.size()); + } + + for (const auto &m : other.billingProducts) + { + billingProducts.emplace_back(m.data(), m.size()); + } + } + + InstanceInfo &InstanceInfo::operator=(const InstanceInfoView &other) + { + availabilityZone = {other.availabilityZone.data(), other.availabilityZone.size()}; + privateIp = {other.privateIp.data(), other.privateIp.size()}; + version = {other.version.data(), other.version.size()}; + instanceId = {other.instanceId.data(), other.instanceId.size()}; + instanceType = {other.instanceType.data(), other.instanceType.size()}; + accountId = {other.accountId.data(), other.accountId.size()}; + imageId = {other.imageId.data(), other.imageId.size()}; + pendingTime = other.pendingTime; + architecture = {other.architecture.data(), other.architecture.size()}; + kernelId = {other.kernelId.data(), other.kernelId.size()}; + ramdiskId = {other.ramdiskId.data(), other.ramdiskId.size()}; + region = {other.region.data(), other.region.size()}; + + for (const auto &m : other.marketplaceProductCodes) + { + marketplaceProductCodes.emplace_back(m.data(), m.size()); + } + + for (const auto &m : other.billingProducts) + { + billingProducts.emplace_back(m.data(), m.size()); + } + return *this; + } + + ImdsClient::ImdsClient(const ImdsClientConfig &config, Allocator *allocator) noexcept + { + struct aws_imds_client_options raw_config; + AWS_ZERO_STRUCT(raw_config); + if (config.Bootstrap != nullptr) + { + raw_config.bootstrap = config.Bootstrap->GetUnderlyingHandle(); + } + else + { + raw_config.bootstrap = ApiHandle::GetOrCreateStaticDefaultClientBootstrap()->GetUnderlyingHandle(); + } + + m_client = aws_imds_client_new(allocator, &raw_config); + m_allocator = allocator; + } + + ImdsClient::~ImdsClient() + { + if (m_client) + { + aws_imds_client_release(m_client); + m_client = nullptr; + } + } + + template <typename T> struct WrappedCallbackArgs + { + WrappedCallbackArgs(Allocator *allocator, T callback, void *userData) + : allocator(allocator), callback(callback), userData(userData) + { + } + Allocator *allocator; + T callback; + void *userData; + }; + + void ImdsClient::s_onResourceAcquired(const aws_byte_buf *resource, int errorCode, void *userData) + { + WrappedCallbackArgs<OnResourceAcquired> *callbackArgs = + static_cast<WrappedCallbackArgs<OnResourceAcquired> *>(userData); + callbackArgs->callback( + ByteCursorToStringView(aws_byte_cursor_from_buf(resource)), errorCode, callbackArgs->userData); + Aws::Crt::Delete(callbackArgs, callbackArgs->allocator); + } + + void ImdsClient::s_onVectorResourceAcquired(const aws_array_list *array, int errorCode, void *userData) + { + WrappedCallbackArgs<OnVectorResourceAcquired> *callbackArgs = + static_cast<WrappedCallbackArgs<OnVectorResourceAcquired> *>(userData); + callbackArgs->callback( + ArrayListToVector<ByteCursor, StringView>(array, ByteCursorToStringView), + errorCode, + callbackArgs->userData); + Aws::Crt::Delete(callbackArgs, callbackArgs->allocator); + } + + void ImdsClient::s_onCredentialsAcquired(const aws_credentials *credentials, int errorCode, void *userData) + { + WrappedCallbackArgs<OnCredentialsAcquired> *callbackArgs = + static_cast<WrappedCallbackArgs<OnCredentialsAcquired> *>(userData); + auto credentialsPtr = Aws::Crt::MakeShared<Auth::Credentials>(callbackArgs->allocator, credentials); + callbackArgs->callback(credentials, errorCode, callbackArgs->userData); + Aws::Crt::Delete(callbackArgs, callbackArgs->allocator); + } + + void ImdsClient::s_onIamProfileAcquired( + const aws_imds_iam_profile *iamProfileInfo, + int errorCode, + void *userData) + { + WrappedCallbackArgs<OnIamProfileAcquired> *callbackArgs = + static_cast<WrappedCallbackArgs<OnIamProfileAcquired> *>(userData); + IamProfileView iamProfile; + iamProfile.lastUpdated = aws_date_time_as_epoch_secs(&(iamProfileInfo->last_updated)); + iamProfile.instanceProfileArn = ByteCursorToStringView(iamProfileInfo->instance_profile_arn); + iamProfile.instanceProfileId = ByteCursorToStringView(iamProfileInfo->instance_profile_id); + callbackArgs->callback(iamProfile, errorCode, callbackArgs->userData); + Aws::Crt::Delete(callbackArgs, callbackArgs->allocator); + } + + void ImdsClient::s_onInstanceInfoAcquired( + const aws_imds_instance_info *instanceInfo, + int errorCode, + void *userData) + { + WrappedCallbackArgs<OnInstanceInfoAcquired> *callbackArgs = + static_cast<WrappedCallbackArgs<OnInstanceInfoAcquired> *>(userData); + InstanceInfoView info; + info.marketplaceProductCodes = ArrayListToVector<ByteCursor, StringView>( + &(instanceInfo->marketplace_product_codes), ByteCursorToStringView); + info.availabilityZone = ByteCursorToStringView(instanceInfo->availability_zone); + info.privateIp = ByteCursorToStringView(instanceInfo->private_ip); + info.version = ByteCursorToStringView(instanceInfo->version); + info.instanceId = ByteCursorToStringView(instanceInfo->instance_id); + info.billingProducts = ArrayListToVector<ByteCursor, StringView>( + &(instanceInfo->billing_products), ByteCursorToStringView); + info.instanceType = ByteCursorToStringView(instanceInfo->instance_type); + info.accountId = ByteCursorToStringView(instanceInfo->account_id); + info.imageId = ByteCursorToStringView(instanceInfo->image_id); + info.pendingTime = aws_date_time_as_epoch_secs(&(instanceInfo->pending_time)); + info.architecture = ByteCursorToStringView(instanceInfo->architecture); + info.kernelId = ByteCursorToStringView(instanceInfo->kernel_id); + info.ramdiskId = ByteCursorToStringView(instanceInfo->ramdisk_id); + info.region = ByteCursorToStringView(instanceInfo->region); + callbackArgs->callback(info, errorCode, callbackArgs->userData); + Aws::Crt::Delete(callbackArgs, callbackArgs->allocator); + } + + int ImdsClient::GetResource(const StringView &resourcePath, OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + + return aws_imds_client_get_resource_async( + m_client, StringViewToByteCursor(resourcePath), s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetAmiId(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_ami_id(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetAmiLaunchIndex(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_ami_launch_index(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetAmiManifestPath(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_ami_manifest_path(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetAncestorAmiIds(OnVectorResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnVectorResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_ancestor_ami_ids(m_client, s_onVectorResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetInstanceAction(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_instance_action(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetInstanceId(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_instance_id(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetInstanceType(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_instance_type(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetMacAddress(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_mac_address(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetPrivateIpAddress(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_private_ip_address(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetAvailabilityZone(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_availability_zone(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetProductCodes(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_product_codes(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetPublicKey(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_public_key(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetRamDiskId(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_ramdisk_id(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetReservationId(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_reservation_id(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetSecurityGroups(OnVectorResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnVectorResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_security_groups(m_client, s_onVectorResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetBlockDeviceMapping(OnVectorResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnVectorResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_block_device_mapping( + m_client, s_onVectorResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetAttachedIamRole(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_attached_iam_role(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetCredentials( + const StringView &iamRoleName, + OnCredentialsAcquired callback, + void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnCredentialsAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_credentials( + m_client, StringViewToByteCursor(iamRoleName), s_onCredentialsAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetIamProfile(OnIamProfileAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnIamProfileAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_iam_profile(m_client, s_onIamProfileAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetUserData(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_user_data(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetInstanceSignature(OnResourceAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnResourceAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_instance_signature(m_client, s_onResourceAcquired, wrappedCallbackArgs); + } + + int ImdsClient::GetInstanceInfo(OnInstanceInfoAcquired callback, void *userData) + { + auto wrappedCallbackArgs = Aws::Crt::New<WrappedCallbackArgs<OnInstanceInfoAcquired>>( + m_allocator, m_allocator, callback, userData); + if (wrappedCallbackArgs == nullptr) + { + return AWS_OP_ERR; + } + return aws_imds_client_get_instance_info(m_client, s_onInstanceInfoAcquired, wrappedCallbackArgs); + } + } // namespace Imds + } // namespace Crt + +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/JsonObject.cpp b/contrib/restricted/aws/aws-crt-cpp/source/JsonObject.cpp new file mode 100644 index 0000000000..86a3ae73fc --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/JsonObject.cpp @@ -0,0 +1,596 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/crt/JsonObject.h> + +#include <aws/crt/external/cJSON.h> + +#include <algorithm> +#include <iterator> + +namespace Aws +{ + namespace Crt + { + JsonObject::JsonObject() : m_wasParseSuccessful(true) { m_value = nullptr; } + + JsonObject::JsonObject(cJSON *value) + : m_value(cJSON_Duplicate(value, 1 /* recurse */)), m_wasParseSuccessful(true) + { + } + + JsonObject::JsonObject(const String &value) : m_wasParseSuccessful(true) + { + const char *return_parse_end; + m_value = cJSON_ParseWithLengthOpts(value.c_str(), value.length(), &return_parse_end, 0); + + if (m_value == nullptr || cJSON_IsInvalid(m_value) == 1) + { + m_wasParseSuccessful = false; + m_errorMessage = "Failed to parse JSON at: "; + m_errorMessage += return_parse_end; + } + } + + JsonObject::JsonObject(const JsonObject &value) + : m_value(cJSON_Duplicate(value.m_value, 1 /*recurse*/)), m_wasParseSuccessful(value.m_wasParseSuccessful), + m_errorMessage(value.m_errorMessage) + { + } + + JsonObject::JsonObject(JsonObject &&value) noexcept + : m_value(value.m_value), m_wasParseSuccessful(value.m_wasParseSuccessful), + m_errorMessage(std::move(value.m_errorMessage)) + { + value.m_value = nullptr; + } + + void JsonObject::Destroy() { cJSON_Delete(m_value); } + + JsonObject::~JsonObject() { Destroy(); } + + JsonObject &JsonObject::operator=(const JsonObject &other) + { + if (this == &other) + { + return *this; + } + + Destroy(); + m_value = cJSON_Duplicate(other.m_value, 1 /*recurse*/); + m_wasParseSuccessful = other.m_wasParseSuccessful; + m_errorMessage = other.m_errorMessage; + return *this; + } + + JsonObject &JsonObject::operator=(JsonObject &&other) noexcept + { + if (this == &other) + { + return *this; + } + + using std::swap; + swap(m_value, other.m_value); + swap(m_errorMessage, other.m_errorMessage); + m_wasParseSuccessful = other.m_wasParseSuccessful; + return *this; + } + + static void AddOrReplace(cJSON *root, const char *key, cJSON *value) + { + const auto existing = cJSON_GetObjectItemCaseSensitive(root, key); + if (existing != nullptr) + { + cJSON_ReplaceItemInObjectCaseSensitive(root, key, value); + } + else + { + cJSON_AddItemToObject(root, key, value); + } + } + + JsonObject &JsonObject::WithString(const char *key, const String &value) + { + if (m_value == nullptr) + { + m_value = cJSON_CreateObject(); + } + + const auto val = cJSON_CreateString(value.c_str()); + AddOrReplace(m_value, key, val); + return *this; + } + + JsonObject &JsonObject::WithString(const String &key, const String &value) + { + return WithString(key.c_str(), value); + } + + JsonObject &JsonObject::AsString(const String &value) + { + Destroy(); + m_value = cJSON_CreateString(value.c_str()); + return *this; + } + + JsonObject &JsonObject::WithBool(const char *key, bool value) + { + if (m_value == nullptr) + { + m_value = cJSON_CreateObject(); + } + + const auto val = cJSON_CreateBool((cJSON_bool)value); + AddOrReplace(m_value, key, val); + return *this; + } + + JsonObject &JsonObject::WithBool(const String &key, bool value) { return WithBool(key.c_str(), value); } + + JsonObject &JsonObject::AsBool(bool value) + { + Destroy(); + m_value = cJSON_CreateBool((cJSON_bool)value); + return *this; + } + + JsonObject &JsonObject::WithInteger(const char *key, int value) + { + return WithDouble(key, static_cast<double>(value)); + } + + JsonObject &JsonObject::WithInteger(const String &key, int value) + { + return WithDouble(key.c_str(), static_cast<double>(value)); + } + + JsonObject &JsonObject::AsInteger(int value) + { + Destroy(); + m_value = cJSON_CreateNumber(static_cast<double>(value)); + return *this; + } + + JsonObject &JsonObject::WithInt64(const char *key, int64_t value) + { + return WithDouble(key, static_cast<double>(value)); + } + + JsonObject &JsonObject::WithInt64(const String &key, int64_t value) + { + return WithDouble(key.c_str(), static_cast<double>(value)); + } + + JsonObject &JsonObject::AsInt64(int64_t value) { return AsDouble(static_cast<double>(value)); } + + JsonObject &JsonObject::WithDouble(const char *key, double value) + { + if (m_value == nullptr) + { + m_value = cJSON_CreateObject(); + } + + const auto val = cJSON_CreateNumber(value); + AddOrReplace(m_value, key, val); + return *this; + } + + JsonObject &JsonObject::WithDouble(const String &key, double value) { return WithDouble(key.c_str(), value); } + + JsonObject &JsonObject::AsDouble(double value) + { + Destroy(); + m_value = cJSON_CreateNumber(value); + return *this; + } + + JsonObject &JsonObject::WithArray(const char *key, const Vector<String> &array) + { + if (m_value == nullptr) + { + m_value = cJSON_CreateObject(); + } + + auto arrayValue = cJSON_CreateArray(); + for (const auto &i : array) + { + cJSON_AddItemToArray(arrayValue, cJSON_CreateString(i.c_str())); + } + + AddOrReplace(m_value, key, arrayValue); + return *this; + } + + JsonObject &JsonObject::WithArray(const String &key, const Vector<String> &array) + { + return WithArray(key.c_str(), array); + } + + JsonObject &JsonObject::WithArray(const String &key, const Vector<JsonObject> &array) + { + if (m_value == nullptr) + { + m_value = cJSON_CreateObject(); + } + + auto arrayValue = cJSON_CreateArray(); + for (const auto &i : array) + { + cJSON_AddItemToArray(arrayValue, cJSON_Duplicate(i.m_value, 1 /*recurse*/)); + } + + AddOrReplace(m_value, key.c_str(), arrayValue); + return *this; + } + + JsonObject &JsonObject::WithArray(const String &key, Vector<JsonObject> &&array) + { + if (m_value == nullptr) + { + m_value = cJSON_CreateObject(); + } + + auto arrayValue = cJSON_CreateArray(); + for (auto &i : array) + { + cJSON_AddItemToArray(arrayValue, i.m_value); + i.m_value = nullptr; + } + + AddOrReplace(m_value, key.c_str(), arrayValue); + return *this; + } + + JsonObject &JsonObject::AsArray(const Vector<JsonObject> &array) + { + auto arrayValue = cJSON_CreateArray(); + for (const auto &i : array) + { + cJSON_AddItemToArray(arrayValue, cJSON_Duplicate(i.m_value, 1 /*recurse*/)); + } + + Destroy(); + m_value = arrayValue; + return *this; + } + + JsonObject &JsonObject::AsArray(Vector<JsonObject> &&array) + { + auto arrayValue = cJSON_CreateArray(); + for (auto &i : array) + { + cJSON_AddItemToArray(arrayValue, i.m_value); + i.m_value = nullptr; + } + + Destroy(); + m_value = arrayValue; + return *this; + } + + JsonObject &JsonObject::AsNull() + { + m_value = cJSON_CreateNull(); + return *this; + } + + JsonObject &JsonObject::WithObject(const char *key, const JsonObject &value) + { + if (m_value == nullptr) + { + m_value = cJSON_CreateObject(); + } + + const auto copy = + value.m_value == nullptr ? cJSON_CreateObject() : cJSON_Duplicate(value.m_value, 1 /*recurse*/); + AddOrReplace(m_value, key, copy); + return *this; + } + + JsonObject &JsonObject::WithObject(const String &key, const JsonObject &value) + { + return WithObject(key.c_str(), value); + } + + JsonObject &JsonObject::WithObject(const char *key, JsonObject &&value) + { + if (m_value == nullptr) + { + m_value = cJSON_CreateObject(); + } + + AddOrReplace(m_value, key, value.m_value == nullptr ? cJSON_CreateObject() : value.m_value); + value.m_value = nullptr; + return *this; + } + + JsonObject &JsonObject::WithObject(const String &key, JsonObject &&value) + { + return WithObject(key.c_str(), std::move(value)); + } + + JsonObject &JsonObject::AsObject(const JsonObject &value) + { + *this = value; + return *this; + } + + JsonObject &JsonObject::AsObject(JsonObject &&value) + { + *this = std::move(value); + return *this; + } + + bool JsonObject::operator==(const JsonObject &other) const + { + return cJSON_Compare(m_value, other.m_value, 1 /*case-sensitive*/) != 0; + } + + bool JsonObject::operator!=(const JsonObject &other) const { return !(*this == other); } + + JsonView JsonObject::View() const { return *this; } + + JsonView::JsonView() : m_value(nullptr) {} + + JsonView::JsonView(const JsonObject &val) : m_value(val.m_value) {} + + JsonView::JsonView(cJSON *val) : m_value(val) {} + + JsonView &JsonView::operator=(const JsonObject &v) + { + m_value = v.m_value; + return *this; + } + + JsonView &JsonView::operator=(cJSON *val) + { + m_value = val; + return *this; + } + + String JsonView::GetString(const String &key) const { return GetString(key.c_str()); } + + String JsonView::GetString(const char *key) const + { + AWS_ASSERT(m_value); + auto item = cJSON_GetObjectItemCaseSensitive(m_value, key); + auto str = cJSON_GetStringValue(item); + return str != nullptr ? str : ""; + } + + String JsonView::AsString() const + { + const char *str = cJSON_GetStringValue(m_value); + if (str == nullptr) + { + return {}; + } + return str; + } + + bool JsonView::GetBool(const String &key) const { return GetBool(key.c_str()); } + + bool JsonView::GetBool(const char *key) const + { + AWS_ASSERT(m_value); + auto item = cJSON_GetObjectItemCaseSensitive(m_value, key); + AWS_ASSERT(item); + return cJSON_IsTrue(item) != 0; + } + + bool JsonView::AsBool() const + { + AWS_ASSERT(cJSON_IsBool(m_value)); + return cJSON_IsTrue(m_value) != 0; + } + + int JsonView::GetInteger(const String &key) const { return GetInteger(key.c_str()); } + + int JsonView::GetInteger(const char *key) const + { + AWS_ASSERT(m_value); + auto item = cJSON_GetObjectItemCaseSensitive(m_value, key); + AWS_ASSERT(item); + return item->valueint; + } + + int JsonView::AsInteger() const + { + AWS_ASSERT(cJSON_IsNumber(m_value)); // can be double or value larger than int_max, but at least not UB + return m_value->valueint; + } + + int64_t JsonView::GetInt64(const String &key) const { return static_cast<int64_t>(GetDouble(key)); } + + int64_t JsonView::GetInt64(const char *key) const { return static_cast<int64_t>(GetDouble(key)); } + + int64_t JsonView::AsInt64() const + { + AWS_ASSERT(cJSON_IsNumber(m_value)); + return static_cast<int64_t>(m_value->valuedouble); + } + + double JsonView::GetDouble(const String &key) const { return GetDouble(key.c_str()); } + + double JsonView::GetDouble(const char *key) const + { + AWS_ASSERT(m_value); + auto item = cJSON_GetObjectItemCaseSensitive(m_value, key); + AWS_ASSERT(item); + return item->valuedouble; + } + + double JsonView::AsDouble() const + { + AWS_ASSERT(cJSON_IsNumber(m_value)); + return m_value->valuedouble; + } + + JsonView JsonView::GetJsonObject(const String &key) const { return GetJsonObject(key.c_str()); } + + JsonView JsonView::GetJsonObject(const char *key) const + { + AWS_ASSERT(m_value); + auto item = cJSON_GetObjectItemCaseSensitive(m_value, key); + return item; + } + + JsonObject JsonView::GetJsonObjectCopy(const String &key) const { return GetJsonObjectCopy(key.c_str()); } + + JsonObject JsonView::GetJsonObjectCopy(const char *key) const + { + AWS_ASSERT(m_value); + /* force a deep copy */ + return JsonObject(cJSON_GetObjectItemCaseSensitive(m_value, key)); + } + + JsonView JsonView::AsObject() const + { + AWS_ASSERT(cJSON_IsObject(m_value)); + return m_value; + } + + Vector<JsonView> JsonView::GetArray(const String &key) const { return GetArray(key.c_str()); } + + Vector<JsonView> JsonView::GetArray(const char *key) const + { + AWS_ASSERT(m_value); + auto array = cJSON_GetObjectItemCaseSensitive(m_value, key); + AWS_ASSERT(cJSON_IsArray(array)); + Vector<JsonView> returnArray(static_cast<size_t>(cJSON_GetArraySize(array))); + + auto element = array->child; + for (size_t i = 0; element != nullptr && i < returnArray.size(); ++i, element = element->next) + { + returnArray[i] = element; + } + + return returnArray; + } + + Vector<JsonView> JsonView::AsArray() const + { + AWS_ASSERT(cJSON_IsArray(m_value)); + Vector<JsonView> returnArray(static_cast<size_t>(cJSON_GetArraySize(m_value))); + + auto element = m_value->child; + + for (size_t i = 0; element != nullptr && i < returnArray.size(); ++i, element = element->next) + { + returnArray[i] = element; + } + + return returnArray; + } + + Map<String, JsonView> JsonView::GetAllObjects() const + { + Map<String, JsonView> valueMap; + if (m_value == nullptr) + { + return valueMap; + } + + for (auto iter = m_value->child; iter != nullptr; iter = iter->next) + { + valueMap.emplace(std::make_pair(String(iter->string), JsonView(iter))); + } + + return valueMap; + } + + bool JsonView::ValueExists(const String &key) const { return ValueExists(key.c_str()); } + + bool JsonView::ValueExists(const char *key) const + { + if (cJSON_IsObject(m_value) == 0) + { + return false; + } + + auto item = cJSON_GetObjectItemCaseSensitive(m_value, key); + return !(item == nullptr || cJSON_IsNull(item) != 0); + } + + bool JsonView::KeyExists(const String &key) const { return KeyExists(key.c_str()); } + + bool JsonView::KeyExists(const char *key) const + { + if (cJSON_IsObject(m_value) == 0) + { + return false; + } + + return cJSON_GetObjectItemCaseSensitive(m_value, key) != nullptr; + } + + bool JsonView::IsObject() const { return cJSON_IsObject(m_value) != 0; } + + bool JsonView::IsBool() const { return cJSON_IsBool(m_value) != 0; } + + bool JsonView::IsString() const { return cJSON_IsString(m_value) != 0; } + + bool JsonView::IsIntegerType() const + { + if (cJSON_IsNumber(m_value) == 0) + { + return false; + } + + return m_value->valuedouble == static_cast<int64_t>(m_value->valuedouble); + } + + bool JsonView::IsFloatingPointType() const + { + if (cJSON_IsNumber(m_value) == 0) + { + return false; + } + + return m_value->valuedouble != static_cast<int64_t>(m_value->valuedouble); + } + + bool JsonView::IsListType() const { return cJSON_IsArray(m_value) != 0; } + + bool JsonView::IsNull() const { return cJSON_IsNull(m_value) != 0; } + + String JsonView::WriteCompact(bool treatAsObject) const + { + if (m_value == nullptr) + { + if (treatAsObject) + { + return "{}"; + } + return ""; + } + + auto temp = cJSON_PrintUnformatted(m_value); + String out(temp); + cJSON_free(temp); + return out; + } + + String JsonView::WriteReadable(bool treatAsObject) const + { + if (m_value == nullptr) + { + if (treatAsObject) + { + return "{\n}\n"; + } + return ""; + } + + auto temp = cJSON_Print(m_value); + String out(temp); + cJSON_free(temp); + return out; + } + + JsonObject JsonView::Materialize() const { return m_value; } + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/StringUtils.cpp b/contrib/restricted/aws/aws-crt-cpp/source/StringUtils.cpp new file mode 100644 index 0000000000..5c6984f628 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/StringUtils.cpp @@ -0,0 +1,15 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/StringUtils.h> + +#include <aws/common/hash_table.h> + +namespace Aws +{ + namespace Crt + { + size_t HashString(const char *str) noexcept { return (size_t)aws_hash_c_string(str); } + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/Types.cpp b/contrib/restricted/aws/aws-crt-cpp/source/Types.cpp new file mode 100644 index 0000000000..89f0626242 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/Types.cpp @@ -0,0 +1,103 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/Types.h> + +#include <aws/common/encoding.h> + +namespace Aws +{ + namespace Crt + { + ByteBuf ByteBufFromCString(const char *str) noexcept { return aws_byte_buf_from_c_str(str); } + + ByteBuf ByteBufFromEmptyArray(const uint8_t *array, size_t len) noexcept + { + return aws_byte_buf_from_empty_array(array, len); + } + + ByteBuf ByteBufFromArray(const uint8_t *array, size_t capacity) noexcept + { + return aws_byte_buf_from_array(array, capacity); + } + + ByteBuf ByteBufNewCopy(Allocator *alloc, const uint8_t *array, size_t len) + { + ByteBuf retVal; + ByteBuf src = aws_byte_buf_from_array(array, len); + aws_byte_buf_init_copy(&retVal, alloc, &src); + return retVal; + } + + void ByteBufDelete(ByteBuf &buf) { aws_byte_buf_clean_up(&buf); } + + ByteCursor ByteCursorFromCString(const char *str) noexcept { return aws_byte_cursor_from_c_str(str); } + + ByteCursor ByteCursorFromString(const Crt::String &str) noexcept + { + return aws_byte_cursor_from_array((const void *)str.data(), str.length()); + } + + ByteCursor ByteCursorFromStringView(const Crt::StringView &str) noexcept + { + return aws_byte_cursor_from_array((const void *)str.data(), str.length()); + } + + ByteCursor ByteCursorFromByteBuf(const ByteBuf &buf) noexcept { return aws_byte_cursor_from_buf(&buf); } + + ByteCursor ByteCursorFromArray(const uint8_t *array, size_t len) noexcept + { + return aws_byte_cursor_from_array(array, len); + } + + Vector<uint8_t> Base64Decode(const String &decode) + { + ByteCursor toDecode = ByteCursorFromString(decode); + + size_t allocation_size = 0; + + if (aws_base64_compute_decoded_len(&toDecode, &allocation_size) == AWS_OP_SUCCESS) + { + Vector<uint8_t> output(allocation_size, 0x00); + ByteBuf tempBuf = aws_byte_buf_from_array(output.data(), output.size()); + tempBuf.len = 0; + + if (aws_base64_decode(&toDecode, &tempBuf) == AWS_OP_SUCCESS) + { + return output; + } + } + + return {}; + } + + String Base64Encode(const Vector<uint8_t> &encode) + { + ByteCursor toEncode = aws_byte_cursor_from_array((const void *)encode.data(), encode.size()); + + size_t allocation_size = 0; + + if (aws_base64_compute_encoded_len(encode.size(), &allocation_size) == AWS_OP_SUCCESS) + { + String output(allocation_size, 0x00); + ByteBuf tempBuf = aws_byte_buf_from_array(output.data(), output.size()); + tempBuf.len = 0; + + if (aws_base64_encode(&toEncode, &tempBuf) == AWS_OP_SUCCESS) + { + // encoding appends a null terminator, and accounts for it in the encoded length, + // which makes the string 1 character too long + if (output.back() == 0) + { + output.pop_back(); + } + return output; + } + } + + return {}; + } + + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/UUID.cpp b/contrib/restricted/aws/aws-crt-cpp/source/UUID.cpp new file mode 100644 index 0000000000..c985ea2778 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/UUID.cpp @@ -0,0 +1,54 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/UUID.h> + +namespace Aws +{ + namespace Crt + { + UUID::UUID() noexcept : m_good(false) + { + if (aws_uuid_init(&m_uuid) == AWS_OP_SUCCESS) + { + m_good = true; + } + } + + UUID::UUID(const String &str) noexcept : m_good(false) + { + auto strCur = aws_byte_cursor_from_c_str(str.c_str()); + if (aws_uuid_init_from_str(&m_uuid, &strCur) == AWS_OP_SUCCESS) + { + m_good = true; + } + } + + UUID &UUID::operator=(const String &str) noexcept + { + *this = UUID(str); + return *this; + } + + bool UUID::operator==(const UUID &other) noexcept { return aws_uuid_equals(&m_uuid, &other.m_uuid); } + + bool UUID::operator!=(const UUID &other) noexcept { return !aws_uuid_equals(&m_uuid, &other.m_uuid); } + + String UUID::ToString() const + { + String uuidStr; + uuidStr.resize(AWS_UUID_STR_LEN); + auto outBuf = ByteBufFromEmptyArray(reinterpret_cast<const uint8_t *>(uuidStr.data()), uuidStr.capacity()); + aws_uuid_to_str(&m_uuid, &outBuf); + uuidStr.resize(outBuf.len); + return uuidStr; + } + + UUID::operator String() const { return ToString(); } + + UUID::operator ByteBuf() const noexcept { return ByteBufFromArray(m_uuid.uuid_data, sizeof(m_uuid.uuid_data)); } + + int UUID::GetLastError() const noexcept { return aws_last_error(); } + } // namespace Crt +} // namespace Aws
\ No newline at end of file diff --git a/contrib/restricted/aws/aws-crt-cpp/source/auth/Credentials.cpp b/contrib/restricted/aws/aws-crt-cpp/source/auth/Credentials.cpp new file mode 100644 index 0000000000..77e40c61a9 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/auth/Credentials.cpp @@ -0,0 +1,478 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/crt/auth/Credentials.h> + +#include <aws/crt/http/HttpConnection.h> +#include <aws/crt/http/HttpProxyStrategy.h> + +#include <aws/auth/credentials.h> +#include <aws/common/string.h> + +#include <algorithm> +#include <aws/http/connection.h> + +#include <aws/crt/Api.h> + +namespace Aws +{ + namespace Crt + { + namespace Auth + { + Credentials::Credentials(const aws_credentials *credentials) noexcept : m_credentials(credentials) + { + if (credentials != nullptr) + { + aws_credentials_acquire(credentials); + } + } + + Credentials::Credentials( + ByteCursor access_key_id, + ByteCursor secret_access_key, + ByteCursor session_token, + uint64_t expiration_timepoint_in_seconds, + Allocator *allocator) noexcept + : m_credentials(aws_credentials_new( + allocator, + access_key_id, + secret_access_key, + session_token, + expiration_timepoint_in_seconds)) + { + } + + Credentials::Credentials(Allocator *allocator) noexcept + : m_credentials(aws_credentials_new_anonymous(allocator)) + { + } + + Credentials::~Credentials() + { + aws_credentials_release(m_credentials); + m_credentials = nullptr; + } + + ByteCursor Credentials::GetAccessKeyId() const noexcept + { + if (m_credentials) + { + return aws_credentials_get_access_key_id(m_credentials); + } + else + { + return ByteCursor{0, nullptr}; + } + } + + ByteCursor Credentials::GetSecretAccessKey() const noexcept + { + if (m_credentials) + { + return aws_credentials_get_secret_access_key(m_credentials); + } + else + { + return ByteCursor{0, nullptr}; + } + } + + ByteCursor Credentials::GetSessionToken() const noexcept + { + if (m_credentials) + { + return aws_credentials_get_session_token(m_credentials); + } + else + { + return ByteCursor{0, nullptr}; + } + } + + uint64_t Credentials::GetExpirationTimepointInSeconds() const noexcept + { + if (m_credentials) + { + return aws_credentials_get_expiration_timepoint_seconds(m_credentials); + } + else + { + return 0; + } + } + + Credentials::operator bool() const noexcept { return m_credentials != nullptr; } + + CredentialsProvider::CredentialsProvider(aws_credentials_provider *provider, Allocator *allocator) noexcept + : m_allocator(allocator), m_provider(provider) + { + } + + CredentialsProvider::~CredentialsProvider() + { + if (m_provider) + { + aws_credentials_provider_release(m_provider); + m_provider = nullptr; + } + } + + struct CredentialsProviderCallbackArgs + { + CredentialsProviderCallbackArgs() = default; + + OnCredentialsResolved m_onCredentialsResolved; + std::shared_ptr<const CredentialsProvider> m_provider; + }; + + void CredentialsProvider::s_onCredentialsResolved( + aws_credentials *credentials, + int error_code, + void *user_data) + { + CredentialsProviderCallbackArgs *callbackArgs = + static_cast<CredentialsProviderCallbackArgs *>(user_data); + + auto credentialsPtr = + Aws::Crt::MakeShared<Credentials>(callbackArgs->m_provider->m_allocator, credentials); + + callbackArgs->m_onCredentialsResolved(credentialsPtr, error_code); + + Aws::Crt::Delete(callbackArgs, callbackArgs->m_provider->m_allocator); + } + + bool CredentialsProvider::GetCredentials(const OnCredentialsResolved &onCredentialsResolved) const + { + if (m_provider == nullptr) + { + return false; + } + + auto callbackArgs = Aws::Crt::New<CredentialsProviderCallbackArgs>(m_allocator); + if (callbackArgs == nullptr) + { + return false; + } + + callbackArgs->m_provider = std::static_pointer_cast<const CredentialsProvider>(shared_from_this()); + callbackArgs->m_onCredentialsResolved = onCredentialsResolved; + + aws_credentials_provider_get_credentials(m_provider, s_onCredentialsResolved, callbackArgs); + + return true; + } + + static std::shared_ptr<ICredentialsProvider> s_CreateWrappedProvider( + struct aws_credentials_provider *raw_provider, + Allocator *allocator) + { + if (raw_provider == nullptr) + { + return nullptr; + } + + /* Switch to some kind of make_shared/allocate_shared when allocator support improves */ + auto provider = Aws::Crt::MakeShared<CredentialsProvider>(allocator, raw_provider, allocator); + return std::static_pointer_cast<ICredentialsProvider>(provider); + } + + std::shared_ptr<ICredentialsProvider> CredentialsProvider::CreateCredentialsProviderStatic( + const CredentialsProviderStaticConfig &config, + Allocator *allocator) + { + aws_credentials_provider_static_options staticOptions; + AWS_ZERO_STRUCT(staticOptions); + staticOptions.access_key_id = config.AccessKeyId; + staticOptions.secret_access_key = config.SecretAccessKey; + staticOptions.session_token = config.SessionToken; + return s_CreateWrappedProvider( + aws_credentials_provider_new_static(allocator, &staticOptions), allocator); + } + + std::shared_ptr<ICredentialsProvider> CredentialsProvider::CreateCredentialsProviderAnonymous( + Allocator *allocator) + { + aws_credentials_provider_shutdown_options shutdown_options; + AWS_ZERO_STRUCT(shutdown_options); + + return s_CreateWrappedProvider( + aws_credentials_provider_new_anonymous(allocator, &shutdown_options), allocator); + } + + std::shared_ptr<ICredentialsProvider> CredentialsProvider::CreateCredentialsProviderEnvironment( + Allocator *allocator) + { + aws_credentials_provider_environment_options environmentOptions; + AWS_ZERO_STRUCT(environmentOptions); + return s_CreateWrappedProvider( + aws_credentials_provider_new_environment(allocator, &environmentOptions), allocator); + } + + std::shared_ptr<ICredentialsProvider> CredentialsProvider::CreateCredentialsProviderProfile( + const CredentialsProviderProfileConfig &config, + Allocator *allocator) + { + struct aws_credentials_provider_profile_options raw_config; + AWS_ZERO_STRUCT(raw_config); + + raw_config.config_file_name_override = config.ConfigFileNameOverride; + raw_config.credentials_file_name_override = config.CredentialsFileNameOverride; + raw_config.profile_name_override = config.ProfileNameOverride; + raw_config.bootstrap = config.Bootstrap ? config.Bootstrap->GetUnderlyingHandle() : nullptr; + raw_config.tls_ctx = config.TlsContext ? config.TlsContext->GetUnderlyingHandle() : nullptr; + + return s_CreateWrappedProvider(aws_credentials_provider_new_profile(allocator, &raw_config), allocator); + } + + std::shared_ptr<ICredentialsProvider> CredentialsProvider::CreateCredentialsProviderImds( + const CredentialsProviderImdsConfig &config, + Allocator *allocator) + { + struct aws_credentials_provider_imds_options raw_config; + AWS_ZERO_STRUCT(raw_config); + + if (config.Bootstrap != nullptr) + { + raw_config.bootstrap = config.Bootstrap->GetUnderlyingHandle(); + } + else + { + raw_config.bootstrap = ApiHandle::GetOrCreateStaticDefaultClientBootstrap()->GetUnderlyingHandle(); + } + + return s_CreateWrappedProvider(aws_credentials_provider_new_imds(allocator, &raw_config), allocator); + } + + std::shared_ptr<ICredentialsProvider> CredentialsProvider::CreateCredentialsProviderChain( + const CredentialsProviderChainConfig &config, + Allocator *allocator) + { + Vector<aws_credentials_provider *> providers; + providers.reserve(config.Providers.size()); + + std::for_each( + config.Providers.begin(), + config.Providers.end(), + [&](const std::shared_ptr<ICredentialsProvider> &provider) { + providers.push_back(provider->GetUnderlyingHandle()); + }); + + struct aws_credentials_provider_chain_options raw_config; + AWS_ZERO_STRUCT(raw_config); + + raw_config.providers = providers.data(); + raw_config.provider_count = config.Providers.size(); + + return s_CreateWrappedProvider(aws_credentials_provider_new_chain(allocator, &raw_config), allocator); + } + + std::shared_ptr<ICredentialsProvider> CredentialsProvider::CreateCredentialsProviderCached( + const CredentialsProviderCachedConfig &config, + Allocator *allocator) + { + struct aws_credentials_provider_cached_options raw_config; + AWS_ZERO_STRUCT(raw_config); + + raw_config.source = config.Provider->GetUnderlyingHandle(); + raw_config.refresh_time_in_milliseconds = config.CachedCredentialTTL.count(); + + return s_CreateWrappedProvider(aws_credentials_provider_new_cached(allocator, &raw_config), allocator); + } + + std::shared_ptr<ICredentialsProvider> CredentialsProvider::CreateCredentialsProviderChainDefault( + const CredentialsProviderChainDefaultConfig &config, + Allocator *allocator) + { + struct aws_credentials_provider_chain_default_options raw_config; + AWS_ZERO_STRUCT(raw_config); + + raw_config.bootstrap = + config.Bootstrap ? config.Bootstrap->GetUnderlyingHandle() + : ApiHandle::GetOrCreateStaticDefaultClientBootstrap()->GetUnderlyingHandle(); + raw_config.tls_ctx = config.TlsContext ? config.TlsContext->GetUnderlyingHandle() : nullptr; + + return s_CreateWrappedProvider( + aws_credentials_provider_new_chain_default(allocator, &raw_config), allocator); + } + + std::shared_ptr<ICredentialsProvider> CredentialsProvider::CreateCredentialsProviderX509( + const CredentialsProviderX509Config &config, + Allocator *allocator) + { + struct aws_credentials_provider_x509_options raw_config; + AWS_ZERO_STRUCT(raw_config); + + raw_config.bootstrap = + config.Bootstrap ? config.Bootstrap->GetUnderlyingHandle() + : ApiHandle::GetOrCreateStaticDefaultClientBootstrap()->GetUnderlyingHandle(); + raw_config.tls_connection_options = config.TlsOptions.GetUnderlyingHandle(); + raw_config.thing_name = aws_byte_cursor_from_c_str(config.ThingName.c_str()); + raw_config.role_alias = aws_byte_cursor_from_c_str(config.RoleAlias.c_str()); + raw_config.endpoint = aws_byte_cursor_from_c_str(config.Endpoint.c_str()); + + struct aws_http_proxy_options proxy_options; + AWS_ZERO_STRUCT(proxy_options); + if (config.ProxyOptions.has_value()) + { + const Http::HttpClientConnectionProxyOptions &proxy_config = config.ProxyOptions.value(); + proxy_config.InitializeRawProxyOptions(proxy_options); + + raw_config.proxy_options = &proxy_options; + } + + return s_CreateWrappedProvider(aws_credentials_provider_new_x509(allocator, &raw_config), allocator); + } + + struct DelegateCredentialsProviderCallbackArgs + { + DelegateCredentialsProviderCallbackArgs() = default; + + Allocator *allocator; + GetCredentialsHandler m_Handler; + }; + + static int s_onDelegateGetCredentials( + void *delegate_user_data, + aws_on_get_credentials_callback_fn callback, + void *callback_user_data) + { + auto args = static_cast<DelegateCredentialsProviderCallbackArgs *>(delegate_user_data); + auto creds = args->m_Handler(); + struct aws_credentials *m_credentials = (struct aws_credentials *)(void *)creds->GetUnderlyingHandle(); + callback(m_credentials, AWS_ERROR_SUCCESS, callback_user_data); + return AWS_OP_SUCCESS; + } + + static void s_onDelegateShutdownComplete(void *user_data) + { + auto args = static_cast<DelegateCredentialsProviderCallbackArgs *>(user_data); + Aws::Crt::Delete(args, args->allocator); + } + + std::shared_ptr<ICredentialsProvider> CredentialsProvider::CreateCredentialsProviderDelegate( + const CredentialsProviderDelegateConfig &config, + Allocator *allocator) + { + struct aws_credentials_provider_delegate_options raw_config; + AWS_ZERO_STRUCT(raw_config); + + auto delegateCallbackArgs = Aws::Crt::New<DelegateCredentialsProviderCallbackArgs>(allocator); + delegateCallbackArgs->allocator = allocator; + delegateCallbackArgs->m_Handler = config.Handler; + raw_config.delegate_user_data = delegateCallbackArgs; + raw_config.get_credentials = s_onDelegateGetCredentials; + aws_credentials_provider_shutdown_options options; + options.shutdown_callback = s_onDelegateShutdownComplete; + options.shutdown_user_data = delegateCallbackArgs; + raw_config.shutdown_options = options; + return s_CreateWrappedProvider( + aws_credentials_provider_new_delegate(allocator, &raw_config), allocator); + } + + CredentialsProviderCognitoConfig::CredentialsProviderCognitoConfig() : Bootstrap(nullptr) {} + + std::shared_ptr<ICredentialsProvider> CredentialsProvider::CreateCredentialsProviderCognito( + const CredentialsProviderCognitoConfig &config, + Allocator *allocator) + { + struct aws_credentials_provider_cognito_options raw_config; + AWS_ZERO_STRUCT(raw_config); + + raw_config.endpoint = aws_byte_cursor_from_c_str(config.Endpoint.c_str()); + raw_config.identity = aws_byte_cursor_from_c_str(config.Identity.c_str()); + + struct aws_byte_cursor custom_role_arn_cursor; + AWS_ZERO_STRUCT(custom_role_arn_cursor); + if (config.CustomRoleArn.has_value()) + { + custom_role_arn_cursor = aws_byte_cursor_from_c_str(config.CustomRoleArn.value().c_str()); + raw_config.custom_role_arn = &custom_role_arn_cursor; + } + + Vector<struct aws_cognito_identity_provider_token_pair> logins; + if (config.Logins.has_value()) + { + for (const auto &login_pair : config.Logins.value()) + { + struct aws_cognito_identity_provider_token_pair cursor_login_pair; + AWS_ZERO_STRUCT(cursor_login_pair); + + cursor_login_pair.identity_provider_name = + aws_byte_cursor_from_c_str(login_pair.IdentityProviderName.c_str()); + cursor_login_pair.identity_provider_token = + aws_byte_cursor_from_c_str(login_pair.IdentityProviderToken.c_str()); + + logins.push_back(cursor_login_pair); + } + + raw_config.login_count = logins.size(); + raw_config.logins = logins.data(); + } + + raw_config.bootstrap = + config.Bootstrap ? config.Bootstrap->GetUnderlyingHandle() + : ApiHandle::GetOrCreateStaticDefaultClientBootstrap()->GetUnderlyingHandle(); + + raw_config.tls_ctx = config.TlsCtx.GetUnderlyingHandle(); + + struct aws_http_proxy_options proxy_options; + AWS_ZERO_STRUCT(proxy_options); + if (config.ProxyOptions.has_value()) + { + const Http::HttpClientConnectionProxyOptions &proxy_config = config.ProxyOptions.value(); + proxy_config.InitializeRawProxyOptions(proxy_options); + + raw_config.http_proxy_options = &proxy_options; + } + + return s_CreateWrappedProvider( + aws_credentials_provider_new_cognito_caching(allocator, &raw_config), allocator); + } + + CredentialsProviderSTSConfig::CredentialsProviderSTSConfig() : Bootstrap(nullptr) {} + + std::shared_ptr<ICredentialsProvider> CredentialsProvider::CreateCredentialsProviderSTS( + const CredentialsProviderSTSConfig &config, + Allocator *allocator) + { + if (config.Provider == nullptr) + { + AWS_LOGF_ERROR( + AWS_LS_AUTH_CREDENTIALS_PROVIDER, + "Failed to build STS credentials provider - missing required 'Provider' configuration " + "parameter"); + return nullptr; + } + + struct aws_credentials_provider_sts_options raw_config; + AWS_ZERO_STRUCT(raw_config); + + raw_config.creds_provider = config.Provider->GetUnderlyingHandle(); + raw_config.role_arn = aws_byte_cursor_from_c_str(config.RoleArn.c_str()); + raw_config.session_name = aws_byte_cursor_from_c_str(config.SessionName.c_str()); + raw_config.duration_seconds = config.DurationSeconds; + + raw_config.bootstrap = + config.Bootstrap ? config.Bootstrap->GetUnderlyingHandle() + : ApiHandle::GetOrCreateStaticDefaultClientBootstrap()->GetUnderlyingHandle(); + + raw_config.tls_ctx = config.TlsCtx.GetUnderlyingHandle(); + + struct aws_http_proxy_options proxy_options; + AWS_ZERO_STRUCT(proxy_options); + if (config.ProxyOptions.has_value()) + { + const Http::HttpClientConnectionProxyOptions &proxy_config = config.ProxyOptions.value(); + proxy_config.InitializeRawProxyOptions(proxy_options); + + raw_config.http_proxy_options = &proxy_options; + } + + return s_CreateWrappedProvider(aws_credentials_provider_new_sts(allocator, &raw_config), allocator); + } + } // namespace Auth + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/auth/Sigv4Signing.cpp b/contrib/restricted/aws/aws-crt-cpp/source/auth/Sigv4Signing.cpp new file mode 100644 index 0000000000..9586b85fab --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/auth/Sigv4Signing.cpp @@ -0,0 +1,274 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/crt/auth/Sigv4Signing.h> + +#include <aws/crt/auth/Credentials.h> +#include <aws/crt/http/HttpRequestResponse.h> + +#include <aws/auth/signable.h> +#include <aws/auth/signing.h> +#include <aws/auth/signing_result.h> + +namespace Aws +{ + namespace Crt + { + namespace Auth + { + namespace SignedBodyValue + { + const char *EmptySha256 = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; + const char *EmptySha256Str() { return EmptySha256; } + + const char *UnsignedPayload = "UNSIGNED-PAYLOAD"; + const char *UnsignedPayloadStr() { return UnsignedPayload; } + + const char *StreamingAws4HmacSha256Payload = "STREAMING-AWS4-HMAC-SHA256-PAYLOAD"; + const char *StreamingAws4HmacSha256PayloadStr() { return StreamingAws4HmacSha256Payload; } + + const char *StreamingAws4HmacSha256Events = "STREAMING-AWS4-HMAC-SHA256-EVENTS"; + const char *StreamingAws4HmacSha256EventsStr() { return StreamingAws4HmacSha256Events; } + } // namespace SignedBodyValue + + AwsSigningConfig::AwsSigningConfig(Allocator *allocator) + : ISigningConfig(), m_allocator(allocator), m_credentialsProvider(nullptr), m_credentials(nullptr) + { + AWS_ZERO_STRUCT(m_config); + + SetSigningAlgorithm(SigningAlgorithm::SigV4); + SetSignatureType(SignatureType::HttpRequestViaHeaders); + SetShouldNormalizeUriPath(true); + SetUseDoubleUriEncode(true); + SetOmitSessionToken(false); + SetSignedBodyHeader(SignedBodyHeaderType::None); + SetSigningTimepoint(DateTime::Now()); + SetExpirationInSeconds(0); + m_config.config_type = AWS_SIGNING_CONFIG_AWS; + } + + AwsSigningConfig::~AwsSigningConfig() { m_allocator = nullptr; } + + SigningAlgorithm AwsSigningConfig::GetSigningAlgorithm() const noexcept + { + return static_cast<SigningAlgorithm>(m_config.algorithm); + } + + void AwsSigningConfig::SetSigningAlgorithm(SigningAlgorithm algorithm) noexcept + { + m_config.algorithm = static_cast<aws_signing_algorithm>(algorithm); + } + + SignatureType AwsSigningConfig::GetSignatureType() const noexcept + { + return static_cast<SignatureType>(m_config.signature_type); + } + + void AwsSigningConfig::SetSignatureType(SignatureType signatureType) noexcept + { + m_config.signature_type = static_cast<aws_signature_type>(signatureType); + } + + const Crt::String &AwsSigningConfig::GetRegion() const noexcept { return m_signingRegion; } + + void AwsSigningConfig::SetRegion(const Crt::String ®ion) noexcept + { + m_signingRegion = region; + m_config.region = ByteCursorFromCString(m_signingRegion.c_str()); + } + + const Crt::String &AwsSigningConfig::GetService() const noexcept { return m_serviceName; } + + void AwsSigningConfig::SetService(const Crt::String &service) noexcept + { + m_serviceName = service; + m_config.service = ByteCursorFromCString(m_serviceName.c_str()); + } + + DateTime AwsSigningConfig::GetSigningTimepoint() const noexcept + { + return {aws_date_time_as_millis(&m_config.date)}; + } + + void AwsSigningConfig::SetSigningTimepoint(const DateTime &date) noexcept + { + aws_date_time_init_epoch_millis(&m_config.date, date.Millis()); + } + + bool AwsSigningConfig::GetUseDoubleUriEncode() const noexcept + { + return m_config.flags.use_double_uri_encode; + } + + void AwsSigningConfig::SetUseDoubleUriEncode(bool useDoubleUriEncode) noexcept + { + m_config.flags.use_double_uri_encode = useDoubleUriEncode; + } + + bool AwsSigningConfig::GetShouldNormalizeUriPath() const noexcept + { + return m_config.flags.should_normalize_uri_path; + } + + void AwsSigningConfig::SetShouldNormalizeUriPath(bool shouldNormalizeUriPath) noexcept + { + m_config.flags.should_normalize_uri_path = shouldNormalizeUriPath; + } + + bool AwsSigningConfig::GetOmitSessionToken() const noexcept { return m_config.flags.omit_session_token; } + + void AwsSigningConfig::SetOmitSessionToken(bool omitSessionToken) noexcept + { + m_config.flags.omit_session_token = omitSessionToken; + } + + ShouldSignHeaderCb AwsSigningConfig::GetShouldSignHeaderCallback() const noexcept + { + return m_config.should_sign_header; + } + + void AwsSigningConfig::SetShouldSignHeaderCallback(ShouldSignHeaderCb shouldSignHeaderCb) noexcept + { + m_config.should_sign_header = shouldSignHeaderCb; + } + + void *AwsSigningConfig::GetShouldSignHeaderUserData() const noexcept + { + return m_config.should_sign_header_ud; + } + + void AwsSigningConfig::SetShouldSignHeaderUserData(void *userData) noexcept + { + m_config.should_sign_header_ud = userData; + } + + const Crt::String &AwsSigningConfig::GetSignedBodyValue() const noexcept { return m_signedBodyValue; } + + void AwsSigningConfig::SetSignedBodyValue(const Crt::String &signedBodyValue) noexcept + { + m_signedBodyValue = signedBodyValue; + m_config.signed_body_value = ByteCursorFromString(m_signedBodyValue); + } + + SignedBodyHeaderType AwsSigningConfig::GetSignedBodyHeader() const noexcept + { + return static_cast<SignedBodyHeaderType>(m_config.signed_body_header); + } + + void AwsSigningConfig::SetSignedBodyHeader(SignedBodyHeaderType signedBodyHeader) noexcept + { + m_config.signed_body_header = static_cast<enum aws_signed_body_header_type>(signedBodyHeader); + } + + uint64_t AwsSigningConfig::GetExpirationInSeconds() const noexcept + { + return m_config.expiration_in_seconds; + } + + void AwsSigningConfig::SetExpirationInSeconds(uint64_t expirationInSeconds) noexcept + { + m_config.expiration_in_seconds = expirationInSeconds; + } + + const std::shared_ptr<ICredentialsProvider> &AwsSigningConfig::GetCredentialsProvider() const noexcept + { + return m_credentialsProvider; + } + + void AwsSigningConfig::SetCredentialsProvider( + const std::shared_ptr<ICredentialsProvider> &credsProvider) noexcept + { + m_credentialsProvider = credsProvider; + m_config.credentials_provider = m_credentialsProvider->GetUnderlyingHandle(); + } + + const std::shared_ptr<Credentials> &AwsSigningConfig::GetCredentials() const noexcept + { + return m_credentials; + } + + void AwsSigningConfig::SetCredentials(const std::shared_ptr<Credentials> &credentials) noexcept + { + m_credentials = credentials; + m_config.credentials = m_credentials->GetUnderlyingHandle(); + } + + const struct aws_signing_config_aws *AwsSigningConfig::GetUnderlyingHandle() const noexcept + { + return &m_config; + } + + ///////////////////////////////////////////////////////////////////////////////////////////// + + Sigv4HttpRequestSigner::Sigv4HttpRequestSigner(Aws::Crt::Allocator *allocator) + : IHttpRequestSigner(), m_allocator(allocator) + { + } + + struct HttpSignerCallbackData + { + HttpSignerCallbackData() : Alloc(nullptr) {} + Allocator *Alloc; + ScopedResource<struct aws_signable> Signable; + OnHttpRequestSigningComplete OnRequestSigningComplete; + std::shared_ptr<Http::HttpRequest> Request; + }; + + static void s_http_signing_complete_fn(struct aws_signing_result *result, int errorCode, void *userdata) + { + auto cbData = reinterpret_cast<HttpSignerCallbackData *>(userdata); + + if (errorCode == AWS_OP_SUCCESS) + { + aws_apply_signing_result_to_http_request( + cbData->Request->GetUnderlyingMessage(), cbData->Alloc, result); + } + + cbData->OnRequestSigningComplete(cbData->Request, errorCode); + Crt::Delete(cbData, cbData->Alloc); + } + + bool Sigv4HttpRequestSigner::SignRequest( + const std::shared_ptr<Aws::Crt::Http::HttpRequest> &request, + const ISigningConfig &config, + const OnHttpRequestSigningComplete &completionCallback) + { + if (config.GetType() != SigningConfigType::Aws) + { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return false; + } + + auto awsSigningConfig = static_cast<const AwsSigningConfig *>(&config); + + if (!awsSigningConfig->GetCredentialsProvider() && !awsSigningConfig->GetCredentials()) + { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return false; + } + + auto signerCallbackData = Crt::New<HttpSignerCallbackData>(m_allocator); + + if (!signerCallbackData) + { + return false; + } + + signerCallbackData->Alloc = m_allocator; + signerCallbackData->OnRequestSigningComplete = completionCallback; + signerCallbackData->Request = request; + signerCallbackData->Signable = ScopedResource<struct aws_signable>( + aws_signable_new_http_request(m_allocator, request->GetUnderlyingMessage()), aws_signable_destroy); + + return aws_sign_request_aws( + m_allocator, + signerCallbackData->Signable.get(), + (aws_signing_config_base *)awsSigningConfig->GetUnderlyingHandle(), + s_http_signing_complete_fn, + signerCallbackData) == AWS_OP_SUCCESS; + } + } // namespace Auth + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/crypto/HMAC.cpp b/contrib/restricted/aws/aws-crt-cpp/source/crypto/HMAC.cpp new file mode 100644 index 0000000000..403aff7d3f --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/crypto/HMAC.cpp @@ -0,0 +1,173 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/crypto/HMAC.h> + +#include <aws/cal/hmac.h> + +namespace Aws +{ + namespace Crt + { + namespace Crypto + { + bool ComputeSHA256HMAC( + Allocator *allocator, + const ByteCursor &secret, + const ByteCursor &input, + ByteBuf &output, + size_t truncateTo) noexcept + { + return aws_sha256_hmac_compute(allocator, &secret, &input, &output, truncateTo) == AWS_OP_SUCCESS; + } + + bool ComputeSHA256HMAC( + const ByteCursor &secret, + const ByteCursor &input, + ByteBuf &output, + size_t truncateTo) noexcept + { + return aws_sha256_hmac_compute(ApiAllocator(), &secret, &input, &output, truncateTo) == AWS_OP_SUCCESS; + } + + HMAC::HMAC(aws_hmac *hmac) noexcept : m_hmac(hmac), m_good(false), m_lastError(0) + { + if (hmac) + { + m_good = true; + } + else + { + m_lastError = aws_last_error(); + } + } + + HMAC::~HMAC() + { + if (m_hmac) + { + aws_hmac_destroy(m_hmac); + m_hmac = nullptr; + } + } + + HMAC::HMAC(HMAC &&toMove) : m_hmac(toMove.m_hmac), m_good(toMove.m_good), m_lastError(toMove.m_lastError) + { + toMove.m_hmac = nullptr; + toMove.m_good = false; + } + + HMAC &HMAC::operator=(HMAC &&toMove) + { + if (&toMove != this) + { + *this = HMAC(std::move(toMove)); + } + + return *this; + } + + HMAC HMAC::CreateSHA256HMAC(Allocator *allocator, const ByteCursor &secret) noexcept + { + return HMAC(aws_sha256_hmac_new(allocator, &secret)); + } + + HMAC HMAC::CreateSHA256HMAC(const ByteCursor &secret) noexcept + { + return HMAC(aws_sha256_hmac_new(ApiAllocator(), &secret)); + } + + bool HMAC::Update(const ByteCursor &toHMAC) noexcept + { + if (*this) + { + if (aws_hmac_update(m_hmac, &toHMAC)) + { + m_lastError = aws_last_error(); + m_good = false; + return false; + } + return true; + } + + return false; + } + + bool HMAC::Digest(ByteBuf &output, size_t truncateTo) noexcept + { + if (*this) + { + m_good = false; + if (aws_hmac_finalize(m_hmac, &output, truncateTo)) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + return false; + } + + aws_hmac_vtable ByoHMAC::s_Vtable = { + "aws-crt-cpp-byo-crypto-hmac", + "aws-crt-cpp-byo-crypto", + ByoHMAC::s_Destroy, + ByoHMAC::s_Update, + ByoHMAC::s_Finalize, + }; + + ByoHMAC::ByoHMAC(size_t digestSize, const ByteCursor &, Allocator *allocator) + { + AWS_ZERO_STRUCT(m_hmacValue); + m_hmacValue.impl = reinterpret_cast<void *>(this); + m_hmacValue.digest_size = digestSize; + m_hmacValue.allocator = allocator; + m_hmacValue.good = true; + m_hmacValue.vtable = &s_Vtable; + } + + aws_hmac *ByoHMAC::SeatForCInterop(const std::shared_ptr<ByoHMAC> &selfRef) + { + AWS_FATAL_ASSERT(this == selfRef.get()); + m_selfReference = selfRef; + return &m_hmacValue; + } + + void ByoHMAC::s_Destroy(struct aws_hmac *hmac) + { + auto *byoHash = reinterpret_cast<ByoHMAC *>(hmac->impl); + byoHash->m_selfReference = nullptr; + } + + int ByoHMAC::s_Update(struct aws_hmac *hmac, const struct aws_byte_cursor *buf) + { + auto *byoHmac = reinterpret_cast<ByoHMAC *>(hmac->impl); + if (!byoHmac->m_hmacValue.good) + { + return aws_raise_error(AWS_ERROR_INVALID_STATE); + } + if (!byoHmac->UpdateInternal(*buf)) + { + byoHmac->m_hmacValue.good = false; + return AWS_OP_ERR; + } + return AWS_OP_SUCCESS; + } + + int ByoHMAC::s_Finalize(struct aws_hmac *hmac, struct aws_byte_buf *out) + { + auto *byoHmac = reinterpret_cast<ByoHMAC *>(hmac->impl); + if (!byoHmac->m_hmacValue.good) + { + return aws_raise_error(AWS_ERROR_INVALID_STATE); + } + + bool success = byoHmac->DigestInternal(*out); + byoHmac->m_hmacValue.good = false; + return success ? AWS_OP_SUCCESS : AWS_OP_ERR; + } + } // namespace Crypto + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/crypto/Hash.cpp b/contrib/restricted/aws/aws-crt-cpp/source/crypto/Hash.cpp new file mode 100644 index 0000000000..273d94da5e --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/crypto/Hash.cpp @@ -0,0 +1,174 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/crypto/Hash.h> + +#include <aws/cal/hash.h> + +namespace Aws +{ + namespace Crt + { + namespace Crypto + { + bool ComputeSHA256( + Allocator *allocator, + const ByteCursor &input, + ByteBuf &output, + size_t truncateTo) noexcept + { + return aws_sha256_compute(allocator, &input, &output, truncateTo) == AWS_OP_SUCCESS; + } + + bool ComputeSHA256(const ByteCursor &input, ByteBuf &output, size_t truncateTo) noexcept + { + return aws_sha256_compute(ApiAllocator(), &input, &output, truncateTo) == AWS_OP_SUCCESS; + } + + bool ComputeMD5(Allocator *allocator, const ByteCursor &input, ByteBuf &output, size_t truncateTo) noexcept + { + return aws_md5_compute(allocator, &input, &output, truncateTo) == AWS_OP_SUCCESS; + } + + bool ComputeMD5(const ByteCursor &input, ByteBuf &output, size_t truncateTo) noexcept + { + return aws_md5_compute(ApiAllocator(), &input, &output, truncateTo) == AWS_OP_SUCCESS; + } + + Hash::Hash(aws_hash *hash) noexcept : m_hash(hash), m_good(false), m_lastError(0) + { + if (hash) + { + m_good = true; + } + else + { + m_lastError = aws_last_error(); + } + } + + Hash::~Hash() + { + if (m_hash) + { + aws_hash_destroy(m_hash); + m_hash = nullptr; + } + } + + Hash::Hash(Hash &&toMove) : m_hash(toMove.m_hash), m_good(toMove.m_good), m_lastError(toMove.m_lastError) + { + toMove.m_hash = nullptr; + toMove.m_good = false; + } + + Hash &Hash::operator=(Hash &&toMove) + { + if (&toMove != this) + { + *this = Hash(std::move(toMove)); + } + + return *this; + } + + Hash Hash::CreateSHA256(Allocator *allocator) noexcept { return Hash(aws_sha256_new(allocator)); } + + Hash Hash::CreateMD5(Allocator *allocator) noexcept { return Hash(aws_md5_new(allocator)); } + + bool Hash::Update(const ByteCursor &toHash) noexcept + { + if (*this) + { + if (aws_hash_update(m_hash, &toHash)) + { + m_lastError = aws_last_error(); + m_good = false; + return false; + } + return true; + } + + return false; + } + + bool Hash::Digest(ByteBuf &output, size_t truncateTo) noexcept + { + if (*this) + { + m_good = false; + if (aws_hash_finalize(m_hash, &output, truncateTo)) + { + m_lastError = aws_last_error(); + return false; + } + return true; + } + + return false; + } + + aws_hash_vtable ByoHash::s_Vtable = { + "aws-crt-cpp-byo-crypto-hash", + "aws-crt-cpp-byo-crypto", + ByoHash::s_Destroy, + ByoHash::s_Update, + ByoHash::s_Finalize, + }; + + ByoHash::ByoHash(size_t digestSize, Allocator *allocator) + { + AWS_ZERO_STRUCT(m_hashValue); + m_hashValue.vtable = &s_Vtable; + m_hashValue.allocator = allocator; + m_hashValue.impl = reinterpret_cast<void *>(this); + m_hashValue.digest_size = digestSize; + m_hashValue.good = true; + } + + ByoHash::~ByoHash() {} + + aws_hash *ByoHash::SeatForCInterop(const std::shared_ptr<ByoHash> &selfRef) + { + AWS_FATAL_ASSERT(this == selfRef.get()); + m_selfReference = selfRef; + return &m_hashValue; + } + + void ByoHash::s_Destroy(struct aws_hash *hash) + { + auto *byoHash = reinterpret_cast<ByoHash *>(hash->impl); + byoHash->m_selfReference = nullptr; + } + + int ByoHash::s_Update(struct aws_hash *hash, const struct aws_byte_cursor *buf) + { + auto *byoHash = reinterpret_cast<ByoHash *>(hash->impl); + if (!byoHash->m_hashValue.good) + { + return aws_raise_error(AWS_ERROR_INVALID_STATE); + } + if (!byoHash->UpdateInternal(*buf)) + { + byoHash->m_hashValue.good = false; + return AWS_OP_ERR; + } + return AWS_OP_SUCCESS; + } + + int ByoHash::s_Finalize(struct aws_hash *hash, struct aws_byte_buf *out) + { + auto *byoHash = reinterpret_cast<ByoHash *>(hash->impl); + if (!byoHash->m_hashValue.good) + { + return aws_raise_error(AWS_ERROR_INVALID_STATE); + } + + bool success = byoHash->DigestInternal(*out); + byoHash->m_hashValue.good = false; + return success ? AWS_OP_SUCCESS : AWS_OP_ERR; + } + } // namespace Crypto + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/endpoints/RuleEngine.cpp b/contrib/restricted/aws/aws-crt-cpp/source/endpoints/RuleEngine.cpp new file mode 100644 index 0000000000..b319508c93 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/endpoints/RuleEngine.cpp @@ -0,0 +1,169 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/common/string.h> +#include <aws/crt/Api.h> +#include <aws/crt/endpoints/RuleEngine.h> +#include <aws/sdkutils/endpoints_rule_engine.h> +#include <aws/sdkutils/partitions.h> + +namespace Aws +{ + namespace Crt + { + namespace Endpoints + { + + RequestContext::RequestContext(Allocator *allocator) noexcept : m_allocator(allocator) + { + m_requestContext = aws_endpoints_request_context_new(allocator); + } + + RequestContext::~RequestContext() + { + m_requestContext = aws_endpoints_request_context_release(m_requestContext); + } + + bool RequestContext::AddString(const ByteCursor &name, const ByteCursor &value) + { + return AWS_OP_SUCCESS != + aws_endpoints_request_context_add_string(m_allocator, m_requestContext, name, value); + } + + bool RequestContext::AddBoolean(const ByteCursor &name, bool value) + { + return AWS_OP_SUCCESS != + aws_endpoints_request_context_add_boolean(m_allocator, m_requestContext, name, value); + } + + ResolutionOutcome::ResolutionOutcome(aws_endpoints_resolved_endpoint *impl) : m_resolvedEndpoint(impl) {} + + ResolutionOutcome::ResolutionOutcome(ResolutionOutcome &&toMove) noexcept + : m_resolvedEndpoint(toMove.m_resolvedEndpoint) + { + toMove.m_resolvedEndpoint = nullptr; + } + + ResolutionOutcome &ResolutionOutcome::operator=(ResolutionOutcome &&toMove) + { + if (&toMove != this) + { + *this = ResolutionOutcome(std::move(toMove)); + } + + return *this; + } + + ResolutionOutcome::~ResolutionOutcome() { aws_endpoints_resolved_endpoint_release(m_resolvedEndpoint); } + + bool ResolutionOutcome::IsEndpoint() const noexcept + { + return AWS_ENDPOINTS_RESOLVED_ENDPOINT == aws_endpoints_resolved_endpoint_get_type(m_resolvedEndpoint); + } + + bool ResolutionOutcome::IsError() const noexcept + { + return AWS_ENDPOINTS_RESOLVED_ERROR == aws_endpoints_resolved_endpoint_get_type(m_resolvedEndpoint); + } + + Optional<StringView> ResolutionOutcome::GetUrl() const + { + ByteCursor url; + if (aws_endpoints_resolved_endpoint_get_url(m_resolvedEndpoint, &url)) + { + return Optional<StringView>(); + } + + return Optional<StringView>(ByteCursorToStringView(url)); + } + + inline StringView CrtStringToStringView(const aws_string *s) + { + ByteCursor key = aws_byte_cursor_from_string(s); + return ByteCursorToStringView(key); + } + + Optional<UnorderedMap<StringView, Vector<StringView>>> ResolutionOutcome::GetHeaders() const + { + const aws_hash_table *resolved_headers = nullptr; + + if (aws_endpoints_resolved_endpoint_get_headers(m_resolvedEndpoint, &resolved_headers)) + { + return Optional<UnorderedMap<StringView, Vector<StringView>>>(); + } + + UnorderedMap<StringView, Vector<StringView>> headers; + for (struct aws_hash_iter iter = aws_hash_iter_begin(resolved_headers); !aws_hash_iter_done(&iter); + aws_hash_iter_next(&iter)) + { + ByteCursor key = aws_byte_cursor_from_string((const aws_string *)iter.element.key); + const aws_array_list *array = (const aws_array_list *)iter.element.value; + headers.emplace(std::make_pair( + ByteCursorToStringView(key), + ArrayListToVector<aws_string *, StringView>(array, CrtStringToStringView))); + } + + return Optional<UnorderedMap<StringView, Vector<StringView>>>(headers); + } + + Optional<StringView> ResolutionOutcome::GetProperties() const + { + ByteCursor properties; + if (aws_endpoints_resolved_endpoint_get_properties(m_resolvedEndpoint, &properties)) + { + return Optional<StringView>(); + } + + return Optional<StringView>(ByteCursorToStringView(properties)); + } + + Optional<StringView> ResolutionOutcome::GetError() const + { + ByteCursor error; + if (aws_endpoints_resolved_endpoint_get_error(m_resolvedEndpoint, &error)) + { + return Optional<StringView>(); + } + + return Optional<StringView>(ByteCursorToStringView(error)); + } + + RuleEngine::RuleEngine( + const ByteCursor &rulesetCursor, + const ByteCursor &partitionsCursor, + Allocator *allocator) noexcept + : m_ruleEngine(nullptr) + { + auto ruleset = aws_endpoints_ruleset_new_from_string(allocator, rulesetCursor); + auto partitions = aws_partitions_config_new_from_string(allocator, partitionsCursor); + if (ruleset != NULL && partitions != NULL) + { + m_ruleEngine = aws_endpoints_rule_engine_new(allocator, ruleset, partitions); + } + + if (ruleset != NULL) + { + aws_endpoints_ruleset_release(ruleset); + } + + if (partitions != NULL) + { + aws_partitions_config_release(partitions); + } + } + + RuleEngine::~RuleEngine() { m_ruleEngine = aws_endpoints_rule_engine_release(m_ruleEngine); } + + Optional<ResolutionOutcome> RuleEngine::Resolve(const RequestContext &context) const + { + aws_endpoints_resolved_endpoint *resolved = NULL; + if (aws_endpoints_rule_engine_resolve(m_ruleEngine, context.GetNativeHandle(), &resolved)) + { + return Optional<ResolutionOutcome>(); + } + return Optional<ResolutionOutcome>(ResolutionOutcome(resolved)); + } + } // namespace Endpoints + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/external/cJSON.cpp b/contrib/restricted/aws/aws-crt-cpp/source/external/cJSON.cpp new file mode 100644 index 0000000000..f3db28d400 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/external/cJSON.cpp @@ -0,0 +1,3120 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/** MODIFICATIONS: + * valueInt was moved up to improve alignment. + * Wrap all symbols in the Aws namespace as a short-term collision resolution + * Replace strcpy() with strncpy() + * + * Modifications licensed under: + * + * Copyright 2010-2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). + * You may not use this file except in compliance with the License. + * A copy of the License is located at + * + * http://aws.amazon.com/apache2.0 + * + * or in the "license" file accompanying this file. This file is distributed + * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ + +/* cJSON */ +/* JSON parser in C. */ + +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + +#include <string.h> +#include <stdio.h> +#include <math.h> +#include <stdlib.h> +#include <limits.h> +#include <ctype.h> +#include <float.h> + +#ifdef ENABLE_LOCALES +#include <locale.h> +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include <aws/crt/external/cJSON.h> + +/* define our own boolean type */ +// #ifdef true +// #undef true +// #endif +// #define true ((cJSON_bool)1) + +// #ifdef false +// #undef false +// #endif +// #define false ((cJSON_bool)0) + +/* define isnan and isinf for ANSI C, if in C99 or above, isnan and isinf has been defined in math.h */ +#ifndef isinf +#define isinf(d) (isnan((d - d)) && !isnan(d)) +#endif +#ifndef isnan +#define isnan(d) (d != d) +#endif + +#ifndef NAN +#define NAN 0.0/0.0 +#endif + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +namespace Aws { + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +CJSON_PUBLIC(char *) cJSON_GetStringValue(const cJSON * const item) +{ + if (!cJSON_IsString(item)) + { + return NULL; + } + + return item->valuestring; +} + +CJSON_PUBLIC(double) cJSON_GetNumberValue(const cJSON * const item) +{ + if (!cJSON_IsNumber(item)) + { + return (double) NAN; + } + + return item->valuedouble; +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 14) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + snprintf(version, sizeof(version), "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(CJSON_CDECL *allocate)(size_t size); + void (CJSON_CDECL *deallocate)(void *pointer); + void *(CJSON_CDECL *reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dllimport '...' is not static */ +static void * CJSON_CDECL internal_malloc(size_t size) +{ + return malloc(size); +} +static void CJSON_CDECL internal_free(void *pointer) +{ + free(pointer); +} +static void * CJSON_CDECL internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +/* strlen of character literals resolved at compile time */ +#define static_strlen(string_literal) (sizeof(string_literal) - sizeof("")) + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= (double)INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +CJSON_PUBLIC(char*) cJSON_SetValuestring(cJSON *object, const char *valuestring) +{ + char *copy = NULL; + /* if object's type is not cJSON_String or is cJSON_IsReference, it should not set valuestring */ + if (!(object->type & cJSON_String) || (object->type & cJSON_IsReference)) + { + return NULL; + } + if (strlen(valuestring) <= strlen(object->valuestring)) + { + memcpy(object->valuestring, valuestring, strlen(valuestring) + 1); + return object->valuestring; + } + copy = (char*) cJSON_strdup((const unsigned char*)valuestring, &global_hooks); + if (copy == NULL) + { + return NULL; + } + if (object->valuestring != NULL) + { + cJSON_free(object->valuestring); + } + object->valuestring = copy; + + return copy; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* securely comparison of floating-point variables */ +static cJSON_bool compare_double(double a, double b) +{ + double maxVal = fabs(a) > fabs(b) ? fabs(a) : fabs(b); + return (fabs(a - b) <= maxVal * DBL_EPSILON); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26] = {0}; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test = 0.0; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if (isnan(d) || isinf(d)) + { + length = snprintf((char*)number_buffer, sizeof(number_buffer), "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || !compare_double((double)test, d)) + { + /* If not, print with 17 decimal places of precision */ + length = snprintf((char*)number_buffer, sizeof(number_buffer), "%1.17g", d); + } + } + + /* snprintf failed or buffer overrun occurred */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strncpy((char*)output, "\"\"", strlen("\"\"") + 1); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + snprintf((char*)output_pointer, output_buffer->length - (output_pointer - output_buffer->buffer), "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + if (cannot_access_at_index(buffer, 0)) + { + return buffer; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + size_t buffer_length; + + if (NULL == value) + { + return NULL; + } + + /* Adding null character size due to require_null_terminated. */ + buffer_length = strlen(value) + sizeof(""); + + return cJSON_ParseWithLengthOpts(value, buffer_length, return_parse_end, require_null_terminated); +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLengthOpts(const char *value, size_t buffer_length, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL || 0 == buffer_length) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = buffer_length; + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length) +{ + return cJSON_ParseWithLengthOpts(value, buffer_length, 0, 0); +} + +#define cjson_min(a, b) (((a) < (b)) ? (a) : (b)) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + if (printed == NULL) { + goto fail; + } + buffer->buffer = NULL; + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((length < 0) || (buffer == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buffer; + p.length = (size_t)length; + p.offset = 0; + p.noalloc = true; + p.format = format; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strncpy((char*)output, "null", strlen("null") + 1); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strncpy((char*)output, "false", strlen("false") + 1); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strncpy((char*)output, "true", strlen("true") + 1); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* failed to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + if (head != NULL) { + head->prev = current_item; + } + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = ((size_t)(output_buffer->format ? 1 : 0) + (size_t)(current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (current_element->string != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + if ((current_element == NULL) || (current_element->string == NULL)) { + return NULL; + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL) || (array == item)) + { + return false; + } + + child = array->child; + /* + * To find the last item in array quickly, we use prev in array + */ + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + item->prev = item; + item->next = NULL; + } + else + { + /* append to the end */ + if (child->prev) + { + suffix_object(child->prev, item); + array->child->prev = item; + } + } + + return true; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + return add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL) || (object == item)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + return add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return false; + } + + return add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return false; + } + + return add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item != parent->child) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + else if (item->next == NULL) + { + /* last element */ + parent->child->prev = item->prev; + } + + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(cJSON_bool) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return false; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + return add_item_to_array(array, newitem); + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (parent->child == item) + { + if (parent->child->prev == parent->child) + { + replacement->prev = replacement; + } + parent->child = replacement; + } + else + { /* + * To find the last item in array quickly, we use prev in array. + * We can't modify the last item's next pointer where this item was the parent's child + */ + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (replacement->next == NULL) + { + parent->child->prev = replacement; + } + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return false; + } + + return cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + return cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + return replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = boolean ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= (double)INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + a->child->prev = n; + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + a->child->prev = n; + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + a->child->prev = n; + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char *const *strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + a->child->prev = n; + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + if (newitem && newitem->child) + { + newitem->child->prev = newchild; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +static void skip_oneline_comment(char **input) +{ + *input += static_strlen("//"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if ((*input)[0] == '\n') { + *input += static_strlen("\n"); + return; + } + } +} + +static void skip_multiline_comment(char **input) +{ + *input += static_strlen("/*"); + + for (; (*input)[0] != '\0'; ++(*input)) + { + if (((*input)[0] == '*') && ((*input)[1] == '/')) + { + *input += static_strlen("*/"); + return; + } + } +} + +static void minify_string(char **input, char **output) { + (*output)[0] = (*input)[0]; + *input += static_strlen("\""); + *output += static_strlen("\""); + + + for (; (*input)[0] != '\0'; (void)++(*input), ++(*output)) { + (*output)[0] = (*input)[0]; + + if ((*input)[0] == '\"') { + (*output)[0] = '\"'; + *input += static_strlen("\""); + *output += static_strlen("\""); + return; + } else if (((*input)[0] == '\\') && ((*input)[1] == '\"')) { + (*output)[1] = (*input)[1]; + *input += static_strlen("\""); + *output += static_strlen("\""); + } + } +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + char *into = json; + + if (json == NULL) + { + return; + } + + while (json[0] != '\0') + { + switch (json[0]) + { + case ' ': + case '\t': + case '\r': + case '\n': + json++; + break; + + case '/': + if (json[1] == '/') + { + skip_oneline_comment(&json); + } + else if (json[1] == '*') + { + skip_multiline_comment(&json); + } else { + json++; + } + break; + + case '\"': + minify_string(&json, (char**)&into); + break; + + default: + into[0] = json[0]; + json++; + into++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (compare_double(a->valuedouble, b->valuedouble)) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} + +} diff --git a/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnection.cpp b/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnection.cpp new file mode 100644 index 0000000000..f8ed94cefc --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnection.cpp @@ -0,0 +1,400 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/Api.h> +#include <aws/crt/http/HttpConnection.h> +#include <aws/crt/http/HttpProxyStrategy.h> +#include <aws/crt/http/HttpRequestResponse.h> +#include <aws/crt/io/Bootstrap.h> + +namespace Aws +{ + namespace Crt + { + namespace Http + { + /* This exists to handle aws_http_connection's shutdown callback, which might fire after + * HttpClientConnection has been destroyed. */ + struct ConnectionCallbackData + { + explicit ConnectionCallbackData(Allocator *allocator) : allocator(allocator) {} + std::weak_ptr<HttpClientConnection> connection; + Allocator *allocator; + OnConnectionSetup onConnectionSetup; + OnConnectionShutdown onConnectionShutdown; + }; + + class UnmanagedConnection final : public HttpClientConnection + { + public: + UnmanagedConnection(aws_http_connection *connection, Aws::Crt::Allocator *allocator) + : HttpClientConnection(connection, allocator) + { + } + + ~UnmanagedConnection() override + { + if (m_connection) + { + aws_http_connection_release(m_connection); + m_connection = nullptr; + } + } + }; + + void HttpClientConnection::s_onClientConnectionSetup( + struct aws_http_connection *connection, + int errorCode, + void *user_data) noexcept + { + /** + * Allocate an HttpClientConnection and seat it to `ConnectionCallbackData`'s shared_ptr. + */ + auto *callbackData = static_cast<ConnectionCallbackData *>(user_data); + if (!errorCode) + { + auto connectionObj = std::allocate_shared<UnmanagedConnection>( + Aws::Crt::StlAllocator<UnmanagedConnection>(), connection, callbackData->allocator); + + if (connectionObj) + { + callbackData->connection = connectionObj; + callbackData->onConnectionSetup(std::move(connectionObj), errorCode); + return; + } + + aws_http_connection_release(connection); + errorCode = aws_last_error(); + } + + callbackData->onConnectionSetup(nullptr, errorCode); + Delete(callbackData, callbackData->allocator); + } + + void HttpClientConnection::s_onClientConnectionShutdown( + struct aws_http_connection *connection, + int errorCode, + void *user_data) noexcept + { + (void)connection; + auto *callbackData = static_cast<ConnectionCallbackData *>(user_data); + + /* Don't invoke callback if the connection object has expired. */ + if (auto connectionPtr = callbackData->connection.lock()) + { + callbackData->onConnectionShutdown(*connectionPtr, errorCode); + } + + Delete(callbackData, callbackData->allocator); + } + + bool HttpClientConnection::CreateConnection( + const HttpClientConnectionOptions &connectionOptions, + Allocator *allocator) noexcept + { + AWS_FATAL_ASSERT(connectionOptions.OnConnectionSetupCallback); + AWS_FATAL_ASSERT(connectionOptions.OnConnectionShutdownCallback); + + if (connectionOptions.TlsOptions && !(*connectionOptions.TlsOptions)) + { + AWS_LOGF_ERROR( + AWS_LS_HTTP_GENERAL, + "Cannot create HttpClientConnection: connectionOptions contains invalid TlsOptions."); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return false; + } + + if (connectionOptions.ProxyOptions) + { + const auto &proxyOpts = connectionOptions.ProxyOptions.value(); + + if (proxyOpts.TlsOptions && !(*proxyOpts.TlsOptions)) + { + AWS_LOGF_ERROR( + AWS_LS_HTTP_GENERAL, + "Cannot create HttpClientConnection: connectionOptions has ProxyOptions that contain " + "invalid TlsOptions."); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return false; + } + } + + auto *callbackData = New<ConnectionCallbackData>(allocator, allocator); + + if (!callbackData) + { + return false; + } + callbackData->onConnectionShutdown = connectionOptions.OnConnectionShutdownCallback; + callbackData->onConnectionSetup = connectionOptions.OnConnectionSetupCallback; + + aws_http_client_connection_options options; + AWS_ZERO_STRUCT(options); + options.self_size = sizeof(aws_http_client_connection_options); + + if (options.bootstrap != nullptr) + { + options.bootstrap = connectionOptions.Bootstrap->GetUnderlyingHandle(); + } + else + { + options.bootstrap = ApiHandle::GetOrCreateStaticDefaultClientBootstrap()->GetUnderlyingHandle(); + } + + if (connectionOptions.TlsOptions) + { + /* This is verified earlier in this function. */ + AWS_FATAL_ASSERT(*connectionOptions.TlsOptions); + + options.tls_options = + const_cast<aws_tls_connection_options *>(connectionOptions.TlsOptions->GetUnderlyingHandle()); + } + options.allocator = allocator; + options.user_data = callbackData; + options.host_name = aws_byte_cursor_from_c_str(connectionOptions.HostName.c_str()); + options.port = connectionOptions.Port; + options.initial_window_size = connectionOptions.InitialWindowSize; + options.socket_options = &connectionOptions.SocketOptions.GetImpl(); + options.on_setup = HttpClientConnection::s_onClientConnectionSetup; + options.on_shutdown = HttpClientConnection::s_onClientConnectionShutdown; + options.manual_window_management = connectionOptions.ManualWindowManagement; + + aws_http_proxy_options proxyOptions; + AWS_ZERO_STRUCT(proxyOptions); + if (connectionOptions.ProxyOptions) + { + const auto &proxyOpts = connectionOptions.ProxyOptions.value(); + + /* This is verified earlier in this function. */ + AWS_FATAL_ASSERT(!proxyOpts.TlsOptions || *proxyOpts.TlsOptions); + + proxyOpts.InitializeRawProxyOptions(proxyOptions); + + options.proxy_options = &proxyOptions; + } + + if (aws_http_client_connect(&options)) + { + Delete(callbackData, allocator); + return false; + } + + return true; + } + + HttpClientConnection::HttpClientConnection(aws_http_connection *connection, Allocator *allocator) noexcept + : m_connection(connection), m_allocator(allocator), m_lastError(AWS_ERROR_SUCCESS) + { + } + + std::shared_ptr<HttpClientStream> HttpClientConnection::NewClientStream( + const HttpRequestOptions &requestOptions) noexcept + { + AWS_ASSERT(requestOptions.onIncomingHeaders); + AWS_ASSERT(requestOptions.onStreamComplete); + + aws_http_make_request_options options; + AWS_ZERO_STRUCT(options); + options.self_size = sizeof(aws_http_make_request_options); + options.request = requestOptions.request->GetUnderlyingMessage(); + options.on_response_body = HttpStream::s_onIncomingBody; + options.on_response_headers = HttpStream::s_onIncomingHeaders; + options.on_response_header_block_done = HttpStream::s_onIncomingHeaderBlockDone; + options.on_complete = HttpStream::s_onStreamComplete; + + /* Do the same ref counting trick we did with HttpClientConnection. We need to maintain a reference + * internally (regardless of what the user does), until the Stream shuts down. */ + auto *toSeat = static_cast<HttpClientStream *>(aws_mem_acquire(m_allocator, sizeof(HttpClientStream))); + + if (toSeat) + { + toSeat = new (toSeat) HttpClientStream(this->shared_from_this()); + + Allocator *captureAllocator = m_allocator; + std::shared_ptr<HttpClientStream> stream( + toSeat, + [captureAllocator](HttpStream *stream) { Delete(stream, captureAllocator); }, + StlAllocator<HttpClientStream>(captureAllocator)); + + stream->m_onIncomingBody = requestOptions.onIncomingBody; + stream->m_onIncomingHeaders = requestOptions.onIncomingHeaders; + stream->m_onIncomingHeadersBlockDone = requestOptions.onIncomingHeadersBlockDone; + stream->m_onStreamComplete = requestOptions.onStreamComplete; + stream->m_callbackData.allocator = m_allocator; + + // we purposefully do not set m_callbackData::stream because we don't want the reference count + // incremented until the request is kicked off via HttpClientStream::Activate(). Activate() + // increments the ref count. + options.user_data = &stream->m_callbackData; + stream->m_stream = aws_http_connection_make_request(m_connection, &options); + + if (!stream->m_stream) + { + stream = nullptr; + m_lastError = aws_last_error(); + return nullptr; + } + + return stream; + } + + m_lastError = aws_last_error(); + return nullptr; + } + + bool HttpClientConnection::IsOpen() const noexcept { return aws_http_connection_is_open(m_connection); } + + void HttpClientConnection::Close() noexcept { aws_http_connection_close(m_connection); } + + HttpVersion HttpClientConnection::GetVersion() noexcept + { + return (HttpVersion)aws_http_connection_get_version(m_connection); + } + + int HttpStream::s_onIncomingHeaders( + struct aws_http_stream *, + enum aws_http_header_block headerBlock, + const struct aws_http_header *headerArray, + size_t numHeaders, + void *userData) noexcept + { + auto callbackData = static_cast<ClientStreamCallbackData *>(userData); + callbackData->stream->m_onIncomingHeaders(*callbackData->stream, headerBlock, headerArray, numHeaders); + + return AWS_OP_SUCCESS; + } + + int HttpStream::s_onIncomingHeaderBlockDone( + struct aws_http_stream *, + enum aws_http_header_block headerBlock, + void *userData) noexcept + { + auto callbackData = static_cast<ClientStreamCallbackData *>(userData); + + if (callbackData->stream->m_onIncomingHeadersBlockDone) + { + callbackData->stream->m_onIncomingHeadersBlockDone(*callbackData->stream, headerBlock); + } + + return AWS_OP_SUCCESS; + } + + int HttpStream::s_onIncomingBody( + struct aws_http_stream *, + const struct aws_byte_cursor *data, + void *userData) noexcept + { + auto callbackData = static_cast<ClientStreamCallbackData *>(userData); + + if (callbackData->stream->m_onIncomingBody) + { + callbackData->stream->m_onIncomingBody(*callbackData->stream, *data); + } + + return AWS_OP_SUCCESS; + } + + void HttpStream::s_onStreamComplete(struct aws_http_stream *, int errorCode, void *userData) noexcept + { + auto callbackData = static_cast<ClientStreamCallbackData *>(userData); + callbackData->stream->m_onStreamComplete(*callbackData->stream, errorCode); + callbackData->stream = nullptr; + } + + HttpStream::HttpStream(const std::shared_ptr<HttpClientConnection> &connection) noexcept + : m_stream(nullptr), m_connection(connection) + { + } + + HttpStream::~HttpStream() + { + if (m_stream) + { + aws_http_stream_release(m_stream); + } + + if (m_connection) + { + m_connection = nullptr; + } + } + + HttpClientConnection &HttpStream::GetConnection() const noexcept { return *m_connection; } + + HttpClientStream::HttpClientStream(const std::shared_ptr<HttpClientConnection> &connection) noexcept + : HttpStream(connection) + { + } + + HttpClientStream::~HttpClientStream() {} + + int HttpClientStream::GetResponseStatusCode() const noexcept + { + int status = 0; + if (!aws_http_stream_get_incoming_response_status(m_stream, &status)) + { + return status; + } + + return -1; + } + + bool HttpClientStream::Activate() noexcept + { + m_callbackData.stream = shared_from_this(); + if (aws_http_stream_activate(m_stream)) + { + m_callbackData.stream = nullptr; + return false; + } + + return true; + } + + void HttpStream::UpdateWindow(std::size_t incrementSize) noexcept + { + aws_http_stream_update_window(m_stream, incrementSize); + } + + HttpClientConnectionProxyOptions::HttpClientConnectionProxyOptions() + : HostName(), Port(0), TlsOptions(), ProxyConnectionType(AwsHttpProxyConnectionType::Legacy), + ProxyStrategy(), AuthType(AwsHttpProxyAuthenticationType::None) + { + } + + void HttpClientConnectionProxyOptions::InitializeRawProxyOptions( + struct aws_http_proxy_options &rawOptions) const + { + AWS_ZERO_STRUCT(rawOptions); + rawOptions.connection_type = (enum aws_http_proxy_connection_type)ProxyConnectionType; + rawOptions.host = aws_byte_cursor_from_c_str(HostName.c_str()); + rawOptions.port = Port; + + if (TlsOptions.has_value()) + { + rawOptions.tls_options = TlsOptions->GetUnderlyingHandle(); + } + + if (ProxyStrategy) + { + rawOptions.proxy_strategy = ProxyStrategy->GetUnderlyingHandle(); + } + + if (AuthType == AwsHttpProxyAuthenticationType::Basic) + { + rawOptions.auth_type = AWS_HPAT_BASIC; + rawOptions.auth_username = ByteCursorFromCString(BasicAuthUsername.c_str()); + rawOptions.auth_password = ByteCursorFromCString(BasicAuthPassword.c_str()); + } + } + + HttpClientConnectionOptions::HttpClientConnectionOptions() + : Bootstrap(nullptr), InitialWindowSize(SIZE_MAX), OnConnectionSetupCallback(), + OnConnectionShutdownCallback(), HostName(), Port(0), SocketOptions(), TlsOptions(), ProxyOptions(), + ManualWindowManagement(false) + { + } + } // namespace Http + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnectionManager.cpp b/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnectionManager.cpp new file mode 100644 index 0000000000..7a13a2bdb1 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/http/HttpConnectionManager.cpp @@ -0,0 +1,236 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/Api.h> +#include <aws/crt/http/HttpConnectionManager.h> +#include <aws/crt/http/HttpProxyStrategy.h> + +#include <algorithm> +#include <aws/http/connection_manager.h> + +namespace Aws +{ + namespace Crt + { + namespace Http + { + struct ConnectionManagerCallbackArgs + { + ConnectionManagerCallbackArgs() = default; + OnClientConnectionAvailable m_onClientConnectionAvailable; + std::shared_ptr<HttpClientConnectionManager> m_connectionManager; + }; + + void HttpClientConnectionManager::s_shutdownCompleted(void *userData) noexcept + { + HttpClientConnectionManager *connectionManager = + reinterpret_cast<HttpClientConnectionManager *>(userData); + connectionManager->m_shutdownPromise.set_value(); + } + + HttpClientConnectionManagerOptions::HttpClientConnectionManagerOptions() noexcept + : ConnectionOptions(), MaxConnections(1), EnableBlockingShutdown(false) + { + } + + std::shared_ptr<HttpClientConnectionManager> HttpClientConnectionManager::NewClientConnectionManager( + const HttpClientConnectionManagerOptions &connectionManagerOptions, + Allocator *allocator) noexcept + { + const Optional<Io::TlsConnectionOptions> &tlsOptions = + connectionManagerOptions.ConnectionOptions.TlsOptions; + + if (tlsOptions && !(*tlsOptions)) + { + AWS_LOGF_ERROR( + AWS_LS_HTTP_GENERAL, + "Cannot create HttpClientConnectionManager: ConnectionOptions contain invalid TLSOptions."); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return nullptr; + } + + const Crt::Optional<Crt::Http::HttpClientConnectionProxyOptions> &proxyOptions = + connectionManagerOptions.ConnectionOptions.ProxyOptions; + + if (proxyOptions && proxyOptions->TlsOptions && !(*proxyOptions->TlsOptions)) + { + AWS_LOGF_ERROR( + AWS_LS_HTTP_GENERAL, + "Cannot create HttpClientConnectionManager: ProxyOptions has ConnectionOptions that contain " + "invalid TLSOptions."); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return nullptr; + } + + auto *toSeat = static_cast<HttpClientConnectionManager *>( + aws_mem_acquire(allocator, sizeof(HttpClientConnectionManager))); + if (toSeat) + { + toSeat = new (toSeat) HttpClientConnectionManager(connectionManagerOptions, allocator); + return std::shared_ptr<HttpClientConnectionManager>( + toSeat, [allocator](HttpClientConnectionManager *manager) { Delete(manager, allocator); }); + } + + return nullptr; + } + + HttpClientConnectionManager::HttpClientConnectionManager( + const HttpClientConnectionManagerOptions &options, + Allocator *allocator) noexcept + : m_allocator(allocator), m_connectionManager(nullptr), m_options(options), m_releaseInvoked(false) + { + const auto &connectionOptions = m_options.ConnectionOptions; + AWS_FATAL_ASSERT(connectionOptions.HostName.size() > 0); + AWS_FATAL_ASSERT(connectionOptions.Port > 0); + + aws_http_connection_manager_options managerOptions; + AWS_ZERO_STRUCT(managerOptions); + + if (connectionOptions.Bootstrap != nullptr) + { + managerOptions.bootstrap = connectionOptions.Bootstrap->GetUnderlyingHandle(); + } + else + { + managerOptions.bootstrap = + ApiHandle::GetOrCreateStaticDefaultClientBootstrap()->GetUnderlyingHandle(); + } + + managerOptions.port = connectionOptions.Port; + managerOptions.max_connections = m_options.MaxConnections; + managerOptions.socket_options = &connectionOptions.SocketOptions.GetImpl(); + managerOptions.initial_window_size = connectionOptions.InitialWindowSize; + + if (options.EnableBlockingShutdown) + { + managerOptions.shutdown_complete_callback = s_shutdownCompleted; + managerOptions.shutdown_complete_user_data = this; + } + else + { + m_shutdownPromise.set_value(); + } + + aws_http_proxy_options proxyOptions; + AWS_ZERO_STRUCT(proxyOptions); + if (connectionOptions.ProxyOptions) + { + /* This is verified by HttpClientConnectionManager::NewClientConnectionManager */ + AWS_FATAL_ASSERT( + !connectionOptions.ProxyOptions->TlsOptions || *connectionOptions.ProxyOptions->TlsOptions); + + const auto &proxyOpts = connectionOptions.ProxyOptions.value(); + proxyOpts.InitializeRawProxyOptions(proxyOptions); + + managerOptions.proxy_options = &proxyOptions; + } + + if (connectionOptions.TlsOptions) + { + /* This is verified by HttpClientConnectionManager::NewClientConnectionManager */ + AWS_FATAL_ASSERT(*connectionOptions.TlsOptions); + + managerOptions.tls_connection_options = + const_cast<aws_tls_connection_options *>(connectionOptions.TlsOptions->GetUnderlyingHandle()); + } + managerOptions.host = aws_byte_cursor_from_c_str(connectionOptions.HostName.c_str()); + + m_connectionManager = aws_http_connection_manager_new(allocator, &managerOptions); + } + + HttpClientConnectionManager::~HttpClientConnectionManager() + { + if (!m_releaseInvoked) + { + aws_http_connection_manager_release(m_connectionManager); + m_shutdownPromise.get_future().get(); + } + m_connectionManager = nullptr; + } + + bool HttpClientConnectionManager::AcquireConnection( + const OnClientConnectionAvailable &onClientConnectionAvailable) noexcept + { + auto connectionManagerCallbackArgs = Aws::Crt::New<ConnectionManagerCallbackArgs>(m_allocator); + if (!connectionManagerCallbackArgs) + { + return false; + } + + connectionManagerCallbackArgs->m_connectionManager = shared_from_this(); + connectionManagerCallbackArgs->m_onClientConnectionAvailable = onClientConnectionAvailable; + + aws_http_connection_manager_acquire_connection( + m_connectionManager, s_onConnectionSetup, connectionManagerCallbackArgs); + return true; + } + + std::future<void> HttpClientConnectionManager::InitiateShutdown() noexcept + { + m_releaseInvoked = true; + aws_http_connection_manager_release(m_connectionManager); + return m_shutdownPromise.get_future(); + } + + class ManagedConnection final : public HttpClientConnection + { + public: + ManagedConnection( + aws_http_connection *connection, + std::shared_ptr<HttpClientConnectionManager> connectionManager) + : HttpClientConnection(connection, connectionManager->m_allocator), + m_connectionManager(std::move(connectionManager)) + { + } + + ~ManagedConnection() override + { + if (m_connection) + { + aws_http_connection_manager_release_connection( + m_connectionManager->m_connectionManager, m_connection); + m_connection = nullptr; + } + } + + private: + std::shared_ptr<HttpClientConnectionManager> m_connectionManager; + }; + + void HttpClientConnectionManager::s_onConnectionSetup( + aws_http_connection *connection, + int errorCode, + void *userData) noexcept + { + auto callbackArgs = static_cast<ConnectionManagerCallbackArgs *>(userData); + std::shared_ptr<HttpClientConnectionManager> manager = callbackArgs->m_connectionManager; + auto callback = std::move(callbackArgs->m_onClientConnectionAvailable); + + Delete(callbackArgs, manager->m_allocator); + + if (errorCode) + { + callback(nullptr, errorCode); + return; + } + + auto allocator = manager->m_allocator; + auto connectionRawObj = Aws::Crt::New<ManagedConnection>(manager->m_allocator, connection, manager); + + if (!connectionRawObj) + { + aws_http_connection_manager_release_connection(manager->m_connectionManager, connection); + callback(nullptr, AWS_ERROR_OOM); + return; + } + auto connectionObj = std::shared_ptr<ManagedConnection>( + connectionRawObj, + [allocator](ManagedConnection *managedConnection) { Delete(managedConnection, allocator); }); + + callback(connectionObj, AWS_OP_SUCCESS); + } + + } // namespace Http + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/http/HttpProxyStrategy.cpp b/contrib/restricted/aws/aws-crt-cpp/source/http/HttpProxyStrategy.cpp new file mode 100644 index 0000000000..7b9ef2c3f9 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/http/HttpProxyStrategy.cpp @@ -0,0 +1,196 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/http/HttpProxyStrategy.h> + +#include <aws/common/string.h> +#include <aws/crt/http/HttpConnection.h> +#include <aws/http/proxy.h> + +namespace Aws +{ + namespace Crt + { + namespace Http + { + HttpProxyStrategy::HttpProxyStrategy(struct aws_http_proxy_strategy *strategy) : m_strategy(strategy) {} + + HttpProxyStrategy::~HttpProxyStrategy() { aws_http_proxy_strategy_release(m_strategy); } + + HttpProxyStrategyBasicAuthConfig::HttpProxyStrategyBasicAuthConfig() + : ConnectionType(AwsHttpProxyConnectionType::Legacy), Username(), Password() + { + } + + std::shared_ptr<HttpProxyStrategy> HttpProxyStrategy::CreateBasicHttpProxyStrategy( + const HttpProxyStrategyBasicAuthConfig &config, + Allocator *allocator) + { + struct aws_http_proxy_strategy_basic_auth_options basicConfig; + AWS_ZERO_STRUCT(basicConfig); + basicConfig.proxy_connection_type = (enum aws_http_proxy_connection_type)config.ConnectionType; + basicConfig.user_name = aws_byte_cursor_from_c_str(config.Username.c_str()); + basicConfig.password = aws_byte_cursor_from_c_str(config.Password.c_str()); + + struct aws_http_proxy_strategy *strategy = + aws_http_proxy_strategy_new_basic_auth(allocator, &basicConfig); + if (strategy == NULL) + { + return NULL; + } + + return Aws::Crt::MakeShared<HttpProxyStrategy>(allocator, strategy); + } + + class AdaptiveHttpProxyStrategy : public HttpProxyStrategy + { + public: + AdaptiveHttpProxyStrategy( + Allocator *allocator, + const KerberosGetTokenFunction &kerberosGetToken, + const KerberosGetTokenFunction &ntlmGetCredential, + const NtlmGetTokenFunction &ntlmGetToken) + : HttpProxyStrategy(nullptr), m_Allocator(allocator), m_KerberosGetToken(kerberosGetToken), + m_NtlmGetCredential(ntlmGetCredential), m_NtlmGetToken(ntlmGetToken) + { + } + + void SetStrategy(struct aws_http_proxy_strategy *strategy) + { + aws_http_proxy_strategy_release(m_strategy); + m_strategy = strategy; + } + + static struct aws_string *NtlmGetCredential(void *user_data, int *error_code) + { + AdaptiveHttpProxyStrategy *strategy = reinterpret_cast<AdaptiveHttpProxyStrategy *>(user_data); + + String ntlmCredential; + if (strategy->m_NtlmGetCredential(ntlmCredential)) + { + struct aws_string *token = + aws_string_new_from_c_str(strategy->m_Allocator, ntlmCredential.c_str()); + + if (token != NULL) + { + return token; + } + + *error_code = aws_last_error(); + } + else + { + *error_code = AWS_ERROR_HTTP_PROXY_STRATEGY_TOKEN_RETRIEVAL_FAILURE; + } + + return NULL; + } + + static struct aws_string *KerberosGetToken(void *user_data, int *error_code) + { + AdaptiveHttpProxyStrategy *strategy = reinterpret_cast<AdaptiveHttpProxyStrategy *>(user_data); + + String kerberosToken; + if (strategy->m_KerberosGetToken(kerberosToken)) + { + struct aws_string *token = + aws_string_new_from_c_str(strategy->m_Allocator, kerberosToken.c_str()); + + if (token != NULL) + { + return token; + } + + *error_code = aws_last_error(); + } + else + { + *error_code = AWS_ERROR_HTTP_PROXY_STRATEGY_TOKEN_RETRIEVAL_FAILURE; + } + + return NULL; + } + + static struct aws_string *NtlmGetToken( + void *user_data, + const struct aws_byte_cursor *challenge_cursor, + int *error_code) + { + AdaptiveHttpProxyStrategy *strategy = reinterpret_cast<AdaptiveHttpProxyStrategy *>(user_data); + + String ntlmToken; + String challengeToken((const char *)challenge_cursor->ptr, challenge_cursor->len); + if (strategy->m_NtlmGetToken(challengeToken, ntlmToken)) + { + struct aws_string *token = aws_string_new_from_c_str(strategy->m_Allocator, ntlmToken.c_str()); + + if (token != NULL) + { + return token; + } + + *error_code = aws_last_error(); + } + else + { + *error_code = AWS_ERROR_HTTP_PROXY_STRATEGY_TOKEN_RETRIEVAL_FAILURE; + } + + return NULL; + } + + private: + Allocator *m_Allocator; + + KerberosGetTokenFunction m_KerberosGetToken; + KerberosGetTokenFunction m_NtlmGetCredential; + NtlmGetTokenFunction m_NtlmGetToken; + }; + + std::shared_ptr<HttpProxyStrategy> HttpProxyStrategy::CreateAdaptiveHttpProxyStrategy( + const HttpProxyStrategyAdaptiveConfig &config, + Allocator *allocator) + { + std::shared_ptr<AdaptiveHttpProxyStrategy> adaptiveStrategy = + Aws::Crt::MakeShared<AdaptiveHttpProxyStrategy>( + allocator, allocator, config.KerberosGetToken, config.NtlmGetCredential, config.NtlmGetToken); + + struct aws_http_proxy_strategy_tunneling_kerberos_options kerberosConfig; + AWS_ZERO_STRUCT(kerberosConfig); + kerberosConfig.get_token = AdaptiveHttpProxyStrategy::KerberosGetToken; + kerberosConfig.get_token_user_data = adaptiveStrategy.get(); + + struct aws_http_proxy_strategy_tunneling_ntlm_options ntlmConfig; + AWS_ZERO_STRUCT(ntlmConfig); + ntlmConfig.get_challenge_token = AdaptiveHttpProxyStrategy::NtlmGetToken; + ntlmConfig.get_token = AdaptiveHttpProxyStrategy::NtlmGetCredential; + ntlmConfig.get_challenge_token_user_data = adaptiveStrategy.get(); + + struct aws_http_proxy_strategy_tunneling_adaptive_options adaptiveConfig; + AWS_ZERO_STRUCT(adaptiveConfig); + + if (config.KerberosGetToken) + { + adaptiveConfig.kerberos_options = &kerberosConfig; + } + + if (config.NtlmGetToken) + { + adaptiveConfig.ntlm_options = &ntlmConfig; + } + + struct aws_http_proxy_strategy *strategy = + aws_http_proxy_strategy_new_tunneling_adaptive(allocator, &adaptiveConfig); + if (strategy == NULL) + { + return NULL; + } + + adaptiveStrategy->SetStrategy(strategy); + + return adaptiveStrategy; + } + } // namespace Http + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/http/HttpRequestResponse.cpp b/contrib/restricted/aws/aws-crt-cpp/source/http/HttpRequestResponse.cpp new file mode 100644 index 0000000000..a80a582ac8 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/http/HttpRequestResponse.cpp @@ -0,0 +1,151 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/crt/http/HttpRequestResponse.h> + +#include <aws/crt/io/Stream.h> +#include <aws/http/request_response.h> +#include <aws/io/stream.h> + +namespace Aws +{ + namespace Crt + { + namespace Http + { + + HttpMessage::HttpMessage(Allocator *allocator, struct aws_http_message *message) noexcept + : m_allocator(allocator), m_message(message), m_bodyStream(nullptr) + { + if (message) + { + // Acquire a refcount to keep the message alive until this object dies. + aws_http_message_acquire(this->m_message); + } + } + + HttpMessage::~HttpMessage() { m_message = aws_http_message_release(m_message); } + + std::shared_ptr<Aws::Crt::Io::InputStream> HttpMessage::GetBody() const noexcept { return m_bodyStream; } + + bool HttpMessage::SetBody(const std::shared_ptr<Aws::Crt::Io::IStream> &body) noexcept + { + aws_http_message_set_body_stream(m_message, nullptr); + m_bodyStream = nullptr; + + if (body != nullptr) + { + m_bodyStream = MakeShared<Io::StdIOStreamInputStream>(m_allocator, body, m_allocator); + if (m_bodyStream == nullptr || !m_bodyStream) + { + return false; + } + aws_http_message_set_body_stream(m_message, m_bodyStream->GetUnderlyingStream()); + } + + return true; + } + + bool HttpMessage::SetBody(const std::shared_ptr<Aws::Crt::Io::InputStream> &body) noexcept + { + m_bodyStream = body; + aws_http_message_set_body_stream( + m_message, m_bodyStream && *m_bodyStream ? m_bodyStream->GetUnderlyingStream() : nullptr); + + return true; + } + + size_t HttpMessage::GetHeaderCount() const noexcept { return aws_http_message_get_header_count(m_message); } + + Optional<HttpHeader> HttpMessage::GetHeader(size_t index) const noexcept + { + HttpHeader header; + if (aws_http_message_get_header(m_message, &header, index) != AWS_OP_SUCCESS) + { + return Optional<HttpHeader>(); + } + + return Optional<HttpHeader>(header); + } + + bool HttpMessage::AddHeader(const HttpHeader &header) noexcept + { + return aws_http_message_add_header(m_message, header) == AWS_OP_SUCCESS; + } + + bool HttpMessage::EraseHeader(size_t index) noexcept + { + return aws_http_message_erase_header(m_message, index) == AWS_OP_SUCCESS; + } + + HttpRequest::HttpRequest(Allocator *allocator) + : HttpMessage(allocator, aws_http_message_new_request(allocator)) + { + // Releas the refcount as it created, since HttpMessage is taking the ownership + aws_http_message_release(this->m_message); + } + + HttpRequest::HttpRequest(Allocator *allocator, struct aws_http_message *message) + : HttpMessage(allocator, message) + { + } + + Optional<ByteCursor> HttpRequest::GetMethod() const noexcept + { + ByteCursor method; + if (aws_http_message_get_request_method(m_message, &method) != AWS_OP_SUCCESS) + { + return Optional<ByteCursor>(); + } + + return Optional<ByteCursor>(method); + } + + bool HttpRequest::SetMethod(ByteCursor method) noexcept + { + return aws_http_message_set_request_method(m_message, method) == AWS_OP_SUCCESS; + } + + Optional<ByteCursor> HttpRequest::GetPath() const noexcept + { + ByteCursor path; + if (aws_http_message_get_request_path(m_message, &path) != AWS_OP_SUCCESS) + { + return Optional<ByteCursor>(); + } + + return Optional<ByteCursor>(path); + } + + bool HttpRequest::SetPath(ByteCursor path) noexcept + { + return aws_http_message_set_request_path(m_message, path) == AWS_OP_SUCCESS; + } + + HttpResponse::HttpResponse(Allocator *allocator) + : HttpMessage(allocator, aws_http_message_new_response(allocator)) + { + // Releas the refcount as it created, since HttpMessage is taking the ownership + aws_http_message_release(this->m_message); + } + + Optional<int> HttpResponse::GetResponseCode() const noexcept + { + int response = 0; + if (aws_http_message_get_response_status(m_message, &response) != AWS_OP_SUCCESS) + { + return Optional<int>(); + } + + return response; + } + + bool HttpResponse::SetResponseCode(int response) noexcept + { + return aws_http_message_set_response_status(m_message, response) == AWS_OP_SUCCESS; + } + } // namespace Http + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/io/Bootstrap.cpp b/contrib/restricted/aws/aws-crt-cpp/source/io/Bootstrap.cpp new file mode 100644 index 0000000000..84005a41dc --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/io/Bootstrap.cpp @@ -0,0 +1,122 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/Api.h> +#include <aws/crt/io/Bootstrap.h> + +namespace Aws +{ + namespace Crt + { + namespace Io + { + + /** + * @private + * Holds the bootstrap's shutdown promise. + * Lives until the bootstrap's shutdown-complete callback fires. + */ + class ClientBootstrapCallbackData + { + private: + Allocator *m_allocator; + + public: + ClientBootstrapCallbackData(Allocator *allocator) : m_allocator(allocator) {} + /** + * Promise for bootstrap's shutdown. + */ + std::promise<void> ShutdownPromise; + /** + * User callback of bootstrap's shutdown-complete. + */ + OnClientBootstrapShutdownComplete ShutdownCallback; + + /** + * Internal callback of bootstrap's shutdown-complete + */ + static void OnShutdownComplete(void *userData) + { + auto callbackData = static_cast<ClientBootstrapCallbackData *>(userData); + + callbackData->ShutdownPromise.set_value(); + if (callbackData->ShutdownCallback) + { + callbackData->ShutdownCallback(); + } + + Crt::Delete(callbackData, callbackData->m_allocator); + } + }; + + ClientBootstrap::ClientBootstrap( + EventLoopGroup &elGroup, + HostResolver &resolver, + Allocator *allocator) noexcept + : m_bootstrap(nullptr), m_lastError(AWS_ERROR_SUCCESS), + m_callbackData(Crt::New<ClientBootstrapCallbackData>(allocator, allocator)), + m_enableBlockingShutdown(false) + { + m_shutdownFuture = m_callbackData->ShutdownPromise.get_future(); + + aws_client_bootstrap_options options; + options.event_loop_group = elGroup.GetUnderlyingHandle(); + options.host_resolution_config = resolver.GetConfig(); + options.host_resolver = resolver.GetUnderlyingHandle(); + options.on_shutdown_complete = ClientBootstrapCallbackData::OnShutdownComplete; + options.user_data = m_callbackData.get(); + m_bootstrap = aws_client_bootstrap_new(allocator, &options); + if (!m_bootstrap) + { + m_lastError = aws_last_error(); + } + } + + ClientBootstrap::ClientBootstrap(Allocator *allocator) noexcept + : ClientBootstrap( + *Crt::ApiHandle::GetOrCreateStaticDefaultEventLoopGroup(), + *Crt::ApiHandle::GetOrCreateStaticDefaultHostResolver(), + allocator) + { + } + + ClientBootstrap::~ClientBootstrap() + { + if (m_bootstrap) + { + // Release m_callbackData, it destroys itself when shutdown completes. + m_callbackData.release(); + + aws_client_bootstrap_release(m_bootstrap); + if (m_enableBlockingShutdown) + { + // If your program is stuck here, stop using EnableBlockingShutdown() + m_shutdownFuture.wait(); + } + } + } + + ClientBootstrap::operator bool() const noexcept { return m_lastError == AWS_ERROR_SUCCESS; } + + int ClientBootstrap::LastError() const noexcept { return m_lastError; } + + void ClientBootstrap::SetShutdownCompleteCallback(OnClientBootstrapShutdownComplete callback) + { + m_callbackData->ShutdownCallback = std::move(callback); + } + + void ClientBootstrap::EnableBlockingShutdown() noexcept { m_enableBlockingShutdown = true; } + + aws_client_bootstrap *ClientBootstrap::GetUnderlyingHandle() const noexcept + { + if (*this) + { + return m_bootstrap; + } + + return nullptr; + } + } // namespace Io + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/io/ChannelHandler.cpp b/contrib/restricted/aws/aws-crt-cpp/source/io/ChannelHandler.cpp new file mode 100644 index 0000000000..fcbc443170 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/io/ChannelHandler.cpp @@ -0,0 +1,217 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/io/ChannelHandler.h> + +#include <chrono> + +namespace Aws +{ + namespace Crt + { + namespace Io + { + int ChannelHandler::s_ProcessReadMessage( + struct aws_channel_handler *handler, + struct aws_channel_slot *, + struct aws_io_message *message) + { + auto *channelHandler = reinterpret_cast<ChannelHandler *>(handler->impl); + + return channelHandler->ProcessReadMessage(message); + } + + int ChannelHandler::s_ProcessWriteMessage( + struct aws_channel_handler *handler, + struct aws_channel_slot *, + struct aws_io_message *message) + { + auto *channelHandler = reinterpret_cast<ChannelHandler *>(handler->impl); + + return channelHandler->ProcessWriteMessage(message); + } + + int ChannelHandler::s_IncrementReadWindow( + struct aws_channel_handler *handler, + struct aws_channel_slot *, + size_t size) + { + auto *channelHandler = reinterpret_cast<ChannelHandler *>(handler->impl); + + return channelHandler->IncrementReadWindow(size); + } + + int ChannelHandler::s_ProcessShutdown( + struct aws_channel_handler *handler, + struct aws_channel_slot *, + enum aws_channel_direction dir, + int errorCode, + bool freeScarceResourcesImmediately) + { + auto *channelHandler = reinterpret_cast<ChannelHandler *>(handler->impl); + + channelHandler->ProcessShutdown( + static_cast<ChannelDirection>(dir), errorCode, freeScarceResourcesImmediately); + return AWS_OP_SUCCESS; + } + + size_t ChannelHandler::s_InitialWindowSize(struct aws_channel_handler *handler) + { + auto *channelHandler = reinterpret_cast<ChannelHandler *>(handler->impl); + return channelHandler->InitialWindowSize(); + } + + size_t ChannelHandler::s_MessageOverhead(struct aws_channel_handler *handler) + { + auto *channelHandler = reinterpret_cast<ChannelHandler *>(handler->impl); + return channelHandler->MessageOverhead(); + } + + void ChannelHandler::s_ResetStatistics(struct aws_channel_handler *handler) + { + auto *channelHandler = reinterpret_cast<ChannelHandler *>(handler->impl); + channelHandler->ResetStatistics(); + } + + void ChannelHandler::s_GatherStatistics( + struct aws_channel_handler *handler, + struct aws_array_list *statsList) + { + auto *channelHandler = reinterpret_cast<ChannelHandler *>(handler->impl); + channelHandler->GatherStatistics(statsList); + } + + void ChannelHandler::s_Destroy(struct aws_channel_handler *handler) + { + auto *channelHandler = reinterpret_cast<ChannelHandler *>(handler->impl); + channelHandler->m_selfReference = nullptr; + } + + struct aws_channel_handler_vtable ChannelHandler::s_vtable = { + s_ProcessReadMessage, + s_ProcessWriteMessage, + s_IncrementReadWindow, + s_ProcessShutdown, + s_InitialWindowSize, + s_MessageOverhead, + ChannelHandler::s_Destroy, + s_ResetStatistics, + s_GatherStatistics, + }; + + ChannelHandler::ChannelHandler(Allocator *allocator) : m_allocator(allocator) + { + AWS_ZERO_STRUCT(m_handler); + m_handler.alloc = allocator; + m_handler.impl = reinterpret_cast<void *>(this); + m_handler.vtable = &ChannelHandler::s_vtable; + } + + struct aws_channel_handler *ChannelHandler::SeatForCInterop(const std::shared_ptr<ChannelHandler> &selfRef) + { + AWS_FATAL_ASSERT(this == selfRef.get()); + m_selfReference = selfRef; + return &m_handler; + } + + struct aws_io_message *ChannelHandler::AcquireMessageFromPool(MessageType messageType, size_t sizeHint) + { + return aws_channel_acquire_message_from_pool( + GetSlot()->channel, static_cast<aws_io_message_type>(messageType), sizeHint); + } + + struct aws_io_message *ChannelHandler::AcquireMaxSizeMessageForWrite() + { + return aws_channel_slot_acquire_max_message_for_write(GetSlot()); + } + + void ChannelHandler::ShutDownChannel(int errorCode) { aws_channel_shutdown(GetSlot()->channel, errorCode); } + + bool ChannelHandler::ChannelsThreadIsCallersThread() const + { + return aws_channel_thread_is_callers_thread(GetSlot()->channel); + } + + bool ChannelHandler::SendMessage(struct aws_io_message *message, ChannelDirection direction) + { + return aws_channel_slot_send_message( + GetSlot(), message, static_cast<aws_channel_direction>(direction)) == AWS_OP_SUCCESS; + } + + bool ChannelHandler::IncrementUpstreamReadWindow(size_t windowUpdateSize) + { + return aws_channel_slot_increment_read_window(GetSlot(), windowUpdateSize) == AWS_OP_SUCCESS; + } + + void ChannelHandler::OnShutdownComplete( + ChannelDirection direction, + int errorCode, + bool freeScarceResourcesImmediately) + { + aws_channel_slot_on_handler_shutdown_complete( + GetSlot(), + static_cast<aws_channel_direction>(direction), + errorCode, + freeScarceResourcesImmediately); + } + + size_t ChannelHandler::DownstreamReadWindow() const + { + if (!GetSlot()->adj_right) + { + return 0; + } + return aws_channel_slot_downstream_read_window(GetSlot()); + } + + size_t ChannelHandler::UpstreamMessageOverhead() const + { + return aws_channel_slot_upstream_message_overhead(GetSlot()); + } + + struct aws_channel_slot *ChannelHandler::GetSlot() const { return m_handler.slot; } + + struct TaskWrapper + { + struct aws_channel_task task + { + }; + Allocator *allocator{}; + std::function<void(TaskStatus)> wrappingFn; + }; + + static void s_ChannelTaskCallback(struct aws_channel_task *, void *arg, enum aws_task_status status) + { + auto *taskWrapper = reinterpret_cast<TaskWrapper *>(arg); + taskWrapper->wrappingFn(static_cast<TaskStatus>(status)); + Delete(taskWrapper, taskWrapper->allocator); + } + + void ChannelHandler::ScheduleTask(std::function<void(TaskStatus)> &&task, std::chrono::nanoseconds run_in) + { + auto *wrapper = New<TaskWrapper>(m_allocator); + wrapper->wrappingFn = std::move(task); + wrapper->allocator = m_allocator; + aws_channel_task_init( + &wrapper->task, s_ChannelTaskCallback, wrapper, "cpp-crt-custom-channel-handler-task"); + + uint64_t currentTimestamp = 0; + aws_channel_current_clock_time(GetSlot()->channel, ¤tTimestamp); + aws_channel_schedule_task_future(GetSlot()->channel, &wrapper->task, currentTimestamp + run_in.count()); + } + + void ChannelHandler::ScheduleTask(std::function<void(TaskStatus)> &&task) + { + auto *wrapper = New<TaskWrapper>(m_allocator); + wrapper->wrappingFn = std::move(task); + wrapper->allocator = m_allocator; + aws_channel_task_init( + &wrapper->task, s_ChannelTaskCallback, wrapper, "cpp-crt-custom-channel-handler-task"); + + aws_channel_schedule_task_now(GetSlot()->channel, &wrapper->task); + } + + } // namespace Io + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/io/EventLoopGroup.cpp b/contrib/restricted/aws/aws-crt-cpp/source/io/EventLoopGroup.cpp new file mode 100644 index 0000000000..000c08513b --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/io/EventLoopGroup.cpp @@ -0,0 +1,71 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/io/EventLoopGroup.h> +#include <iostream> + +namespace Aws +{ + namespace Crt + { + namespace Io + { + EventLoopGroup::EventLoopGroup(uint16_t threadCount, Allocator *allocator) noexcept + : m_eventLoopGroup(nullptr), m_lastError(AWS_ERROR_SUCCESS) + { + m_eventLoopGroup = aws_event_loop_group_new_default(allocator, threadCount, NULL); + if (m_eventLoopGroup == nullptr) + { + m_lastError = aws_last_error(); + } + } + + EventLoopGroup::EventLoopGroup(uint16_t cpuGroup, uint16_t threadCount, Allocator *allocator) noexcept + : m_eventLoopGroup(nullptr), m_lastError(AWS_ERROR_SUCCESS) + { + m_eventLoopGroup = + aws_event_loop_group_new_default_pinned_to_cpu_group(allocator, threadCount, cpuGroup, NULL); + if (m_eventLoopGroup == nullptr) + { + m_lastError = aws_last_error(); + } + } + + EventLoopGroup::~EventLoopGroup() { aws_event_loop_group_release(m_eventLoopGroup); } + + EventLoopGroup::EventLoopGroup(EventLoopGroup &&toMove) noexcept + : m_eventLoopGroup(toMove.m_eventLoopGroup), m_lastError(toMove.m_lastError) + { + toMove.m_lastError = AWS_ERROR_UNKNOWN; + toMove.m_eventLoopGroup = nullptr; + } + + EventLoopGroup &EventLoopGroup::operator=(EventLoopGroup &&toMove) noexcept + { + m_eventLoopGroup = toMove.m_eventLoopGroup; + m_lastError = toMove.m_lastError; + toMove.m_lastError = AWS_ERROR_UNKNOWN; + toMove.m_eventLoopGroup = nullptr; + + return *this; + } + + int EventLoopGroup::LastError() const { return m_lastError; } + + EventLoopGroup::operator bool() const { return m_lastError == AWS_ERROR_SUCCESS; } + + aws_event_loop_group *EventLoopGroup::GetUnderlyingHandle() noexcept + { + if (*this) + { + return m_eventLoopGroup; + } + + return nullptr; + } + + } // namespace Io + + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/io/HostResolver.cpp b/contrib/restricted/aws/aws-crt-cpp/source/io/HostResolver.cpp new file mode 100644 index 0000000000..18173fc413 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/io/HostResolver.cpp @@ -0,0 +1,121 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/io/HostResolver.h> + +#include <aws/crt/io/EventLoopGroup.h> + +#include <aws/common/string.h> +#include <aws/crt/Api.h> + +namespace Aws +{ + namespace Crt + { + namespace Io + { + HostResolver::~HostResolver() {} + + DefaultHostResolver::DefaultHostResolver( + EventLoopGroup &elGroup, + size_t maxHosts, + size_t maxTTL, + Allocator *allocator) noexcept + : m_resolver(nullptr), m_allocator(allocator), m_initialized(false) + { + AWS_ZERO_STRUCT(m_config); + + struct aws_host_resolver_default_options resolver_options; + AWS_ZERO_STRUCT(resolver_options); + resolver_options.max_entries = maxHosts; + resolver_options.el_group = elGroup.GetUnderlyingHandle(); + + m_resolver = aws_host_resolver_new_default(allocator, &resolver_options); + if (m_resolver != nullptr) + { + m_initialized = true; + } + + m_config.impl = aws_default_dns_resolve; + m_config.impl_data = nullptr; + m_config.max_ttl = maxTTL; + } + + DefaultHostResolver::DefaultHostResolver(size_t maxHosts, size_t maxTTL, Allocator *allocator) noexcept + : DefaultHostResolver( + *Crt::ApiHandle::GetOrCreateStaticDefaultEventLoopGroup(), + maxHosts, + maxTTL, + allocator) + { + } + + DefaultHostResolver::~DefaultHostResolver() + { + aws_host_resolver_release(m_resolver); + m_initialized = false; + } + + /** + * @private + */ + struct DefaultHostResolveArgs + { + Allocator *allocator; + HostResolver *resolver; + OnHostResolved onResolved; + aws_string *host; + }; + + void DefaultHostResolver::s_onHostResolved( + struct aws_host_resolver *, + const struct aws_string *hostName, + int errCode, + const struct aws_array_list *hostAddresses, + void *userData) + { + DefaultHostResolveArgs *args = static_cast<DefaultHostResolveArgs *>(userData); + + size_t len = aws_array_list_length(hostAddresses); + Vector<HostAddress> addresses; + + for (size_t i = 0; i < len; ++i) + { + HostAddress *address_ptr = NULL; + aws_array_list_get_at_ptr(hostAddresses, reinterpret_cast<void **>(&address_ptr), i); + addresses.push_back(*address_ptr); + } + + String host(aws_string_c_str(hostName), hostName->len); + args->onResolved(*args->resolver, addresses, errCode); + aws_string_destroy(args->host); + Delete(args, args->allocator); + } + + bool DefaultHostResolver::ResolveHost(const String &host, const OnHostResolved &onResolved) noexcept + { + DefaultHostResolveArgs *args = New<DefaultHostResolveArgs>(m_allocator); + if (!args) + { + return false; + } + + args->host = aws_string_new_from_array( + m_allocator, reinterpret_cast<const uint8_t *>(host.data()), host.length()); + args->onResolved = onResolved; + args->resolver = this; + args->allocator = m_allocator; + + if (!args->host || + aws_host_resolver_resolve_host(m_resolver, args->host, s_onHostResolved, &m_config, args)) + { + Delete(args, m_allocator); + return false; + } + + return true; + } + } // namespace Io + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/io/Pkcs11.cpp b/contrib/restricted/aws/aws-crt-cpp/source/io/Pkcs11.cpp new file mode 100644 index 0000000000..6e71287387 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/io/Pkcs11.cpp @@ -0,0 +1,69 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/io/Pkcs11.h> + +#include <aws/io/logging.h> +#include <aws/io/pkcs11.h> + +namespace Aws +{ + namespace Crt + { + namespace Io + { + std::shared_ptr<Pkcs11Lib> Pkcs11Lib::Create(const String &filename, Allocator *allocator) + { + return Create(filename, InitializeFinalizeBehavior::Default, allocator); + } + + std::shared_ptr<Pkcs11Lib> Pkcs11Lib::Create( + const String &filename, + InitializeFinalizeBehavior initializeFinalizeBehavior, + Allocator *allocator) + { + aws_pkcs11_lib_options options; + AWS_ZERO_STRUCT(options); + + if (!filename.empty()) + { + options.filename = ByteCursorFromString(filename); + } + + switch (initializeFinalizeBehavior) + { + case InitializeFinalizeBehavior::Default: + options.initialize_finalize_behavior = AWS_PKCS11_LIB_DEFAULT_BEHAVIOR; + break; + case InitializeFinalizeBehavior::Omit: + options.initialize_finalize_behavior = AWS_PKCS11_LIB_OMIT_INITIALIZE; + break; + case InitializeFinalizeBehavior::Strict: + options.initialize_finalize_behavior = AWS_PKCS11_LIB_STRICT_INITIALIZE_FINALIZE; + break; + default: + AWS_LOGF_ERROR( + AWS_LS_IO_PKCS11, + "Cannot create Pkcs11Lib. Invalid InitializeFinalizeBehavior %d", + (int)initializeFinalizeBehavior); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return nullptr; + } + + struct aws_pkcs11_lib *impl = aws_pkcs11_lib_new(allocator, &options); + if (impl == nullptr) + { + return nullptr; + } + + return MakeShared<Pkcs11Lib>(allocator, *impl); + } + + Pkcs11Lib::Pkcs11Lib(aws_pkcs11_lib &impl) : impl(&impl) {} + + Pkcs11Lib::~Pkcs11Lib() { aws_pkcs11_lib_release(impl); } + + } // namespace Io + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/io/SocketOptions.cpp b/contrib/restricted/aws/aws-crt-cpp/source/io/SocketOptions.cpp new file mode 100644 index 0000000000..339b81c087 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/io/SocketOptions.cpp @@ -0,0 +1,28 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/io/SocketOptions.h> + +namespace Aws +{ + namespace Crt + { + namespace Io + { + + static const uint32_t DEFAULT_SOCKET_TIME_MSEC = 3000; + + SocketOptions::SocketOptions() + { + options.type = AWS_SOCKET_STREAM; + options.domain = AWS_SOCKET_IPV4; + options.connect_timeout_ms = DEFAULT_SOCKET_TIME_MSEC; + options.keep_alive_max_failed_probes = 0; + options.keep_alive_timeout_sec = 0; + options.keep_alive_interval_sec = 0; + options.keepalive = false; + } + } // namespace Io + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/io/Stream.cpp b/contrib/restricted/aws/aws-crt-cpp/source/io/Stream.cpp new file mode 100644 index 0000000000..cf3d6d1cf6 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/io/Stream.cpp @@ -0,0 +1,211 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/crt/StlAllocator.h> +#include <aws/crt/io/Stream.h> + +#include <aws/io/stream.h> + +namespace Aws +{ + namespace Crt + { + namespace Io + { + InputStream::~InputStream() + { + // DO NOTHING: for now. But keep this here because it has to be virtual, and we may have + // resources to clean up in the future. + } + + int InputStream::s_Seek(aws_input_stream *stream, int64_t offset, enum aws_stream_seek_basis basis) + { + auto impl = static_cast<InputStream *>(stream->impl); + + // Detect whether implementation raises an error when reporting failure. + // Docs for C++ SeekImpl API say you "SHOULD" raise an error, + // but the C API does in fact require an error to be raised. + aws_reset_error(); + + if (impl->SeekImpl(offset, static_cast<StreamSeekBasis>(basis))) + { + return AWS_OP_SUCCESS; + } + + if (aws_last_error() == 0) + { + aws_raise_error(AWS_IO_STREAM_SEEK_FAILED); + } + + return AWS_OP_ERR; + } + + int InputStream::s_Read(aws_input_stream *stream, aws_byte_buf *dest) + { + auto impl = static_cast<InputStream *>(stream->impl); + + // Detect whether implementation raises an error when reporting failure. + // Docs for C++ ReadImpl API say you "SHOULD" raise an error, + // but the C API does in fact require an error to be raised. + aws_reset_error(); + + if (impl->ReadImpl(*dest)) + { + return AWS_OP_SUCCESS; + } + + if (aws_last_error() == 0) + { + aws_raise_error(AWS_IO_STREAM_READ_FAILED); + } + + return AWS_OP_ERR; + } + + int InputStream::s_GetStatus(aws_input_stream *stream, aws_stream_status *status) + { + auto impl = static_cast<InputStream *>(stream->impl); + + *status = impl->GetStatusImpl(); + return AWS_OP_SUCCESS; + } + + int InputStream::s_GetLength(struct aws_input_stream *stream, int64_t *out_length) + { + auto impl = static_cast<InputStream *>(stream->impl); + + int64_t length = impl->GetLengthImpl(); + + if (length >= 0) + { + *out_length = length; + return AWS_OP_SUCCESS; + } + + aws_raise_error(AWS_IO_STREAM_READ_FAILED); + return AWS_OP_ERR; + } + + void InputStream::s_Acquire(aws_input_stream *stream) + { + auto impl = static_cast<InputStream *>(stream->impl); + impl->AcquireRef(); + } + + void InputStream::s_Release(aws_input_stream *stream) + { + auto impl = static_cast<InputStream *>(stream->impl); + impl->ReleaseRef(); + } + + aws_input_stream_vtable InputStream::s_vtable = { + InputStream::s_Seek, + InputStream::s_Read, + InputStream::s_GetStatus, + InputStream::s_GetLength, + InputStream::s_Acquire, + InputStream::s_Release, + }; + + InputStream::InputStream(Aws::Crt::Allocator *allocator) + { + m_allocator = allocator; + AWS_ZERO_STRUCT(m_underlying_stream); + + m_underlying_stream.impl = this; + m_underlying_stream.vtable = &s_vtable; + } + + StdIOStreamInputStream::StdIOStreamInputStream( + std::shared_ptr<Aws::Crt::Io::IStream> stream, + Aws::Crt::Allocator *allocator) noexcept + : InputStream(allocator), m_stream(std::move(stream)) + { + } + + bool StdIOStreamInputStream::IsValid() const noexcept + { + auto status = GetStatusImpl(); + return status.is_valid; + } + + bool StdIOStreamInputStream::ReadImpl(ByteBuf &buffer) noexcept + { + // so this blocks, but readsome() doesn't work at all, so this is the best we've got. + // if you don't like this, don't use std::input_stream and implement your own version + // of Aws::Crt::Io::InputStream. + m_stream->read(reinterpret_cast<char *>(buffer.buffer + buffer.len), buffer.capacity - buffer.len); + auto read = m_stream->gcount(); + buffer.len += static_cast<size_t>(read); + + if (read > 0 || (read == 0 && m_stream->eof())) + { + return true; + } + + auto status = GetStatusImpl(); + + return status.is_valid && !status.is_end_of_stream; + } + + StreamStatus StdIOStreamInputStream::GetStatusImpl() const noexcept + { + StreamStatus status; + status.is_end_of_stream = m_stream->eof(); + status.is_valid = static_cast<bool>(*m_stream); + + return status; + } + + int64_t StdIOStreamInputStream::GetLengthImpl() const noexcept + { + auto currentPosition = m_stream->tellg(); + + m_stream->seekg(0, std::ios_base::end); + int64_t retVal = -1; + + if (*m_stream) + { + retVal = static_cast<int64_t>(m_stream->tellg()); + } + + m_stream->seekg(currentPosition); + + return retVal; + } + + bool StdIOStreamInputStream::SeekImpl(int64_t offset, StreamSeekBasis seekBasis) noexcept + { + // very important, otherwise the stream can't be reused after reading the entire stream the first time. + m_stream->clear(); + + auto seekDir = std::ios_base::beg; + switch (seekBasis) + { + case StreamSeekBasis::Begin: + seekDir = std::ios_base::beg; + break; + case StreamSeekBasis::End: + seekDir = std::ios_base::end; + break; + default: + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return false; + } + + using stdOffType = Aws::Crt::Io::IStream::off_type; + if (offset < std::numeric_limits<stdOffType>::min() || offset > std::numeric_limits<stdOffType>::max()) + { + aws_raise_error(AWS_IO_STREAM_INVALID_SEEK_POSITION); + return false; + } + + m_stream->seekg(static_cast<stdOffType>(offset), seekDir); + + return true; + } + } // namespace Io + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/io/TlsOptions.cpp b/contrib/restricted/aws/aws-crt-cpp/source/io/TlsOptions.cpp new file mode 100644 index 0000000000..6077912c9a --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/io/TlsOptions.cpp @@ -0,0 +1,520 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/io/TlsOptions.h> + +#include <aws/crt/io/Pkcs11.h> + +#include <aws/crt/Api.h> +#include <aws/io/logging.h> +#include <aws/io/tls_channel_handler.h> + +namespace Aws +{ + namespace Crt + { + namespace Io + { + TlsContextOptions::~TlsContextOptions() + { + if (m_isInit) + { + aws_tls_ctx_options_clean_up(&m_options); + } + } + + TlsContextOptions::TlsContextOptions() noexcept : m_isInit(false) { AWS_ZERO_STRUCT(m_options); } + + TlsContextOptions::TlsContextOptions(TlsContextOptions &&other) noexcept + { + m_options = other.m_options; + m_isInit = other.m_isInit; + AWS_ZERO_STRUCT(other.m_options); + other.m_isInit = false; + } + + TlsContextOptions &TlsContextOptions::operator=(TlsContextOptions &&other) noexcept + { + if (&other != this) + { + if (m_isInit) + { + aws_tls_ctx_options_clean_up(&m_options); + } + + m_options = other.m_options; + m_isInit = other.m_isInit; + AWS_ZERO_STRUCT(other.m_options); + other.m_isInit = false; + } + + return *this; + } + + TlsContextOptions TlsContextOptions::InitDefaultClient(Allocator *allocator) noexcept + { + TlsContextOptions ctxOptions; + aws_tls_ctx_options_init_default_client(&ctxOptions.m_options, allocator); + ctxOptions.m_isInit = true; + return ctxOptions; + } + + TlsContextOptions TlsContextOptions::InitClientWithMtls( + const char *certPath, + const char *pKeyPath, + Allocator *allocator) noexcept + { + TlsContextOptions ctxOptions; + if (!aws_tls_ctx_options_init_client_mtls_from_path( + &ctxOptions.m_options, allocator, certPath, pKeyPath)) + { + ctxOptions.m_isInit = true; + } + return ctxOptions; + } + + TlsContextOptions TlsContextOptions::InitClientWithMtls( + const ByteCursor &cert, + const ByteCursor &pkey, + Allocator *allocator) noexcept + { + TlsContextOptions ctxOptions; + if (!aws_tls_ctx_options_init_client_mtls( + &ctxOptions.m_options, + allocator, + const_cast<ByteCursor *>(&cert), + const_cast<ByteCursor *>(&pkey))) + { + ctxOptions.m_isInit = true; + } + return ctxOptions; + } + + TlsContextOptions TlsContextOptions::InitClientWithMtlsPkcs11( + const TlsContextPkcs11Options &pkcs11Options, + Allocator *allocator) noexcept + { + TlsContextOptions ctxOptions; + aws_tls_ctx_pkcs11_options nativePkcs11Options = pkcs11Options.GetUnderlyingHandle(); + if (!aws_tls_ctx_options_init_client_mtls_with_pkcs11( + &ctxOptions.m_options, allocator, &nativePkcs11Options)) + { + ctxOptions.m_isInit = true; + } + return ctxOptions; + } + + TlsContextOptions TlsContextOptions::InitClientWithMtlsPkcs12( + const char *pkcs12Path, + const char *pkcs12Pwd, + Allocator *allocator) noexcept + { + TlsContextOptions ctxOptions; + struct aws_byte_cursor password = aws_byte_cursor_from_c_str(pkcs12Pwd); + if (!aws_tls_ctx_options_init_client_mtls_pkcs12_from_path( + &ctxOptions.m_options, allocator, pkcs12Path, &password)) + { + ctxOptions.m_isInit = true; + } + return ctxOptions; + } + + bool TlsContextOptions::SetKeychainPath(ByteCursor &keychain_path) noexcept + { + AWS_ASSERT(m_isInit); + return aws_tls_ctx_options_set_keychain_path(&m_options, &keychain_path) == 0; + } + + TlsContextOptions TlsContextOptions::InitClientWithMtlsSystemPath( + const char *windowsCertStorePath, + Allocator *allocator) noexcept + { + TlsContextOptions ctxOptions; + if (!aws_tls_ctx_options_init_client_mtls_from_system_path( + &ctxOptions.m_options, allocator, windowsCertStorePath)) + { + ctxOptions.m_isInit = true; + } + return ctxOptions; + } + + int TlsContextOptions::LastError() const noexcept { return LastErrorOrUnknown(); } + + bool TlsContextOptions::IsAlpnSupported() noexcept { return aws_tls_is_alpn_available(); } + + bool TlsContextOptions::SetAlpnList(const char *alpn_list) noexcept + { + AWS_ASSERT(m_isInit); + return aws_tls_ctx_options_set_alpn_list(&m_options, alpn_list) == 0; + } + + void TlsContextOptions::SetVerifyPeer(bool verify_peer) noexcept + { + AWS_ASSERT(m_isInit); + aws_tls_ctx_options_set_verify_peer(&m_options, verify_peer); + } + + void TlsContextOptions::SetMinimumTlsVersion(aws_tls_versions minimumTlsVersion) + { + AWS_ASSERT(m_isInit); + aws_tls_ctx_options_set_minimum_tls_version(&m_options, minimumTlsVersion); + } + + void TlsContextOptions::SetTlsCipherPreference(aws_tls_cipher_pref cipher_pref) + { + AWS_ASSERT(m_isInit); + aws_tls_ctx_options_set_tls_cipher_preference(&m_options, cipher_pref); + } + + bool TlsContextOptions::OverrideDefaultTrustStore(const char *caPath, const char *caFile) noexcept + { + AWS_ASSERT(m_isInit); + return aws_tls_ctx_options_override_default_trust_store_from_path(&m_options, caPath, caFile) == 0; + } + + bool TlsContextOptions::OverrideDefaultTrustStore(const ByteCursor &ca) noexcept + { + AWS_ASSERT(m_isInit); + return aws_tls_ctx_options_override_default_trust_store(&m_options, const_cast<ByteCursor *>(&ca)) == 0; + } + + TlsContextPkcs11Options::TlsContextPkcs11Options( + const std::shared_ptr<Pkcs11Lib> &pkcs11Lib, + Allocator *) noexcept + : m_pkcs11Lib{pkcs11Lib} + { + } + + void TlsContextPkcs11Options::SetUserPin(const String &pin) noexcept { m_userPin = pin; } + + void TlsContextPkcs11Options::SetSlotId(const uint64_t id) noexcept { m_slotId = id; } + + void TlsContextPkcs11Options::SetTokenLabel(const String &label) noexcept { m_tokenLabel = label; } + + void TlsContextPkcs11Options::SetPrivateKeyObjectLabel(const String &label) noexcept + { + m_privateKeyObjectLabel = label; + } + + void TlsContextPkcs11Options::SetCertificateFilePath(const String &path) noexcept + { + m_certificateFilePath = path; + } + + void TlsContextPkcs11Options::SetCertificateFileContents(const String &contents) noexcept + { + m_certificateFileContents = contents; + } + + aws_tls_ctx_pkcs11_options TlsContextPkcs11Options::GetUnderlyingHandle() const noexcept + { + aws_tls_ctx_pkcs11_options options; + AWS_ZERO_STRUCT(options); + + if (m_pkcs11Lib) + { + options.pkcs11_lib = m_pkcs11Lib->GetNativeHandle(); + } + + if (m_slotId) + { + options.slot_id = &(*m_slotId); + } + + if (m_userPin) + { + options.user_pin = ByteCursorFromString(*m_userPin); + } + + if (m_tokenLabel) + { + options.token_label = ByteCursorFromString(*m_tokenLabel); + } + + if (m_privateKeyObjectLabel) + { + options.private_key_object_label = ByteCursorFromString(*m_privateKeyObjectLabel); + } + + if (m_certificateFilePath) + { + options.cert_file_path = ByteCursorFromString(*m_certificateFilePath); + } + + if (m_certificateFileContents) + { + options.cert_file_contents = ByteCursorFromString(*m_certificateFileContents); + } + + return options; + } + + TlsConnectionOptions::TlsConnectionOptions() noexcept : m_lastError(AWS_ERROR_SUCCESS), m_isInit(false) {} + + TlsConnectionOptions::TlsConnectionOptions(aws_tls_ctx *ctx, Allocator *allocator) noexcept + : m_allocator(allocator), m_lastError(AWS_ERROR_SUCCESS), m_isInit(true) + { + aws_tls_connection_options_init_from_ctx(&m_tls_connection_options, ctx); + } + + TlsConnectionOptions::~TlsConnectionOptions() + { + if (m_isInit) + { + aws_tls_connection_options_clean_up(&m_tls_connection_options); + m_isInit = false; + } + } + + TlsConnectionOptions::TlsConnectionOptions(const TlsConnectionOptions &options) noexcept + { + m_isInit = false; + AWS_ZERO_STRUCT(m_tls_connection_options); + + if (options.m_isInit) + { + m_allocator = options.m_allocator; + + if (!aws_tls_connection_options_copy(&m_tls_connection_options, &options.m_tls_connection_options)) + { + m_isInit = true; + } + else + { + m_lastError = LastErrorOrUnknown(); + } + } + } + + TlsConnectionOptions &TlsConnectionOptions::operator=(const TlsConnectionOptions &options) noexcept + { + if (this != &options) + { + if (m_isInit) + { + aws_tls_connection_options_clean_up(&m_tls_connection_options); + } + + m_isInit = false; + AWS_ZERO_STRUCT(m_tls_connection_options); + + if (options.m_isInit) + { + m_allocator = options.m_allocator; + if (!aws_tls_connection_options_copy( + &m_tls_connection_options, &options.m_tls_connection_options)) + { + m_isInit = true; + } + else + { + m_lastError = LastErrorOrUnknown(); + } + } + } + + return *this; + } + + TlsConnectionOptions::TlsConnectionOptions(TlsConnectionOptions &&options) noexcept + : m_isInit(options.m_isInit) + { + if (options.m_isInit) + { + m_tls_connection_options = options.m_tls_connection_options; + m_allocator = options.m_allocator; + AWS_ZERO_STRUCT(options.m_tls_connection_options); + options.m_isInit = false; + } + } + + TlsConnectionOptions &TlsConnectionOptions::operator=(TlsConnectionOptions &&options) noexcept + { + if (this != &options) + { + if (m_isInit) + { + aws_tls_connection_options_clean_up(&m_tls_connection_options); + } + + m_isInit = false; + + if (options.m_isInit) + { + m_tls_connection_options = options.m_tls_connection_options; + AWS_ZERO_STRUCT(options.m_tls_connection_options); + options.m_isInit = false; + m_isInit = true; + m_allocator = options.m_allocator; + } + } + + return *this; + } + + bool TlsConnectionOptions::SetServerName(ByteCursor &serverName) noexcept + { + if (!isValid()) + { + m_lastError = LastErrorOrUnknown(); + return false; + } + + if (aws_tls_connection_options_set_server_name(&m_tls_connection_options, m_allocator, &serverName)) + { + m_lastError = LastErrorOrUnknown(); + return false; + } + + return true; + } + + bool TlsConnectionOptions::SetAlpnList(const char *alpnList) noexcept + { + if (!isValid()) + { + m_lastError = LastErrorOrUnknown(); + return false; + } + + if (aws_tls_connection_options_set_alpn_list(&m_tls_connection_options, m_allocator, alpnList)) + { + m_lastError = LastErrorOrUnknown(); + return false; + } + + return true; + } + + TlsContext::TlsContext() noexcept : m_ctx(nullptr), m_initializationError(AWS_ERROR_SUCCESS) {} + + TlsContext::TlsContext(TlsContextOptions &options, TlsMode mode, Allocator *allocator) noexcept + : m_ctx(nullptr), m_initializationError(AWS_ERROR_SUCCESS) + { +#if BYO_CRYPTO + if (!ApiHandle::GetBYOCryptoNewTlsContextImplCallback() || + !ApiHandle::GetBYOCryptoDeleteTlsContextImplCallback()) + { + AWS_LOGF_ERROR( + AWS_LS_IO_TLS, + "Must call ApiHandle::SetBYOCryptoTlsContextCallbacks() before TlsContext can be created"); + m_initializationError = AWS_IO_TLS_CTX_ERROR; + return; + } + + void *impl = ApiHandle::GetBYOCryptoNewTlsContextImplCallback()(options, mode, allocator); + if (!impl) + { + AWS_LOGF_ERROR( + AWS_LS_IO_TLS, "Creation callback from ApiHandle::SetBYOCryptoTlsContextCallbacks() failed"); + m_initializationError = AWS_IO_TLS_CTX_ERROR; + return; + } + + auto underlying_tls_ctx = static_cast<aws_tls_ctx *>(aws_mem_calloc(allocator, 1, sizeof(aws_tls_ctx))); + underlying_tls_ctx->alloc = allocator; + underlying_tls_ctx->impl = impl; + + aws_ref_count_init(&underlying_tls_ctx->ref_count, underlying_tls_ctx, [](void *userdata) { + auto dying_ctx = static_cast<aws_tls_ctx *>(userdata); + ApiHandle::GetBYOCryptoDeleteTlsContextImplCallback()(dying_ctx->impl); + aws_mem_release(dying_ctx->alloc, dying_ctx); + }); + + m_ctx.reset(underlying_tls_ctx, aws_tls_ctx_release); +#else + if (mode == TlsMode::CLIENT) + { + aws_tls_ctx *underlying_tls_ctx = aws_tls_client_ctx_new(allocator, &options.m_options); + if (underlying_tls_ctx != nullptr) + { + m_ctx.reset(underlying_tls_ctx, aws_tls_ctx_release); + } + } + else + { + aws_tls_ctx *underlying_tls_ctx = aws_tls_server_ctx_new(allocator, &options.m_options); + if (underlying_tls_ctx != nullptr) + { + m_ctx.reset(underlying_tls_ctx, aws_tls_ctx_release); + } + } + if (!m_ctx) + { + m_initializationError = Aws::Crt::LastErrorOrUnknown(); + } +#endif // BYO_CRYPTO + } + + TlsConnectionOptions TlsContext::NewConnectionOptions() const noexcept + { + if (!isValid()) + { + AWS_LOGF_ERROR( + AWS_LS_IO_TLS, "Trying to call TlsContext::NewConnectionOptions from an invalid TlsContext."); + return TlsConnectionOptions(); + } + + return TlsConnectionOptions(m_ctx.get(), m_ctx->alloc); + } + + TlsChannelHandler::TlsChannelHandler( + struct aws_channel_slot *, + const struct aws_tls_connection_options &options, + Allocator *allocator) + : ChannelHandler(allocator) + { + m_OnNegotiationResult = options.on_negotiation_result; + m_userData = options.user_data; + aws_byte_buf_init(&m_protocolByteBuf, allocator, 16); + } + + TlsChannelHandler::~TlsChannelHandler() { aws_byte_buf_clean_up(&m_protocolByteBuf); } + + void TlsChannelHandler::CompleteTlsNegotiation(int errorCode) + { + m_OnNegotiationResult(&this->m_handler, GetSlot(), errorCode, m_userData); + } + + ClientTlsChannelHandler::ClientTlsChannelHandler( + struct aws_channel_slot *slot, + const struct aws_tls_connection_options &options, + Allocator *allocator) + : TlsChannelHandler(slot, options, allocator) + { + } + + } // namespace Io + } // namespace Crt +} // namespace Aws + +#if BYO_CRYPTO +AWS_EXTERN_C_BEGIN + +bool aws_tls_is_alpn_available(void) +{ + const auto &callback = Aws::Crt::ApiHandle::GetBYOCryptoIsTlsAlpnSupportedCallback(); + if (!callback) + { + AWS_LOGF_ERROR( + AWS_LS_IO_TLS, "Must call ApiHandle::SetBYOCryptoTlsContextCallbacks() before ALPN can be queried"); + return false; + } + return callback(); +} + +struct aws_byte_buf aws_tls_handler_protocol(struct aws_channel_handler *handler) +{ + auto *channelHandler = reinterpret_cast<Aws::Crt::Io::ChannelHandler *>(handler->impl); + auto *tlsHandler = static_cast<Aws::Crt::Io::TlsChannelHandler *>(channelHandler); + Aws::Crt::String protocolString = const_cast<const Aws::Crt::Io::TlsChannelHandler *>(tlsHandler)->GetProtocol(); + + tlsHandler->m_protocolByteBuf.len = 0; + aws_byte_cursor protocolCursor = Aws::Crt::ByteCursorFromString(protocolString); + aws_byte_buf_append_dynamic(&tlsHandler->m_protocolByteBuf, &protocolCursor); + return tlsHandler->m_protocolByteBuf; +} + +AWS_EXTERN_C_END +#endif /* BYO_CRYPTO */ diff --git a/contrib/restricted/aws/aws-crt-cpp/source/io/Uri.cpp b/contrib/restricted/aws/aws-crt-cpp/source/io/Uri.cpp new file mode 100644 index 0000000000..dd43f3249e --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/io/Uri.cpp @@ -0,0 +1,145 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/io/Uri.h> + +namespace Aws +{ + namespace Crt + { + namespace Io + { + Uri::Uri() noexcept : m_lastError(AWS_ERROR_SUCCESS), m_isInit(false) { AWS_ZERO_STRUCT(m_uri); } + + Uri::~Uri() + { + if (m_isInit) + { + aws_uri_clean_up(&m_uri); + m_isInit = false; + } + } + + Uri::Uri(const ByteCursor &cursor, Allocator *allocator) noexcept + : m_lastError(AWS_ERROR_SUCCESS), m_isInit(false) + { + if (!aws_uri_init_parse(&m_uri, allocator, &cursor)) + { + m_isInit = true; + } + else + { + m_lastError = aws_last_error(); + } + } + + Uri::Uri(aws_uri_builder_options &builderOptions, Allocator *allocator) noexcept + : m_lastError(AWS_ERROR_SUCCESS), m_isInit(false) + { + if (!aws_uri_init_from_builder_options(&m_uri, allocator, &builderOptions)) + { + m_isInit = true; + } + else + { + m_lastError = aws_last_error(); + } + } + + Uri::Uri(const Uri &other) : m_lastError(AWS_ERROR_SUCCESS), m_isInit(false) + { + if (other.m_isInit) + { + ByteCursor uriCursor = other.GetFullUri(); + + if (!aws_uri_init_parse(&m_uri, other.m_uri.allocator, &uriCursor)) + { + m_isInit = true; + } + else + { + m_lastError = aws_last_error(); + } + } + } + + Uri &Uri::operator=(const Uri &other) + { + if (this != &other) + { + m_isInit = false; + m_lastError = AWS_ERROR_SUCCESS; + + if (other.m_isInit) + { + ByteCursor uriCursor = other.GetFullUri(); + + if (!aws_uri_init_parse(&m_uri, other.m_uri.allocator, &uriCursor)) + { + m_isInit = true; + } + else + { + m_lastError = aws_last_error(); + } + } + } + + return *this; + } + + Uri::Uri(Uri &&uri) noexcept : m_lastError(AWS_ERROR_SUCCESS), m_isInit(uri.m_isInit) + { + if (uri.m_isInit) + { + m_uri = uri.m_uri; + AWS_ZERO_STRUCT(uri.m_uri); + uri.m_isInit = false; + } + } + + Uri &Uri::operator=(Uri &&uri) noexcept + { + if (this != &uri) + { + if (m_isInit) + { + aws_uri_clean_up(&m_uri); + } + + if (uri.m_isInit) + { + m_uri = uri.m_uri; + AWS_ZERO_STRUCT(uri.m_uri); + uri.m_isInit = false; + m_isInit = true; + m_lastError = AWS_ERROR_SUCCESS; + } + else + { + m_lastError = uri.m_lastError; + } + } + + return *this; + } + + ByteCursor Uri::GetScheme() const noexcept { return m_uri.scheme; } + + ByteCursor Uri::GetAuthority() const noexcept { return m_uri.authority; } + + ByteCursor Uri::GetPath() const noexcept { return m_uri.path; } + + ByteCursor Uri::GetQueryString() const noexcept { return m_uri.query_string; } + + ByteCursor Uri::GetHostName() const noexcept { return m_uri.host_name; } + + uint16_t Uri::GetPort() const noexcept { return m_uri.port; } + + ByteCursor Uri::GetPathAndQuery() const noexcept { return m_uri.path_and_query; } + + ByteCursor Uri::GetFullUri() const noexcept { return ByteCursorFromByteBuf(m_uri.uri_str); } + } // namespace Io + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/iot/Mqtt5Client.cpp b/contrib/restricted/aws/aws-crt-cpp/source/iot/Mqtt5Client.cpp new file mode 100644 index 0000000000..1c69203a40 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/iot/Mqtt5Client.cpp @@ -0,0 +1,641 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include <aws/crt/Api.h> +#include <aws/crt/auth/Credentials.h> +#include <aws/crt/auth/Sigv4Signing.h> +#include <aws/crt/http/HttpRequestResponse.h> +#include <aws/crt/mqtt/Mqtt5Packets.h> + +#include <aws/iot/Mqtt5Client.h> + +#if !BYO_CRYPTO + +namespace Aws +{ + namespace Iot + { + static Crt::String AddToUsernameParameter( + Crt::String currentUsername, + Crt::String parameterValue, + Crt::String parameterPreText) + { + Crt::String return_string = currentUsername; + if (return_string.find("?") != Crt::String::npos) + { + return_string += "&"; + } + else + { + return_string += "?"; + } + + if (parameterValue.find(parameterPreText) != Crt::String::npos) + { + return return_string + parameterValue; + } + else + { + return return_string + parameterPreText + parameterValue; + } + } + + static bool buildMqtt5FinalUsername( + Crt::Optional<Mqtt5CustomAuthConfig> customAuthConfig, + Crt::String &username) + { + if (customAuthConfig.has_value()) + { + /* If we're using token-signing authentication, then all token properties must be set */ + bool usingSigning = false; + if (customAuthConfig->GetTokenValue().has_value() || customAuthConfig->GetTokenKeyName().has_value() || + customAuthConfig->GetTokenSignature().has_value()) + { + usingSigning = true; + if (!customAuthConfig->GetTokenValue().has_value() || + !customAuthConfig->GetTokenKeyName().has_value() || + !customAuthConfig->GetTokenSignature().has_value()) + { + return false; + } + } + Crt::String usernameString = ""; + + if (!customAuthConfig->GetUsername().has_value()) + { + if (!username.empty()) + { + usernameString += username; + } + } + else + { + usernameString += customAuthConfig->GetUsername().value(); + } + + if (customAuthConfig->GetAuthorizerName().has_value()) + { + usernameString = AddToUsernameParameter( + usernameString, customAuthConfig->GetAuthorizerName().value(), "x-amz-customauthorizer-name="); + } + if (usingSigning) + { + usernameString = AddToUsernameParameter( + usernameString, + customAuthConfig->GetTokenValue().value(), + customAuthConfig->GetTokenKeyName().value() + "="); + usernameString = AddToUsernameParameter( + usernameString, + customAuthConfig->GetTokenSignature().value(), + "x-amz-customauthorizer-signature="); + } + + username = usernameString; + } + return true; + } + + /***************************************************** + * + * Mqtt5ClientOptionsBuilder + * + *****************************************************/ + + Mqtt5ClientBuilder::Mqtt5ClientBuilder(Crt::Allocator *allocator) noexcept + : m_allocator(allocator), m_port(0), m_lastError(0), m_enableMetricsCollection(true) + { + m_options = new Crt::Mqtt5::Mqtt5ClientOptions(allocator); + } + + Mqtt5ClientBuilder::Mqtt5ClientBuilder(int error, Crt::Allocator *allocator) noexcept + : m_allocator(allocator), m_options(nullptr), m_lastError(error) + { + } + + Mqtt5ClientBuilder *Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithMtlsFromPath( + const Crt::String hostName, + const char *certPath, + const char *pkeyPath, + Crt::Allocator *allocator) noexcept + { + Mqtt5ClientBuilder *result = new Mqtt5ClientBuilder(allocator); + result->m_tlsConnectionOptions = + Crt::Io::TlsContextOptions::InitClientWithMtls(certPath, pkeyPath, allocator); + if (!result->m_tlsConnectionOptions.value()) + { + result->m_lastError = result->m_tlsConnectionOptions->LastError(); + return result; + } + result->withHostName(hostName); + return result; + } + + Mqtt5ClientBuilder *Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithMtlsFromMemory( + const Crt::String hostName, + const Crt::ByteCursor &cert, + const Crt::ByteCursor &pkey, + Crt::Allocator *allocator) noexcept + { + Mqtt5ClientBuilder *result = new Mqtt5ClientBuilder(allocator); + result->m_tlsConnectionOptions = Crt::Io::TlsContextOptions::InitClientWithMtls(cert, pkey, allocator); + if (!result->m_tlsConnectionOptions.value()) + { + result->m_lastError = result->m_tlsConnectionOptions->LastError(); + return result; + } + result->withHostName(hostName); + return result; + } + + Mqtt5ClientBuilder *Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithMtlsPkcs11( + const Crt::String hostName, + const Crt::Io::TlsContextPkcs11Options &pkcs11Options, + Crt::Allocator *allocator) noexcept + { + Mqtt5ClientBuilder *result = new Mqtt5ClientBuilder(allocator); + result->m_tlsConnectionOptions = + Crt::Io::TlsContextOptions::InitClientWithMtlsPkcs11(pkcs11Options, allocator); + if (!result->m_tlsConnectionOptions.value()) + { + result->m_lastError = result->m_tlsConnectionOptions->LastError(); + return result; + } + result->withHostName(hostName); + return result; + } + + Mqtt5ClientBuilder *Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithWindowsCertStorePath( + const Crt::String hostName, + const char *windowsCertStorePath, + Crt::Allocator *allocator) noexcept + { + Mqtt5ClientBuilder *result = new Mqtt5ClientBuilder(allocator); + result->m_tlsConnectionOptions = + Crt::Io::TlsContextOptions::InitClientWithMtlsSystemPath(windowsCertStorePath, allocator); + if (!result->m_tlsConnectionOptions.value()) + { + result->m_lastError = result->m_tlsConnectionOptions->LastError(); + return result; + } + result->withHostName(hostName); + return result; + } + + Mqtt5ClientBuilder *Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithWebsocket( + const Crt::String hostName, + const WebsocketConfig &config, + Crt::Allocator *allocator) noexcept + { + Mqtt5ClientBuilder *result = new Mqtt5ClientBuilder(allocator); + result->m_tlsConnectionOptions = Crt::Io::TlsContextOptions::InitDefaultClient(); + result->withHostName(hostName); + result->m_websocketConfig = config; + return result; + } + + Mqtt5ClientBuilder *Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithCustomAuthorizer( + const Crt::String hostName, + const Mqtt5CustomAuthConfig &customAuthConfig, + Crt::Allocator *allocator) noexcept + { + Mqtt5ClientBuilder *result = new Mqtt5ClientBuilder(allocator); + result->m_tlsConnectionOptions = Crt::Io::TlsContextOptions::InitDefaultClient(); + result->withHostName(hostName); + result->WithCustomAuthorizer(customAuthConfig); + return result; + } + + Mqtt5ClientBuilder *Mqtt5ClientBuilder::NewMqtt5ClientBuilderWithCustomAuthorizerWebsocket( + const Crt::String hostName, + const Mqtt5CustomAuthConfig &customAuthConfig, + const WebsocketConfig &config, + Crt::Allocator *allocator) noexcept + { + Mqtt5ClientBuilder *result = new Mqtt5ClientBuilder(allocator); + result->m_tlsConnectionOptions = Crt::Io::TlsContextOptions::InitDefaultClient(); + result->withHostName(hostName); + result->m_websocketConfig = config; + result->WithCustomAuthorizer(customAuthConfig); + return result; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withHostName(const Crt::String hostName) + { + m_options->withHostName(hostName); + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withPort(uint16_t port) noexcept + { + m_port = port; + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::WithCertificateAuthority(const char *caPath) noexcept + { + if (m_tlsConnectionOptions) + { + if (!m_tlsConnectionOptions->OverrideDefaultTrustStore(nullptr, caPath)) + { + m_lastError = m_tlsConnectionOptions->LastError(); + } + } + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::WithCertificateAuthority(const Crt::ByteCursor &cert) noexcept + { + if (m_tlsConnectionOptions) + { + if (!m_tlsConnectionOptions->OverrideDefaultTrustStore(cert)) + { + m_lastError = m_tlsConnectionOptions->LastError(); + } + } + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withHttpProxyOptions( + const Crt::Http::HttpClientConnectionProxyOptions &proxyOptions) noexcept + { + m_proxyOptions = proxyOptions; + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::WithCustomAuthorizer(const Iot::Mqtt5CustomAuthConfig &config) noexcept + { + m_customAuthConfig = config; + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withConnectOptions( + std::shared_ptr<ConnectPacket> packetConnect) noexcept + { + m_connectOptions = packetConnect; + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withSessionBehavior(ClientSessionBehaviorType sessionBehavior) noexcept + { + m_options->withSessionBehavior(sessionBehavior); + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withClientExtendedValidationAndFlowControl( + ClientExtendedValidationAndFlowControl clientExtendedValidationAndFlowControl) noexcept + { + m_options->withClientExtendedValidationAndFlowControl(clientExtendedValidationAndFlowControl); + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withOfflineQueueBehavior( + ClientOperationQueueBehaviorType operationQueueBehavior) noexcept + { + m_options->withAckTimeoutSeconds(operationQueueBehavior); + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withReconnectOptions(ReconnectOptions reconnectOptions) noexcept + { + m_options->withReconnectOptions(reconnectOptions); + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withPingTimeoutMs(uint32_t pingTimeoutMs) noexcept + { + m_options->withPingTimeoutMs(pingTimeoutMs); + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withConnackTimeoutMs(uint32_t connackTimeoutMs) noexcept + { + m_options->withConnackTimeoutMs(connackTimeoutMs); + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withAckTimeoutSeconds(uint32_t ackTimeoutSeconds) noexcept + { + m_options->withAckTimeoutSeconds(ackTimeoutSeconds); + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::WithSdkName(const Crt::String &sdkName) + { + m_sdkName = sdkName; + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::WithSdkVersion(const Crt::String &sdkVersion) + { + m_sdkVersion = sdkVersion; + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withClientConnectionSuccessCallback( + OnConnectionSuccessHandler callback) noexcept + { + m_options->withClientConnectionSuccessCallback(std::move(callback)); + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withClientConnectionFailureCallback( + OnConnectionFailureHandler callback) noexcept + { + m_options->withClientConnectionFailureCallback(std::move(callback)); + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withClientDisconnectionCallback( + OnDisconnectionHandler callback) noexcept + { + m_options->withClientDisconnectionCallback(std::move(callback)); + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withClientStoppedCallback(OnStoppedHandler callback) noexcept + { + m_options->withClientStoppedCallback(std::move(callback)); + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withClientAttemptingConnectCallback( + OnAttemptingConnectHandler callback) noexcept + { + m_options->withClientAttemptingConnectCallback(std::move(callback)); + return *this; + } + + Mqtt5ClientBuilder &Mqtt5ClientBuilder::withPublishReceivedCallback(OnPublishReceivedHandler callback) noexcept + { + m_options->withPublishReceivedCallback(std::move(callback)); + return *this; + } + + std::shared_ptr<Mqtt5Client> Mqtt5ClientBuilder::Build() noexcept + { + if (m_lastError != 0) + { + return nullptr; + } + + uint16_t port = m_port; + + if (!port) // port is default to 0 + { + if (m_websocketConfig || Crt::Io::TlsContextOptions::IsAlpnSupported()) + { + port = 443; + } + else + { + port = 8883; + } + } + + if (port == 443 && !m_websocketConfig && Crt::Io::TlsContextOptions::IsAlpnSupported() && + !m_customAuthConfig.has_value()) + { + if (!m_tlsConnectionOptions->SetAlpnList("x-amzn-mqtt-ca")) + { + return nullptr; + } + } + + if (m_customAuthConfig.has_value()) + { + if (port != 443) + { + AWS_LOGF_WARN( + AWS_LS_MQTT_GENERAL, + "Attempting to connect to authorizer with unsupported port. Port is not 443..."); + } + if (!m_websocketConfig) + { + if (!m_tlsConnectionOptions->SetAlpnList("mqtt")) + { + return nullptr; + } + } + } + + // add metrics string to username (if metrics enabled) + if (m_enableMetricsCollection || m_customAuthConfig.has_value()) + { + Crt::String username = ""; + if (m_connectOptions != nullptr) + { + if (m_connectOptions->getUsername().has_value()) + username = m_connectOptions->getUsername().value(); + } + else + { + m_connectOptions = std::make_shared<ConnectPacket>(m_allocator); + } + + if (m_customAuthConfig.has_value()) + { + if (!buildMqtt5FinalUsername(m_customAuthConfig, username)) + { + AWS_LOGF_ERROR( + AWS_LS_MQTT5_CLIENT, + "Failed to setup CustomAuthorizerConfig, please check if the parameters are set " + "correctly."); + return nullptr; + } + if (m_customAuthConfig->GetPassword().has_value()) + { + m_connectOptions->withPassword(m_customAuthConfig->GetPassword().value()); + } + } + + if (m_enableMetricsCollection) + { + username = AddToUsernameParameter(username, "SDK", m_sdkName); + username = AddToUsernameParameter(username, "Version", m_sdkName); + } + m_connectOptions->withUserName(username); + } + + auto tlsContext = + Crt::Io::TlsContext(m_tlsConnectionOptions.value(), Crt::Io::TlsMode::CLIENT, m_allocator); + if (!tlsContext) + { + return nullptr; + } + + m_options->withPort(port).withTlsConnectionOptions(tlsContext.NewConnectionOptions()); + + if (m_connectOptions != nullptr) + { + m_options->withConnectOptions(m_connectOptions); + } + + if (m_websocketConfig.has_value()) + { + auto websocketConfig = m_websocketConfig.value(); + auto signerTransform = [websocketConfig]( + std::shared_ptr<Crt::Http::HttpRequest> req, + const Crt::Mqtt::OnWebSocketHandshakeInterceptComplete &onComplete) { + // it is only a very happy coincidence that these function signatures match. This is the callback + // for signing to be complete. It invokes the callback for websocket handshake to be complete. + auto signingComplete = + [onComplete](const std::shared_ptr<Aws::Crt::Http::HttpRequest> &req1, int errorCode) { + onComplete(req1, errorCode); + }; + + auto signerConfig = websocketConfig.CreateSigningConfigCb(); + + websocketConfig.Signer->SignRequest(req, *signerConfig, signingComplete); + }; + + m_options->withWebsocketHandshakeTransformCallback(signerTransform); + bool useWebsocketProxyOptions = + m_websocketConfig->ProxyOptions.has_value() && !m_proxyOptions.has_value(); + if (useWebsocketProxyOptions) + { + m_options->withHttpProxyOptions(m_websocketConfig->ProxyOptions.value()); + } + else if (m_proxyOptions.has_value()) + { + m_options->withHttpProxyOptions(m_proxyOptions.value()); + } + } + + return Crt::Mqtt5::Mqtt5Client::NewMqtt5Client(*m_options, m_allocator); + } + + Aws::Iot::Mqtt5CustomAuthConfig::Mqtt5CustomAuthConfig(Crt::Allocator *allocator) noexcept + : m_allocator(allocator) + { + AWS_ZERO_STRUCT(m_passwordStorage); + } + + Aws::Iot::Mqtt5CustomAuthConfig::~Mqtt5CustomAuthConfig() { aws_byte_buf_clean_up(&m_passwordStorage); } + + Aws::Iot::Mqtt5CustomAuthConfig::Mqtt5CustomAuthConfig(const Mqtt5CustomAuthConfig &rhs) + { + if (&rhs != this) + { + m_allocator = rhs.m_allocator; + if (rhs.m_authorizerName.has_value()) + { + m_authorizerName = rhs.m_authorizerName.value(); + } + if (rhs.m_tokenKeyName.has_value()) + { + m_tokenKeyName = rhs.m_tokenKeyName.value(); + } + if (rhs.m_tokenSignature.has_value()) + { + m_tokenSignature = rhs.m_tokenSignature.value(); + } + if (rhs.m_tokenValue.has_value()) + { + m_tokenValue = rhs.m_tokenValue.value(); + } + if (rhs.m_username.has_value()) + { + m_username = rhs.m_username.value(); + } + if (rhs.m_password.has_value()) + { + AWS_ZERO_STRUCT(m_passwordStorage); + aws_byte_buf_init_copy_from_cursor(&m_passwordStorage, m_allocator, rhs.m_password.value()); + m_password = aws_byte_cursor_from_buf(&m_passwordStorage); + } + } + } + + Mqtt5CustomAuthConfig &Aws::Iot::Mqtt5CustomAuthConfig::operator=(const Mqtt5CustomAuthConfig &rhs) + { + if (&rhs != this) + { + m_allocator = rhs.m_allocator; + if (rhs.m_authorizerName.has_value()) + { + m_authorizerName = rhs.m_authorizerName.value(); + } + if (rhs.m_tokenKeyName.has_value()) + { + m_tokenKeyName = rhs.m_tokenKeyName.value(); + } + if (rhs.m_tokenSignature.has_value()) + { + m_tokenSignature = rhs.m_tokenSignature.value(); + } + if (rhs.m_tokenValue.has_value()) + { + m_tokenValue = rhs.m_tokenValue.value(); + } + if (rhs.m_username.has_value()) + { + m_username = rhs.m_username.value(); + } + if (rhs.m_password.has_value()) + { + aws_byte_buf_clean_up(&m_passwordStorage); + AWS_ZERO_STRUCT(m_passwordStorage); + aws_byte_buf_init_copy_from_cursor(&m_passwordStorage, m_allocator, rhs.m_password.value()); + m_password = aws_byte_cursor_from_buf(&m_passwordStorage); + } + } + return *this; + } + + const Crt::Optional<Crt::String> &Mqtt5CustomAuthConfig::GetAuthorizerName() { return m_authorizerName; } + + const Crt::Optional<Crt::String> &Mqtt5CustomAuthConfig::GetUsername() { return m_username; } + + const Crt::Optional<Crt::ByteCursor> &Mqtt5CustomAuthConfig::GetPassword() { return m_password; } + + const Crt::Optional<Crt::String> &Mqtt5CustomAuthConfig::GetTokenKeyName() { return m_tokenKeyName; } + + const Crt::Optional<Crt::String> &Mqtt5CustomAuthConfig::GetTokenValue() { return m_tokenValue; } + + const Crt::Optional<Crt::String> &Mqtt5CustomAuthConfig::GetTokenSignature() { return m_tokenSignature; } + + Mqtt5CustomAuthConfig &Aws::Iot::Mqtt5CustomAuthConfig::WithAuthorizerName(Crt::String authName) + { + m_authorizerName = std::move(authName); + return *this; + } + + Mqtt5CustomAuthConfig &Aws::Iot::Mqtt5CustomAuthConfig::WithUsername(Crt::String username) + { + m_username = std::move(username); + return *this; + } + + Mqtt5CustomAuthConfig &Aws::Iot::Mqtt5CustomAuthConfig::WithPassword(Crt::ByteCursor password) + { + aws_byte_buf_clean_up(&m_passwordStorage); + AWS_ZERO_STRUCT(m_passwordStorage); + aws_byte_buf_init_copy_from_cursor(&m_passwordStorage, m_allocator, password); + m_password = aws_byte_cursor_from_buf(&m_passwordStorage); + return *this; + } + + Mqtt5CustomAuthConfig &Aws::Iot::Mqtt5CustomAuthConfig::WithTokenKeyName(Crt::String tokenKeyName) + { + m_tokenKeyName = std::move(tokenKeyName); + return *this; + } + + Mqtt5CustomAuthConfig &Aws::Iot::Mqtt5CustomAuthConfig::WithTokenValue(Crt::String tokenValue) + { + m_tokenValue = std::move(tokenValue); + return *this; + } + + Mqtt5CustomAuthConfig &Aws::Iot::Mqtt5CustomAuthConfig::WithTokenSignature(Crt::String tokenSignature) + { + m_tokenSignature = std::move(tokenSignature); + return *this; + } + + } // namespace Iot +} // namespace Aws + +#endif // !BYO_CRYPTO diff --git a/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttClient.cpp b/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttClient.cpp new file mode 100644 index 0000000000..3f80782b3f --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttClient.cpp @@ -0,0 +1,541 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/iot/MqttClient.h> + +#include <aws/crt/Api.h> +#include <aws/crt/auth/Credentials.h> +#include <aws/crt/auth/Sigv4Signing.h> +#include <aws/crt/http/HttpRequestResponse.h> + +#if !BYO_CRYPTO + +namespace Aws +{ + namespace Iot + { + + MqttClientConnectionConfig::MqttClientConnectionConfig(int lastError) noexcept + : m_port(0), m_lastError(lastError) + { + } + + MqttClientConnectionConfig MqttClientConnectionConfig::CreateInvalid(int lastError) noexcept + { + return MqttClientConnectionConfig(lastError); + } + + MqttClientConnectionConfig::MqttClientConnectionConfig( + const Crt::String &endpoint, + uint16_t port, + const Crt::Io::SocketOptions &socketOptions, + Crt::Io::TlsContext &&tlsContext) + : m_endpoint(endpoint), m_port(port), m_context(std::move(tlsContext)), m_socketOptions(socketOptions), + m_lastError(0) + { + } + + MqttClientConnectionConfig::MqttClientConnectionConfig( + const Crt::String &endpoint, + uint16_t port, + const Crt::Io::SocketOptions &socketOptions, + Crt::Io::TlsContext &&tlsContext, + Crt::Mqtt::OnWebSocketHandshakeIntercept &&interceptor, + const Crt::Optional<Crt::Http::HttpClientConnectionProxyOptions> &proxyOptions) + : m_endpoint(endpoint), m_port(port), m_context(std::move(tlsContext)), m_socketOptions(socketOptions), + m_webSocketInterceptor(std::move(interceptor)), m_proxyOptions(proxyOptions), m_lastError(0) + { + } + + MqttClientConnectionConfig::MqttClientConnectionConfig( + const Crt::String &endpoint, + uint16_t port, + const Crt::Io::SocketOptions &socketOptions, + Crt::Io::TlsContext &&tlsContext, + const Crt::Optional<Crt::Http::HttpClientConnectionProxyOptions> &proxyOptions) + : m_endpoint(endpoint), m_port(port), m_context(std::move(tlsContext)), m_socketOptions(socketOptions), + m_proxyOptions(proxyOptions), m_lastError(0) + { + } + + MqttClientConnectionConfigBuilder::MqttClientConnectionConfigBuilder() + : MqttClientConnectionConfigBuilder(Crt::ApiAllocator()) + { + m_lastError = AWS_ERROR_INVALID_STATE; + } + + // Common setup shared by all valid constructors + MqttClientConnectionConfigBuilder::MqttClientConnectionConfigBuilder(Crt::Allocator *allocator) noexcept + : m_allocator(allocator), m_portOverride(0), +# ifdef AWS_IOT_SDK_VERSION + m_sdkVersion(AWS_IOT_SDK_VERSION), +# else + m_sdkVersion(AWS_CRT_CPP_VERSION), +# endif + m_lastError(0) + { + m_socketOptions.SetConnectTimeoutMs(3000); + } + + MqttClientConnectionConfigBuilder::MqttClientConnectionConfigBuilder( + const char *certPath, + const char *pkeyPath, + Crt::Allocator *allocator) noexcept + : MqttClientConnectionConfigBuilder(allocator) + { + m_contextOptions = Crt::Io::TlsContextOptions::InitClientWithMtls(certPath, pkeyPath, allocator); + if (!m_contextOptions) + { + m_lastError = m_contextOptions.LastError(); + return; + } + } + + MqttClientConnectionConfigBuilder::MqttClientConnectionConfigBuilder( + const Crt::ByteCursor &cert, + const Crt::ByteCursor &pkey, + Crt::Allocator *allocator) noexcept + : MqttClientConnectionConfigBuilder(allocator) + { + m_contextOptions = Crt::Io::TlsContextOptions::InitClientWithMtls(cert, pkey, allocator); + if (!m_contextOptions) + { + m_lastError = m_contextOptions.LastError(); + return; + } + } + + MqttClientConnectionConfigBuilder::MqttClientConnectionConfigBuilder( + const Crt::Io::TlsContextPkcs11Options &pkcs11Options, + Crt::Allocator *allocator) noexcept + : MqttClientConnectionConfigBuilder(allocator) + { + m_contextOptions = Crt::Io::TlsContextOptions::InitClientWithMtlsPkcs11(pkcs11Options, allocator); + if (!m_contextOptions) + { + m_lastError = m_contextOptions.LastError(); + return; + } + } + + MqttClientConnectionConfigBuilder::MqttClientConnectionConfigBuilder( + const char *windowsCertStorePath, + Crt::Allocator *allocator) noexcept + : MqttClientConnectionConfigBuilder(allocator) + { + m_contextOptions = + Crt::Io::TlsContextOptions::InitClientWithMtlsSystemPath(windowsCertStorePath, allocator); + if (!m_contextOptions) + { + m_lastError = m_contextOptions.LastError(); + return; + } + } + + MqttClientConnectionConfigBuilder::MqttClientConnectionConfigBuilder( + const WebsocketConfig &config, + Crt::Allocator *allocator) noexcept + : MqttClientConnectionConfigBuilder(allocator) + { + m_contextOptions = Crt::Io::TlsContextOptions::InitDefaultClient(allocator); + if (!m_contextOptions) + { + m_lastError = m_contextOptions.LastError(); + return; + } + + m_websocketConfig = config; + } + + MqttClientConnectionConfigBuilder MqttClientConnectionConfigBuilder::NewDefaultBuilder() noexcept + { + MqttClientConnectionConfigBuilder return_value = + MqttClientConnectionConfigBuilder(Aws::Crt::ApiAllocator()); + return_value.m_contextOptions = Crt::Io::TlsContextOptions::InitDefaultClient(); + return return_value; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithEndpoint(const Crt::String &endpoint) + { + m_endpoint = endpoint; + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithEndpoint(Crt::String &&endpoint) + { + m_endpoint = std::move(endpoint); + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithMetricsCollection(bool enabled) + { + m_enableMetricsCollection = enabled; + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithSdkName(const Crt::String &sdkName) + { + m_sdkName = sdkName; + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithSdkVersion( + const Crt::String &sdkVersion) + { + m_sdkVersion = sdkVersion; + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithPortOverride(uint16_t port) noexcept + { + m_portOverride = port; + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithCertificateAuthority( + const char *caPath) noexcept + { + if (m_contextOptions) + { + if (!m_contextOptions.OverrideDefaultTrustStore(nullptr, caPath)) + { + m_lastError = m_contextOptions.LastError(); + } + } + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithCertificateAuthority( + const Crt::ByteCursor &cert) noexcept + { + if (m_contextOptions) + { + if (!m_contextOptions.OverrideDefaultTrustStore(cert)) + { + m_lastError = m_contextOptions.LastError(); + } + } + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithTcpKeepAlive() noexcept + { + m_socketOptions.SetKeepAlive(true); + return *this; + } + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithTcpConnectTimeout( + uint32_t connectTimeoutMs) noexcept + { + m_socketOptions.SetConnectTimeoutMs(connectTimeoutMs); + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithTcpKeepAliveTimeout( + uint16_t keepAliveTimeoutSecs) noexcept + { + m_socketOptions.SetKeepAliveTimeoutSec(keepAliveTimeoutSecs); + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithTcpKeepAliveInterval( + uint16_t keepAliveIntervalSecs) noexcept + { + m_socketOptions.SetKeepAliveIntervalSec(keepAliveIntervalSecs); + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithTcpKeepAliveMaxProbes( + uint16_t maxProbes) noexcept + { + m_socketOptions.SetKeepAliveMaxFailedProbes(maxProbes); + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithMinimumTlsVersion( + aws_tls_versions minimumTlsVersion) noexcept + { + m_contextOptions.SetMinimumTlsVersion(minimumTlsVersion); + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithHttpProxyOptions( + const Crt::Http::HttpClientConnectionProxyOptions &proxyOptions) noexcept + { + m_proxyOptions = proxyOptions; + return *this; + } + + Crt::String MqttClientConnectionConfigBuilder::AddToUsernameParameter( + Crt::String currentUsername, + Crt::String parameterValue, + Crt::String parameterPreText) + { + Crt::String return_string = currentUsername; + if (return_string.find("?") != Crt::String::npos) + { + return_string += "&"; + } + else + { + return_string += "?"; + } + + if (parameterValue.find(parameterPreText) != Crt::String::npos) + { + return return_string + parameterValue; + } + else + { + return return_string + parameterPreText + parameterValue; + } + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithCustomAuthorizer( + const Crt::String &username, + const Crt::String &authorizerName, + const Crt::String &authorizerSignature, + const Crt::String &password) noexcept + { + if (!m_contextOptions.IsAlpnSupported()) + { + m_lastError = AWS_ERROR_INVALID_STATE; + return *this; + } + + m_isUsingCustomAuthorizer = true; + Crt::String usernameString = ""; + + if (username.empty()) + { + if (!m_username.empty()) + { + usernameString += m_username; + } + } + else + { + usernameString += username; + } + + if (!authorizerName.empty()) + { + usernameString = AddToUsernameParameter(usernameString, authorizerName, "x-amz-customauthorizer-name="); + } + if (!authorizerSignature.empty()) + { + usernameString = + AddToUsernameParameter(usernameString, authorizerSignature, "x-amz-customauthorizer-signature="); + } + + m_username = usernameString; + m_password = password; + + if (!m_websocketConfig) + { + if (!m_contextOptions.SetAlpnList("mqtt")) + { + m_lastError = m_contextOptions.LastError(); + } + m_portOverride = 443; + } + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithUsername( + const Crt::String &username) noexcept + { + m_username = username; + return *this; + } + + MqttClientConnectionConfigBuilder &MqttClientConnectionConfigBuilder::WithPassword( + const Crt::String &password) noexcept + { + m_password = password; + return *this; + } + + MqttClientConnectionConfig MqttClientConnectionConfigBuilder::Build() noexcept + { + if (m_lastError != 0) + { + return MqttClientConnectionConfig::CreateInvalid(m_lastError); + } + + uint16_t port = m_portOverride; + + if (!m_portOverride) + { + if (m_websocketConfig || Crt::Io::TlsContextOptions::IsAlpnSupported()) + { + port = 443; + } + else + { + port = 8883; + } + } + + Crt::String username = m_username; + Crt::String password = m_password; + + // Check to see if a custom authorizer is being used but not through the builder + if (!m_isUsingCustomAuthorizer) + { + if (!m_username.empty()) + { + if (m_username.find_first_of("x-amz-customauthorizer-name=") != Crt::String::npos || + m_username.find_first_of("x-amz-customauthorizer-signature=") != Crt::String::npos) + { + m_isUsingCustomAuthorizer = true; + } + } + } + + if (port == 443 && !m_websocketConfig && Crt::Io::TlsContextOptions::IsAlpnSupported() && + !m_isUsingCustomAuthorizer) + { + if (!m_contextOptions.SetAlpnList("x-amzn-mqtt-ca")) + { + return MqttClientConnectionConfig::CreateInvalid(m_contextOptions.LastError()); + } + } + + // Is the user trying to connect using a custom authorizer? + if (m_isUsingCustomAuthorizer) + { + if (port != 443) + { + AWS_LOGF_WARN( + AWS_LS_MQTT_GENERAL, + "Attempting to connect to authorizer with unsupported port. Port is not 443..."); + } + } + + // add metrics string to username (if metrics enabled) + if (m_enableMetricsCollection) + { + if (username.find('?') != Crt::String::npos) + { + username += "&"; + } + else + { + username += "?"; + } + username += "SDK="; + username += m_sdkName; + username += "&Version="; + username += m_sdkVersion; + } + + auto tlsContext = Crt::Io::TlsContext(m_contextOptions, Crt::Io::TlsMode::CLIENT, m_allocator); + if (!tlsContext) + { + return MqttClientConnectionConfig::CreateInvalid(tlsContext.GetInitializationError()); + } + + if (!m_websocketConfig) + { + auto config = MqttClientConnectionConfig( + m_endpoint, port, m_socketOptions, std::move(tlsContext), m_proxyOptions); + config.m_username = username; + config.m_password = password; + return config; + } + + auto websocketConfig = m_websocketConfig.value(); + auto signerTransform = [websocketConfig]( + std::shared_ptr<Crt::Http::HttpRequest> req, + const Crt::Mqtt::OnWebSocketHandshakeInterceptComplete &onComplete) { + // it is only a very happy coincidence that these function signatures match. This is the callback + // for signing to be complete. It invokes the callback for websocket handshake to be complete. + auto signingComplete = + [onComplete](const std::shared_ptr<Aws::Crt::Http::HttpRequest> &req1, int errorCode) { + onComplete(req1, errorCode); + }; + + auto signerConfig = websocketConfig.CreateSigningConfigCb(); + + websocketConfig.Signer->SignRequest(req, *signerConfig, signingComplete); + }; + + bool useWebsocketProxyOptions = m_websocketConfig->ProxyOptions.has_value() && !m_proxyOptions.has_value(); + + auto config = MqttClientConnectionConfig( + m_endpoint, + port, + m_socketOptions, + std::move(tlsContext), + signerTransform, + useWebsocketProxyOptions ? m_websocketConfig->ProxyOptions : m_proxyOptions); + config.m_username = username; + config.m_password = password; + return config; + } + + MqttClient::MqttClient(Crt::Io::ClientBootstrap &bootstrap, Crt::Allocator *allocator) noexcept + : m_client(bootstrap, allocator), m_lastError(0) + { + if (!m_client) + { + m_lastError = m_client.LastError(); + } + } + + MqttClient::MqttClient(Crt::Allocator *allocator) noexcept + : MqttClient(*Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap(), allocator) + { + } + + std::shared_ptr<Crt::Mqtt::MqttConnection> MqttClient::NewConnection( + const MqttClientConnectionConfig &config) noexcept + { + if (!config) + { + m_lastError = config.LastError(); + return nullptr; + } + + bool useWebsocket = config.m_webSocketInterceptor.operator bool(); + auto newConnection = m_client.NewConnection( + config.m_endpoint.c_str(), config.m_port, config.m_socketOptions, config.m_context, useWebsocket); + + if (!newConnection) + { + m_lastError = m_client.LastError(); + return nullptr; + } + + if (!(*newConnection)) + { + m_lastError = newConnection->LastError(); + return nullptr; + } + + if (!config.m_username.empty() || !config.m_password.empty()) + { + if (!newConnection->SetLogin(config.m_username.c_str(), config.m_password.c_str())) + { + m_lastError = newConnection->LastError(); + return nullptr; + } + } + + if (useWebsocket) + { + newConnection->WebsocketInterceptor = config.m_webSocketInterceptor; + } + + if (config.m_proxyOptions) + { + newConnection->SetHttpProxyOptions(config.m_proxyOptions.value()); + } + + return newConnection; + } + } // namespace Iot +} // namespace Aws + +#endif // !BYO_CRYPTO diff --git a/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttCommon.cpp b/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttCommon.cpp new file mode 100644 index 0000000000..e96c7b5e79 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/iot/MqttCommon.cpp @@ -0,0 +1,88 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/iot/MqttClient.h> + +#include <aws/crt/Api.h> +#include <aws/crt/auth/Credentials.h> +#include <aws/crt/auth/Sigv4Signing.h> +#include <aws/crt/http/HttpRequestResponse.h> + +#if !BYO_CRYPTO + +namespace Aws +{ + namespace Iot + { + WebsocketConfig::WebsocketConfig( + const Crt::String &signingRegion, + Crt::Io::ClientBootstrap *bootstrap, + Crt::Allocator *allocator) noexcept + : SigningRegion(signingRegion), ServiceName("iotdevicegateway") + { + Crt::Auth::CredentialsProviderChainDefaultConfig config; + config.Bootstrap = bootstrap; + + CredentialsProvider = + Crt::Auth::CredentialsProvider::CreateCredentialsProviderChainDefault(config, allocator); + + Signer = Aws::Crt::MakeShared<Crt::Auth::Sigv4HttpRequestSigner>(allocator, allocator); + + auto credsProviderRef = CredentialsProvider; + auto signingRegionCopy = SigningRegion; + auto serviceNameCopy = ServiceName; + CreateSigningConfigCb = [allocator, credsProviderRef, signingRegionCopy, serviceNameCopy]() { + auto signerConfig = Aws::Crt::MakeShared<Crt::Auth::AwsSigningConfig>(allocator); + signerConfig->SetRegion(signingRegionCopy); + signerConfig->SetService(serviceNameCopy); + signerConfig->SetSigningAlgorithm(Crt::Auth::SigningAlgorithm::SigV4); + signerConfig->SetSignatureType(Crt::Auth::SignatureType::HttpRequestViaQueryParams); + signerConfig->SetOmitSessionToken(true); + signerConfig->SetCredentialsProvider(credsProviderRef); + + return signerConfig; + }; + } + + WebsocketConfig::WebsocketConfig(const Crt::String &signingRegion, Crt::Allocator *allocator) noexcept + : WebsocketConfig(signingRegion, Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap(), allocator) + { + } + + WebsocketConfig::WebsocketConfig( + const Crt::String &signingRegion, + const std::shared_ptr<Crt::Auth::ICredentialsProvider> &credentialsProvider, + Crt::Allocator *allocator) noexcept + : CredentialsProvider(credentialsProvider), + Signer(Aws::Crt::MakeShared<Crt::Auth::Sigv4HttpRequestSigner>(allocator, allocator)), + SigningRegion(signingRegion), ServiceName("iotdevicegateway") + { + auto credsProviderRef = CredentialsProvider; + auto signingRegionCopy = SigningRegion; + auto serviceNameCopy = ServiceName; + CreateSigningConfigCb = [allocator, credsProviderRef, signingRegionCopy, serviceNameCopy]() { + auto signerConfig = Aws::Crt::MakeShared<Crt::Auth::AwsSigningConfig>(allocator); + signerConfig->SetRegion(signingRegionCopy); + signerConfig->SetService(serviceNameCopy); + signerConfig->SetSigningAlgorithm(Crt::Auth::SigningAlgorithm::SigV4); + signerConfig->SetSignatureType(Crt::Auth::SignatureType::HttpRequestViaQueryParams); + signerConfig->SetOmitSessionToken(true); + signerConfig->SetCredentialsProvider(credsProviderRef); + + return signerConfig; + }; + } + + WebsocketConfig::WebsocketConfig( + const std::shared_ptr<Crt::Auth::ICredentialsProvider> &credentialsProvider, + const std::shared_ptr<Crt::Auth::IHttpRequestSigner> &signer, + Iot::CreateSigningConfig createSigningConfig) noexcept + : CredentialsProvider(credentialsProvider), Signer(signer), + CreateSigningConfigCb(std::move(createSigningConfig)), ServiceName("iotdevicegateway") + { + } + } // namespace Iot +} // namespace Aws + +#endif // !BYO_CRYPTO diff --git a/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Client.cpp b/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Client.cpp new file mode 100644 index 0000000000..b8e1217d73 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Client.cpp @@ -0,0 +1,743 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/mqtt/Mqtt5Client.h> +#include <aws/crt/mqtt/Mqtt5Packets.h> + +#include <aws/crt/Api.h> +#include <aws/crt/StlAllocator.h> +#include <aws/crt/http/HttpProxyStrategy.h> +#include <aws/crt/http/HttpRequestResponse.h> +#include <aws/crt/io/Bootstrap.h> +#include <aws/iot/MqttClient.h> + +#include <utility> + +namespace Aws +{ + namespace Crt + { + namespace Mqtt5 + { + struct PubAckCallbackData : public std::enable_shared_from_this<PubAckCallbackData> + { + PubAckCallbackData(Allocator *alloc = ApiAllocator()) : client(nullptr), allocator(alloc) {} + + std::shared_ptr<Mqtt5Client> client; + OnPublishCompletionHandler onPublishCompletion; + Allocator *allocator; + }; + + struct SubAckCallbackData + { + SubAckCallbackData(Allocator *alloc = ApiAllocator()) : client(nullptr), allocator(alloc) {} + + std::shared_ptr<Mqtt5Client> client; + OnSubscribeCompletionHandler onSubscribeCompletion; + Allocator *allocator; + }; + + struct UnSubAckCallbackData + { + UnSubAckCallbackData(Allocator *alloc = ApiAllocator()) : client(nullptr), allocator(alloc) {} + + std::shared_ptr<Mqtt5Client> client; + OnUnsubscribeCompletionHandler onUnsubscribeCompletion; + Allocator *allocator; + }; + + void Mqtt5Client::s_lifeCycleEventCallback(const struct aws_mqtt5_client_lifecycle_event *event) + { + Mqtt5Client *client = reinterpret_cast<Mqtt5Client *>(event->user_data); + switch (event->event_type) + { + case AWS_MQTT5_CLET_STOPPED: + AWS_LOGF_INFO(AWS_LS_MQTT5_CLIENT, "Lifecycle event: Client Stopped!"); + if (client->onStopped) + { + OnStoppedEventData eventData; + client->onStopped(*client, eventData); + } + break; + + case AWS_MQTT5_CLET_ATTEMPTING_CONNECT: + AWS_LOGF_INFO(AWS_LS_MQTT5_CLIENT, "Lifecycle event: Attempting Connect!"); + if (client->onAttemptingConnect) + { + OnAttemptingConnectEventData eventData; + client->onAttemptingConnect(*client, eventData); + } + break; + + case AWS_MQTT5_CLET_CONNECTION_FAILURE: + AWS_LOGF_INFO(AWS_LS_MQTT5_CLIENT, "Lifecycle event: Connection Failure!"); + AWS_LOGF_INFO( + AWS_LS_MQTT5_CLIENT, + " Error Code: %d(%s)", + event->error_code, + aws_error_debug_str(event->error_code)); + if (client->onConnectionFailure) + { + OnConnectionFailureEventData eventData; + eventData.errorCode = event->error_code; + std::shared_ptr<ConnAckPacket> packet = nullptr; + if (event->connack_data != NULL) + { + packet = Aws::Crt::MakeShared<ConnAckPacket>( + client->m_allocator, *event->connack_data, client->m_allocator); + eventData.connAckPacket = packet; + } + client->onConnectionFailure(*client, eventData); + } + break; + + case AWS_MQTT5_CLET_CONNECTION_SUCCESS: + AWS_LOGF_INFO(AWS_LS_MQTT5_CLIENT, "Lifecycle event: Connection Success!"); + if (client->onConnectionSuccess) + { + OnConnectionSuccessEventData eventData; + + std::shared_ptr<ConnAckPacket> packet = nullptr; + if (event->connack_data != NULL) + { + packet = Aws::Crt::MakeShared<ConnAckPacket>(ApiAllocator(), *event->connack_data); + } + + std::shared_ptr<NegotiatedSettings> neg_settings = nullptr; + if (event->settings != NULL) + { + neg_settings = + Aws::Crt::MakeShared<NegotiatedSettings>(ApiAllocator(), *event->settings); + } + + eventData.connAckPacket = packet; + eventData.negotiatedSettings = neg_settings; + client->onConnectionSuccess(*client, eventData); + } + break; + + case AWS_MQTT5_CLET_DISCONNECTION: + AWS_LOGF_INFO( + AWS_LS_MQTT5_CLIENT, + " Error Code: %d(%s)", + event->error_code, + aws_error_debug_str(event->error_code)); + if (client->onDisconnection) + { + OnDisconnectionEventData eventData; + std::shared_ptr<DisconnectPacket> disconnection = nullptr; + if (event->disconnect_data != nullptr) + { + disconnection = Aws::Crt::MakeShared<DisconnectPacket>( + client->m_allocator, *event->disconnect_data, client->m_allocator); + } + eventData.errorCode = event->error_code; + eventData.disconnectPacket = disconnection; + client->onDisconnection(*client, eventData); + } + break; + } + } + + void Mqtt5Client::s_publishReceivedCallback( + const struct aws_mqtt5_packet_publish_view *publish, + void *user_data) + { + AWS_LOGF_INFO(AWS_LS_MQTT5_CLIENT, "on publish recieved callback"); + Mqtt5Client *client = reinterpret_cast<Mqtt5Client *>(user_data); + if (client != nullptr && client->onPublishReceived != nullptr) + { + if (publish != NULL) + { + std::shared_ptr<PublishPacket> packet = + std::make_shared<PublishPacket>(*publish, client->m_allocator); + PublishReceivedEventData eventData; + eventData.publishPacket = packet; + client->onPublishReceived(*client, eventData); + } + else + { + AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "Failed to access Publish packet view."); + } + } + } + + void Mqtt5Client::s_publishCompletionCallback( + enum aws_mqtt5_packet_type packet_type, + const void *publshCompletionPacket, + int error_code, + void *complete_ctx) + { + AWS_LOGF_INFO(AWS_LS_MQTT5_CLIENT, "Publish completion callback triggered."); + auto callbackData = reinterpret_cast<PubAckCallbackData *>(complete_ctx); + + if (callbackData) + { + std::shared_ptr<PublishResult> publish = nullptr; + switch (packet_type) + { + case aws_mqtt5_packet_type::AWS_MQTT5_PT_PUBACK: + { + if (publshCompletionPacket != NULL) + { + std::shared_ptr<PubAckPacket> packet = std::make_shared<PubAckPacket>( + *(aws_mqtt5_packet_puback_view *)publshCompletionPacket, callbackData->allocator); + publish = std::make_shared<PublishResult>(std::move(packet)); + } + else // This should never happened. + { + AWS_LOGF_INFO(AWS_LS_MQTT5_CLIENT, "The PubAck Packet is invalid."); + publish = std::make_shared<PublishResult>(AWS_ERROR_INVALID_ARGUMENT); + } + break; + } + case aws_mqtt5_packet_type::AWS_MQTT5_PT_NONE: + { + publish = std::make_shared<PublishResult>(error_code); + break; + } + default: // Invalid packet type + { + AWS_LOGF_INFO(AWS_LS_MQTT5_CLIENT, "Invalid Packet Type."); + publish = std::make_shared<PublishResult>(AWS_ERROR_INVALID_ARGUMENT); + break; + } + } + if (callbackData->onPublishCompletion != NULL) + { + callbackData->onPublishCompletion(callbackData->client, error_code, publish); + } + + Crt::Delete(callbackData, callbackData->allocator); + } + } + + void Mqtt5Client::s_onWebsocketHandshake( + struct aws_http_message *rawRequest, + void *user_data, + aws_mqtt5_transform_websocket_handshake_complete_fn *complete_fn, + void *complete_ctx) + { + auto client = reinterpret_cast<Mqtt5Client *>(user_data); + + Allocator *allocator = client->m_allocator; + // we have to do this because of private constructors. + auto toSeat = + reinterpret_cast<Http::HttpRequest *>(aws_mem_acquire(allocator, sizeof(Http::HttpRequest))); + toSeat = new (toSeat) Http::HttpRequest(allocator, rawRequest); + + std::shared_ptr<Http::HttpRequest> request = std::shared_ptr<Http::HttpRequest>( + toSeat, [allocator](Http::HttpRequest *ptr) { Crt::Delete(ptr, allocator); }); + + auto onInterceptComplete = + [complete_fn, + complete_ctx](const std::shared_ptr<Http::HttpRequest> &transformedRequest, int errorCode) { + complete_fn(transformedRequest->GetUnderlyingMessage(), errorCode, complete_ctx); + }; + + client->websocketInterceptor(request, onInterceptComplete); + } + + void Mqtt5Client::s_clientTerminationCompletion(void *complete_ctx) + { + Mqtt5Client *client = reinterpret_cast<Mqtt5Client *>(complete_ctx); + std::unique_lock<std::mutex> lock(client->m_terminationMutex); + client->m_terminationPredicate = true; + client->m_terminationCondition.notify_all(); + } + + void Mqtt5Client::s_subscribeCompletionCallback( + const aws_mqtt5_packet_suback_view *suback, + int error_code, + void *complete_ctx) + { + SubAckCallbackData *callbackData = reinterpret_cast<SubAckCallbackData *>(complete_ctx); + AWS_ASSERT(callbackData != nullptr); + + std::shared_ptr<SubAckPacket> packet = nullptr; + if (suback != nullptr) + { + packet = std::make_shared<SubAckPacket>(*suback, callbackData->allocator); + } + + if (error_code != 0) + { + AWS_LOGF_INFO( + AWS_LS_MQTT5_CLIENT, + "SubscribeCompletion Failed with Error Code: %d(%s)", + error_code, + aws_error_debug_str(error_code)); + } + + if (callbackData->onSubscribeCompletion) + { + callbackData->onSubscribeCompletion(callbackData->client, error_code, packet); + } + Crt::Delete(callbackData, callbackData->allocator); + } + + void Mqtt5Client::s_unsubscribeCompletionCallback( + const aws_mqtt5_packet_unsuback_view *unsuback, + int error_code, + void *complete_ctx) + { + UnSubAckCallbackData *callbackData = reinterpret_cast<UnSubAckCallbackData *>(complete_ctx); + AWS_ASSERT(callbackData != nullptr); + + std::shared_ptr<UnSubAckPacket> packet = nullptr; + if (unsuback != nullptr) + { + packet = std::make_shared<UnSubAckPacket>(*unsuback, callbackData->allocator); + } + + if (error_code != 0) + { + AWS_LOGF_INFO( + AWS_LS_MQTT5_CLIENT, + "UnsubscribeCompletion Failed with Error Code: %d(%s)", + error_code, + aws_error_debug_str(error_code)); + } + + if (callbackData->onUnsubscribeCompletion != NULL) + { + callbackData->onUnsubscribeCompletion(callbackData->client, error_code, packet); + } + + Crt::Delete(callbackData, callbackData->allocator); + } + + Mqtt5Client::Mqtt5Client(const Mqtt5ClientOptions &options, Allocator *allocator) noexcept + : m_client(nullptr), m_allocator(allocator) + { + aws_mqtt5_client_options clientOptions; + + options.initializeRawOptions(clientOptions); + + /* Setup Callbacks */ + if (options.websocketHandshakeTransform) + { + this->websocketInterceptor = options.websocketHandshakeTransform; + clientOptions.websocket_handshake_transform = &Mqtt5Client::s_onWebsocketHandshake; + clientOptions.websocket_handshake_transform_user_data = this; + } + + if (options.onConnectionFailure) + { + this->onConnectionFailure = options.onConnectionFailure; + } + + if (options.onConnectionSuccess) + { + this->onConnectionSuccess = options.onConnectionSuccess; + } + + if (options.onDisconnection) + { + this->onDisconnection = options.onDisconnection; + } + + if (options.onPublishReceived) + { + this->onPublishReceived = options.onPublishReceived; + } + + if (options.onStopped) + { + this->onStopped = options.onStopped; + } + + if (options.onAttemptingConnect) + { + this->onAttemptingConnect = options.onAttemptingConnect; + } + + clientOptions.publish_received_handler_user_data = this; + clientOptions.publish_received_handler = &Mqtt5Client::s_publishReceivedCallback; + + clientOptions.lifecycle_event_handler = &Mqtt5Client::s_lifeCycleEventCallback; + clientOptions.lifecycle_event_handler_user_data = this; + + clientOptions.client_termination_handler = &Mqtt5Client::s_clientTerminationCompletion; + clientOptions.client_termination_handler_user_data = this; + + m_client = aws_mqtt5_client_new(allocator, &clientOptions); + } + + Mqtt5Client::~Mqtt5Client() + { + if (m_client != nullptr) + { + aws_mqtt5_client_release(m_client); + std::unique_lock<std::mutex> lock(m_terminationMutex); + m_terminationCondition.wait(lock, [this] { return m_terminationPredicate == true; }); + m_client = nullptr; + } + } + + std::shared_ptr<Mqtt5Client> Mqtt5Client::NewMqtt5Client( + const Mqtt5ClientOptions &options, + Allocator *allocator) noexcept + { + /* Copied from MqttClient.cpp:ln754 */ + // As the constructor is private, make share would not work here. We do make_share manually. + Mqtt5Client *toSeat = reinterpret_cast<Mqtt5Client *>(aws_mem_acquire(allocator, sizeof(Mqtt5Client))); + if (!toSeat) + { + return nullptr; + } + + toSeat = new (toSeat) Mqtt5Client(options, allocator); + return std::shared_ptr<Mqtt5Client>( + toSeat, [allocator](Mqtt5Client *client) { Crt::Delete(client, allocator); }); + } + + Mqtt5Client::operator bool() const noexcept { return m_client != nullptr; } + + int Mqtt5Client::LastError() const noexcept { return aws_last_error(); } + + bool Mqtt5Client::Start() const noexcept { return aws_mqtt5_client_start(m_client) == AWS_OP_SUCCESS; } + + bool Mqtt5Client::Stop() noexcept { return aws_mqtt5_client_stop(m_client, NULL, NULL) == AWS_OP_SUCCESS; } + + bool Mqtt5Client::Stop(std::shared_ptr<DisconnectPacket> disconnectOptions) noexcept + { + if (disconnectOptions == nullptr) + { + return Stop(); + } + + aws_mqtt5_packet_disconnect_view disconnect_packet; + AWS_ZERO_STRUCT(disconnect_packet); + if (disconnectOptions->initializeRawOptions(disconnect_packet) == false) + { + return false; + } + return aws_mqtt5_client_stop(m_client, &disconnect_packet, NULL) == AWS_OP_SUCCESS; + } + + bool Mqtt5Client::Publish( + std::shared_ptr<PublishPacket> publishOptions, + OnPublishCompletionHandler onPublishCmpletionCallback) noexcept + { + if (publishOptions == nullptr) + { + return false; + } + + aws_mqtt5_packet_publish_view publish; + publishOptions->initializeRawOptions(publish); + + PubAckCallbackData *pubCallbackData = Aws::Crt::New<PubAckCallbackData>(m_allocator); + + pubCallbackData->client = this->getptr(); + pubCallbackData->allocator = m_allocator; + pubCallbackData->onPublishCompletion = onPublishCmpletionCallback; + + aws_mqtt5_publish_completion_options options; + + options.completion_callback = Mqtt5Client::s_publishCompletionCallback; + options.completion_user_data = pubCallbackData; + + int result = aws_mqtt5_client_publish(m_client, &publish, &options); + if (result != AWS_OP_SUCCESS) + { + Crt::Delete(pubCallbackData, pubCallbackData->allocator); + return false; + } + return true; + } + + bool Mqtt5Client::Subscribe( + std::shared_ptr<SubscribePacket> subscribeOptions, + OnSubscribeCompletionHandler onSubscribeCompletionCallback) noexcept + { + if (subscribeOptions == nullptr) + { + return false; + } + /* Setup packet_subscribe */ + aws_mqtt5_packet_subscribe_view subscribe; + + subscribeOptions->initializeRawOptions(subscribe); + + /* Setup subscription Completion callback*/ + SubAckCallbackData *subCallbackData = Aws::Crt::New<SubAckCallbackData>(m_allocator); + + subCallbackData->client = this->getptr(); + subCallbackData->allocator = m_allocator; + subCallbackData->onSubscribeCompletion = onSubscribeCompletionCallback; + + aws_mqtt5_subscribe_completion_options options; + + options.completion_callback = Mqtt5Client::s_subscribeCompletionCallback; + options.completion_user_data = subCallbackData; + + /* Subscribe to topic */ + int result = aws_mqtt5_client_subscribe(m_client, &subscribe, &options); + if (result != AWS_OP_SUCCESS) + { + Crt::Delete(subCallbackData, subCallbackData->allocator); + return false; + } + return result == AWS_OP_SUCCESS; + } + + bool Mqtt5Client::Unsubscribe( + std::shared_ptr<UnsubscribePacket> unsubscribeOptions, + OnUnsubscribeCompletionHandler onUnsubscribeCompletionCallback) noexcept + { + if (unsubscribeOptions == nullptr) + { + return false; + } + + aws_mqtt5_packet_unsubscribe_view unsubscribe; + unsubscribeOptions->initializeRawOptions(unsubscribe); + + UnSubAckCallbackData *unSubCallbackData = Aws::Crt::New<UnSubAckCallbackData>(m_allocator); + + unSubCallbackData->client = this->getptr(); + unSubCallbackData->allocator = m_allocator; + unSubCallbackData->onUnsubscribeCompletion = onUnsubscribeCompletionCallback; + + aws_mqtt5_unsubscribe_completion_options options; + + options.completion_callback = Mqtt5Client::s_unsubscribeCompletionCallback; + options.completion_user_data = unSubCallbackData; + + int result = aws_mqtt5_client_unsubscribe(m_client, &unsubscribe, &options); + if (result != AWS_OP_SUCCESS) + { + Crt::Delete(unSubCallbackData, unSubCallbackData->allocator); + return false; + } + return result == AWS_OP_SUCCESS; + } + + const Mqtt5ClientOperationStatistics &Mqtt5Client::GetOperationStatistics() noexcept + { + aws_mqtt5_client_operation_statistics m_operationStatisticsNative = {0, 0, 0, 0}; + if (m_client != nullptr) + { + aws_mqtt5_client_get_stats(m_client, &m_operationStatisticsNative); + m_operationStatistics.incompleteOperationCount = + m_operationStatisticsNative.incomplete_operation_count; + m_operationStatistics.incompleteOperationSize = + m_operationStatisticsNative.incomplete_operation_size; + m_operationStatistics.unackedOperationCount = m_operationStatisticsNative.unacked_operation_count; + m_operationStatistics.unackedOperationSize = m_operationStatisticsNative.unacked_operation_size; + } + return m_operationStatistics; + } + + /***************************************************** + * + * Mqtt5ClientOptions + * + *****************************************************/ + + /** + * Mqtt5ClientOptions + */ + Mqtt5ClientOptions::Mqtt5ClientOptions(Crt::Allocator *allocator) noexcept + : m_bootstrap(nullptr), m_sessionBehavior(ClientSessionBehaviorType::AWS_MQTT5_CSBT_DEFAULT), + m_extendedValidationAndFlowControlOptions(AWS_MQTT5_EVAFCO_AWS_IOT_CORE_DEFAULTS), + m_offlineQueueBehavior(AWS_MQTT5_COQBT_DEFAULT), + m_reconnectionOptions({AWS_EXPONENTIAL_BACKOFF_JITTER_DEFAULT, 0, 0, 0}), m_pingTimeoutMs(0), + m_connackTimeoutMs(0), m_ackTimeoutSec(0), m_allocator(allocator) + { + m_socketOptions.SetSocketType(Io::SocketType::Stream); + AWS_ZERO_STRUCT(m_packetConnectViewStorage); + AWS_ZERO_STRUCT(m_httpProxyOptionsStorage); + } + + bool Mqtt5ClientOptions::initializeRawOptions(aws_mqtt5_client_options &raw_options) const noexcept + { + AWS_ZERO_STRUCT(raw_options); + + raw_options.host_name = ByteCursorFromString(m_hostName); + raw_options.port = m_port; + + if (m_bootstrap == nullptr) + { + raw_options.bootstrap = ApiHandle::GetOrCreateStaticDefaultClientBootstrap()->GetUnderlyingHandle(); + } + else + { + raw_options.bootstrap = m_bootstrap->GetUnderlyingHandle(); + } + raw_options.socket_options = &m_socketOptions.GetImpl(); + if (m_tlsConnectionOptions.has_value()) + { + raw_options.tls_options = m_tlsConnectionOptions.value().GetUnderlyingHandle(); + } + + if (m_proxyOptions.has_value()) + { + raw_options.http_proxy_options = &m_httpProxyOptionsStorage; + } + + raw_options.connect_options = &m_packetConnectViewStorage; + raw_options.session_behavior = m_sessionBehavior; + raw_options.extended_validation_and_flow_control_options = m_extendedValidationAndFlowControlOptions; + raw_options.offline_queue_behavior = m_offlineQueueBehavior; + raw_options.retry_jitter_mode = m_reconnectionOptions.m_reconnectMode; + raw_options.max_reconnect_delay_ms = m_reconnectionOptions.m_maxReconnectDelayMs; + raw_options.min_reconnect_delay_ms = m_reconnectionOptions.m_minReconnectDelayMs; + raw_options.min_connected_time_to_reset_reconnect_delay_ms = + m_reconnectionOptions.m_minConnectedTimeToResetReconnectDelayMs; + raw_options.ping_timeout_ms = m_pingTimeoutMs; + raw_options.connack_timeout_ms = m_connackTimeoutMs; + raw_options.ack_timeout_seconds = m_ackTimeoutSec; + + return true; + } + + Mqtt5ClientOptions::~Mqtt5ClientOptions() {} + + Mqtt5ClientOptions &Mqtt5ClientOptions::withHostName(Crt::String hostname) + { + m_hostName = std::move(hostname); + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withPort(uint16_t port) noexcept + { + m_port = port; + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withBootstrap(Io::ClientBootstrap *bootStrap) noexcept + { + m_bootstrap = bootStrap; + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withSocketOptions(Io::SocketOptions socketOptions) noexcept + { + m_socketOptions = std::move(socketOptions); + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withTlsConnectionOptions( + const Io::TlsConnectionOptions &tslOptions) noexcept + { + m_tlsConnectionOptions = tslOptions; + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withHttpProxyOptions( + const Crt::Http::HttpClientConnectionProxyOptions &proxyOptions) noexcept + { + m_proxyOptions = proxyOptions; + m_proxyOptions->InitializeRawProxyOptions(m_httpProxyOptionsStorage); + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withConnectOptions( + std::shared_ptr<ConnectPacket> packetConnect) noexcept + { + m_connectOptions = packetConnect; + m_connectOptions->initializeRawOptions(m_packetConnectViewStorage, m_allocator); + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withSessionBehavior( + ClientSessionBehaviorType sessionBehavior) noexcept + { + m_sessionBehavior = sessionBehavior; + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withClientExtendedValidationAndFlowControl( + ClientExtendedValidationAndFlowControl clientExtendedValidationAndFlowControl) noexcept + { + m_extendedValidationAndFlowControlOptions = clientExtendedValidationAndFlowControl; + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withOfflineQueueBehavior( + ClientOperationQueueBehaviorType offlineQueueBehavior) noexcept + { + m_offlineQueueBehavior = offlineQueueBehavior; + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withReconnectOptions(ReconnectOptions reconnectOptions) noexcept + { + m_reconnectionOptions = reconnectOptions; + + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withPingTimeoutMs(uint32_t pingTimeoutMs) noexcept + { + m_pingTimeoutMs = pingTimeoutMs; + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withConnackTimeoutMs(uint32_t connackTimeoutMs) noexcept + { + m_connackTimeoutMs = connackTimeoutMs; + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withAckTimeoutSeconds(uint32_t ackTimeoutSeconds) noexcept + { + m_ackTimeoutSec = ackTimeoutSeconds; + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withWebsocketHandshakeTransformCallback( + OnWebSocketHandshakeIntercept callback) noexcept + { + websocketHandshakeTransform = std::move(callback); + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withClientConnectionSuccessCallback( + OnConnectionSuccessHandler callback) noexcept + { + onConnectionSuccess = std::move(callback); + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withClientConnectionFailureCallback( + OnConnectionFailureHandler callback) noexcept + { + onConnectionFailure = std::move(callback); + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withClientDisconnectionCallback( + OnDisconnectionHandler callback) noexcept + { + onDisconnection = std::move(callback); + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withClientStoppedCallback(OnStoppedHandler callback) noexcept + { + onStopped = std::move(callback); + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withClientAttemptingConnectCallback( + OnAttemptingConnectHandler callback) noexcept + { + onAttemptingConnect = std::move(callback); + return *this; + } + + Mqtt5ClientOptions &Mqtt5ClientOptions::withPublishReceivedCallback( + OnPublishReceivedHandler callback) noexcept + { + onPublishReceived = std::move(callback); + return *this; + } + + } // namespace Mqtt5 + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Packets.cpp b/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Packets.cpp new file mode 100644 index 0000000000..a59bebb635 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/mqtt/Mqtt5Packets.cpp @@ -0,0 +1,1236 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/mqtt/Mqtt5Client.h> +#include <aws/crt/mqtt/Mqtt5Packets.h> + +namespace Aws +{ + namespace Crt + { + namespace Mqtt5 + { + template <typename T> void setPacketVector(Vector<T> &vector, const T *values, size_t length) + { + vector.clear(); + for (size_t i = 0; i < length; ++i) + { + vector.push_back(values[i]); + } + } + template <typename T> void setPacketOptional(Optional<T> &optional, const T *value) + { + if (value != nullptr) + { + optional = *value; + } + else + { + optional.reset(); + } + } + + void setPacketStringOptional( + Optional<aws_byte_cursor> &optional, + Crt::String &optionalStorage, + const aws_byte_cursor *value) + { + if (value != nullptr) + { + optionalStorage = Crt::String((const char *)value->ptr, value->len); + struct aws_byte_cursor optional_cursor; + optional_cursor.ptr = (uint8_t *)optionalStorage.c_str(); + optional_cursor.len = optionalStorage.size(); + optional = optional_cursor; + } + } + + void setPacketStringOptional(Optional<Crt::String> &optional, const aws_byte_cursor *value) + { + if (value != nullptr) + { + optional = Crt::String((const char *)value->ptr, value->len); + } + else + { + optional.reset(); + } + } + + void setPacketStringOptional(Optional<Crt::String> &optional, Crt::String &&toMove) + { + if (!toMove.empty()) + { + optional = std::move(toMove); + } + else + { + optional.reset(); + } + } + + void setPacketByteBufOptional( + Optional<aws_byte_cursor> &optional, + ByteBuf &optionalStorage, + Allocator *allocator, + const aws_byte_cursor *value) + { + aws_byte_buf_clean_up(&optionalStorage); + AWS_ZERO_STRUCT(optionalStorage); + if (value != nullptr) + { + aws_byte_buf_init_copy_from_cursor(&optionalStorage, allocator, *value); + optional = aws_byte_cursor_from_buf(&optionalStorage); + } + else + { + optional.reset(); + } + } + void setUserProperties( + Vector<UserProperty> &userProperties, + const struct aws_mqtt5_user_property *properties, + size_t propertyCount) + { + for (size_t i = 0; i < propertyCount; ++i) + { + userProperties.push_back(UserProperty( + Aws::Crt::String((const char *)properties[i].name.ptr, properties[i].name.len), + Aws::Crt::String((const char *)properties[i].value.ptr, properties[i].value.len))); + } + } + template <typename T> void setNullableFromOptional(const T *&nullable, const Optional<T> &optional) + { + if (optional.has_value()) + { + nullable = &optional.value(); + } + } + + void s_AllocateUnderlyingUserProperties( + aws_mqtt5_user_property *&dst, + const Crt::Vector<UserProperty> &userProperties, + Allocator *allocator) + { + if (dst != nullptr) + { + aws_mem_release(allocator, (void *)dst); + dst = nullptr; + } + if (userProperties.size() > 0) + { + dst = reinterpret_cast<struct aws_mqtt5_user_property *>( + aws_mem_calloc(allocator, userProperties.size(), sizeof(aws_mqtt5_user_property))); + AWS_ZERO_STRUCT(*dst); + for (size_t index = 0; index < userProperties.size(); ++index) + { + (dst + index)->name = aws_byte_cursor_from_array( + userProperties[index].getName().c_str(), userProperties[index].getName().length()); + (dst + index)->value = aws_byte_cursor_from_array( + userProperties[index].getValue().c_str(), userProperties[index].getValue().length()); + } + } + } + + void s_AllocateStringVector( + aws_array_list &dst, + const Crt::Vector<String> &stringVector, + Allocator *allocator) + { + AWS_ZERO_STRUCT(dst); + + if (aws_array_list_init_dynamic(&dst, allocator, stringVector.size(), sizeof(aws_byte_cursor)) != + AWS_OP_SUCCESS) + { + return; + } + + for (auto &topic : stringVector) + { + ByteCursor topicCursor = ByteCursorFromString(topic); + aws_array_list_push_back(&dst, reinterpret_cast<const void *>(&topicCursor)); + } + } + + void s_AllocateUnderlyingSubscription( + aws_mqtt5_subscription_view *&dst, + const Crt::Vector<Subscription> &subscriptions, + Allocator *allocator) + { + if (dst != nullptr) + { + aws_mem_release(allocator, dst); + dst = nullptr; + } + + aws_array_list subscription_list; + AWS_ZERO_STRUCT(subscription_list); + + if (aws_array_list_init_dynamic( + &subscription_list, allocator, subscriptions.size(), sizeof(aws_mqtt5_subscription_view)) != + AWS_OP_SUCCESS) + { + return; + } + + for (auto &subscription : subscriptions) + { + + aws_mqtt5_subscription_view underlying_subscription; + if (subscription.initializeRawOptions(underlying_subscription) != true) + { + goto clean_up; + } + + aws_array_list_push_back( + &subscription_list, reinterpret_cast<const void *>(&underlying_subscription)); + } + dst = static_cast<aws_mqtt5_subscription_view *>(subscription_list.data); + return; + + clean_up: + aws_array_list_clean_up(&subscription_list); + } + + ConnectPacket::ConnectPacket(Allocator *allocator) noexcept + : m_allocator(allocator), m_keepAliveIntervalSec(1200), m_userPropertiesStorage(nullptr) + { + // m_clientId.clear(); + AWS_ZERO_STRUCT(m_usernameCursor); + AWS_ZERO_STRUCT(m_passowrdStorage); + AWS_ZERO_STRUCT(m_willStorage); + } + + ConnectPacket &ConnectPacket::withKeepAliveIntervalSec(uint16_t second) noexcept + { + m_keepAliveIntervalSec = second; + return *this; + } + + ConnectPacket &ConnectPacket::withClientId(Crt::String client_id) noexcept + { + m_clientId = std::move(client_id); + return *this; + } + + ConnectPacket &ConnectPacket::withUserName(Crt::String username) noexcept + { + m_username = std::move(username); + m_usernameCursor = ByteCursorFromString(m_username.value()); + return *this; + } + + ConnectPacket &ConnectPacket::withPassword(Crt::ByteCursor password) noexcept + { + setPacketByteBufOptional(m_password, m_passowrdStorage, m_allocator, &password); + return *this; + } + + ConnectPacket &ConnectPacket::withSessionExpiryIntervalSec(uint32_t sessionExpiryIntervalSec) noexcept + { + m_sessionExpiryIntervalSec = sessionExpiryIntervalSec; + return *this; + } + + ConnectPacket &ConnectPacket::withRequestResponseInformation(bool requestResponseInformation) noexcept + { + m_requestResponseInformation = requestResponseInformation; + return *this; + } + + ConnectPacket &ConnectPacket::withRequestProblemInformation(bool requestProblemInformation) noexcept + { + m_requestProblemInformation = requestProblemInformation; + return *this; + } + + ConnectPacket &ConnectPacket::withReceiveMaximum(uint16_t receiveMaximum) noexcept + { + m_receiveMaximum = receiveMaximum; + return *this; + } + + ConnectPacket &ConnectPacket::withMaximumPacketSizeBytes(uint32_t maximumPacketSizeBytes) noexcept + { + m_maximumPacketSizeBytes = maximumPacketSizeBytes; + return *this; + } + + ConnectPacket &ConnectPacket::withWillDelayIntervalSec(uint32_t willDelayIntervalSec) noexcept + { + m_willDelayIntervalSeconds = willDelayIntervalSec; + return *this; + } + + ConnectPacket &ConnectPacket::withWill(std::shared_ptr<PublishPacket> will) noexcept + { + m_will = will; + m_will.value()->initializeRawOptions(m_willStorage); + return *this; + } + + ConnectPacket &ConnectPacket::withUserProperties(const Vector<UserProperty> &userProperties) noexcept + { + m_userProperties = userProperties; + return *this; + } + + ConnectPacket &ConnectPacket::withUserProperties(Vector<UserProperty> &&userProperties) noexcept + { + m_userProperties = userProperties; + return *this; + } + + ConnectPacket &ConnectPacket::withUserProperty(UserProperty &&property) noexcept + { + m_userProperties.push_back(std::move(property)); + return *this; + } + + bool ConnectPacket::initializeRawOptions( + aws_mqtt5_packet_connect_view &raw_options, + Allocator * /*allocator*/) noexcept + { + AWS_ZERO_STRUCT(raw_options); + + raw_options.keep_alive_interval_seconds = m_keepAliveIntervalSec; + raw_options.client_id = ByteCursorFromString(m_clientId); + + if (m_username.has_value()) + { + raw_options.username = &m_usernameCursor; + } + + if (m_password.has_value()) + { + raw_options.password = &m_password.value(); + } + + if (m_sessionExpiryIntervalSec.has_value()) + { + raw_options.session_expiry_interval_seconds = &m_sessionExpiryIntervalSec.value(); + } + + if (m_requestProblemInformation.has_value()) + { + m_requestResponseInformationStorage = m_requestResponseInformation.value() ? 1 : 0; + raw_options.request_response_information = &m_requestResponseInformationStorage; + } + + if (m_requestProblemInformation.has_value()) + { + m_requestProblemInformationStorage = m_requestProblemInformation.value() ? 1 : 0; + raw_options.request_problem_information = &m_requestProblemInformationStorage; + } + + if (m_receiveMaximum.has_value()) + { + raw_options.receive_maximum = &m_receiveMaximum.value(); + } + + if (m_maximumPacketSizeBytes.has_value()) + { + raw_options.maximum_packet_size_bytes = &m_maximumPacketSizeBytes.value(); + } + + if (m_willDelayIntervalSeconds.has_value()) + { + raw_options.will_delay_interval_seconds = &m_willDelayIntervalSeconds.value(); + } + + if (m_will.has_value()) + { + raw_options.will = &m_willStorage; + } + + s_AllocateUnderlyingUserProperties(m_userPropertiesStorage, m_userProperties, m_allocator); + raw_options.user_properties = m_userPropertiesStorage; + raw_options.user_property_count = m_userProperties.size(); + + return true; + } + + ConnectPacket::~ConnectPacket() + { + if (m_userPropertiesStorage != nullptr) + { + aws_mem_release(m_allocator, m_userPropertiesStorage); + m_userProperties.clear(); + } + aws_byte_buf_clean_up(&m_passowrdStorage); + } + + uint16_t ConnectPacket::getKeepAliveIntervalSec() const noexcept { return m_keepAliveIntervalSec; } + + const Crt::String &ConnectPacket::getClientId() const noexcept { return m_clientId; } + + const Crt::Optional<Crt::String> &ConnectPacket::getUsername() const noexcept { return m_username; } + + const Crt::Optional<Crt::ByteCursor> &ConnectPacket::getPassword() const noexcept { return m_password; } + + const Crt::Optional<uint32_t> &ConnectPacket::getSessionExpiryIntervalSec() const noexcept + { + return m_sessionExpiryIntervalSec; + } + + const Crt::Optional<bool> &ConnectPacket::getRequestResponseInformation() const noexcept + { + return m_requestResponseInformation; + } + + const Crt::Optional<bool> &ConnectPacket::getRequestProblemInformation() const noexcept + { + return m_requestProblemInformation; + } + + const Crt::Optional<uint16_t> &ConnectPacket::getReceiveMaximum() const noexcept + { + return m_receiveMaximum; + } + + const Crt::Optional<uint32_t> &ConnectPacket::getMaximumPacketSizeBytes() const noexcept + { + return m_maximumPacketSizeBytes; + } + + const Crt::Optional<uint32_t> &ConnectPacket::getWillDelayIntervalSec() const noexcept + { + return m_willDelayIntervalSeconds; + } + + const Crt::Optional<std::shared_ptr<PublishPacket>> &ConnectPacket::getWill() const noexcept + { + return m_will; + } + + const Crt::Vector<UserProperty> &ConnectPacket::getUserProperties() const noexcept + { + return m_userProperties; + } + + UserProperty::UserProperty(Crt::String name, Crt::String value) noexcept + : m_name(std::move(name)), m_value(std::move(value)) + { + } + + UserProperty::~UserProperty() noexcept {} + + UserProperty::UserProperty(const UserProperty &toCopy) noexcept + : m_name(toCopy.getName()), m_value(toCopy.getValue()) + { + } + + UserProperty::UserProperty(UserProperty &&toMove) noexcept + : m_name(std::move(toMove.m_name)), m_value(std::move(toMove.m_value)) + { + } + + UserProperty &UserProperty::operator=(const UserProperty &toCopy) noexcept + { + if (&toCopy != this) + { + m_name = toCopy.getName(); + m_value = toCopy.getValue(); + } + return *this; + } + + UserProperty &UserProperty::operator=(UserProperty &&toMove) noexcept + { + if (&toMove != this) + { + m_name = std::move(toMove.m_name); + m_value = std::move(toMove.m_value); + } + return *this; + } + + PublishPacket::PublishPacket(const aws_mqtt5_packet_publish_view &packet, Allocator *allocator) noexcept + : m_allocator(allocator), m_qos(packet.qos), m_retain(packet.retain), + m_topicName((const char *)packet.topic.ptr, packet.topic.len), m_userPropertiesStorage(nullptr) + { + AWS_ZERO_STRUCT(m_payloadStorage); + AWS_ZERO_STRUCT(m_contentTypeStorage); + AWS_ZERO_STRUCT(m_correlationDataStorage); + AWS_ZERO_STRUCT(m_payload); + + withPayload(packet.payload); + + setPacketOptional(m_payloadFormatIndicator, packet.payload_format); + setPacketOptional(m_messageExpiryIntervalSec, packet.message_expiry_interval_seconds); + setPacketStringOptional(m_responseTopic, m_responseTopicString, packet.response_topic); + setPacketByteBufOptional( + m_correlationData, m_correlationDataStorage, allocator, packet.correlation_data); + setPacketByteBufOptional(m_contentType, m_contentTypeStorage, allocator, packet.content_type); + setPacketVector( + m_subscriptionIdentifiers, packet.subscription_identifiers, packet.subscription_identifier_count); + setUserProperties(m_userProperties, packet.user_properties, packet.user_property_count); + } + + /* Default constructor */ + PublishPacket::PublishPacket(Allocator *allocator) noexcept + : m_allocator(allocator), m_qos(QOS::AWS_MQTT5_QOS_AT_MOST_ONCE), m_retain(false), m_topicName(""), + m_userPropertiesStorage(nullptr) + { + AWS_ZERO_STRUCT(m_payloadStorage); + AWS_ZERO_STRUCT(m_contentTypeStorage); + AWS_ZERO_STRUCT(m_correlationDataStorage); + AWS_ZERO_STRUCT(m_payload); + } + + PublishPacket::PublishPacket( + Crt::String topic, + ByteCursor payload, + Mqtt5::QOS qos, + Allocator *allocator) noexcept + : m_allocator(allocator), m_qos(qos), m_retain(false), m_topicName(std::move(topic)), + m_userPropertiesStorage(nullptr) + { + AWS_ZERO_STRUCT(m_payloadStorage); + AWS_ZERO_STRUCT(m_contentTypeStorage); + AWS_ZERO_STRUCT(m_correlationDataStorage); + AWS_ZERO_STRUCT(m_payload); + + // Setup message payload, sync with PublishPacket::withPayload + aws_byte_buf_clean_up(&m_payloadStorage); + aws_byte_buf_init_copy_from_cursor(&m_payloadStorage, m_allocator, payload); + m_payload = aws_byte_cursor_from_buf(&m_payloadStorage); + } + + PublishPacket &PublishPacket::withPayload(ByteCursor payload) noexcept + { + aws_byte_buf_clean_up(&m_payloadStorage); + aws_byte_buf_init_copy_from_cursor(&m_payloadStorage, m_allocator, payload); + m_payload = aws_byte_cursor_from_buf(&m_payloadStorage); + return *this; + } + + PublishPacket &PublishPacket::withQOS(Mqtt5::QOS qos) noexcept + { + m_qos = qos; + return *this; + } + + PublishPacket &PublishPacket::withRetain(bool retain) noexcept + { + m_retain = retain; + return *this; + } + + PublishPacket &PublishPacket::withTopic(Crt::String topic) noexcept + { + m_topicName = std::move(topic); + return *this; + } + + PublishPacket &PublishPacket::withPayloadFormatIndicator(PayloadFormatIndicator format) noexcept + { + m_payloadFormatIndicator = format; + return *this; + } + + PublishPacket &PublishPacket::withMessageExpiryIntervalSec(uint32_t second) noexcept + { + m_messageExpiryIntervalSec = second; + return *this; + } + + PublishPacket &PublishPacket::withResponseTopic(ByteCursor responseTopic) noexcept + { + setPacketStringOptional(m_responseTopic, m_responseTopicString, &responseTopic); + return *this; + } + + PublishPacket &PublishPacket::withCorrelationData(ByteCursor correlationData) noexcept + { + setPacketByteBufOptional(m_correlationData, m_correlationDataStorage, m_allocator, &correlationData); + return *this; + } + + PublishPacket &PublishPacket::withUserProperties(const Vector<UserProperty> &userProperties) noexcept + { + m_userProperties = userProperties; + return *this; + } + + PublishPacket &PublishPacket::withUserProperties(Vector<UserProperty> &&userProperties) noexcept + { + m_userProperties = userProperties; + return *this; + } + + PublishPacket &PublishPacket::withUserProperty(UserProperty &&property) noexcept + { + m_userProperties.push_back(std::move(property)); + return *this; + } + + bool PublishPacket::initializeRawOptions(aws_mqtt5_packet_publish_view &raw_options) noexcept + { + AWS_ZERO_STRUCT(raw_options); + raw_options.payload = m_payload; + raw_options.qos = m_qos; + raw_options.retain = m_retain; + raw_options.topic = ByteCursorFromString(m_topicName); + + if (m_payloadFormatIndicator.has_value()) + { + raw_options.payload_format = + (aws_mqtt5_payload_format_indicator *)&m_payloadFormatIndicator.value(); + } + if (m_messageExpiryIntervalSec.has_value()) + { + raw_options.message_expiry_interval_seconds = &m_messageExpiryIntervalSec.value(); + } + if (m_responseTopic.has_value()) + { + raw_options.response_topic = &m_responseTopic.value(); + } + if (m_correlationData.has_value()) + { + raw_options.correlation_data = &m_correlationData.value(); + } + + s_AllocateUnderlyingUserProperties(m_userPropertiesStorage, m_userProperties, m_allocator); + raw_options.user_properties = m_userPropertiesStorage; + raw_options.user_property_count = m_userProperties.size(); + + return true; + } + + const ByteCursor &PublishPacket::getPayload() const noexcept { return m_payload; } + + Mqtt5::QOS PublishPacket::getQOS() const noexcept { return m_qos; } + + bool PublishPacket::getRetain() const noexcept { return m_retain; } + + const Crt::String &PublishPacket::getTopic() const noexcept { return m_topicName; } + + const Crt::Optional<PayloadFormatIndicator> &PublishPacket::getPayloadFormatIndicator() const noexcept + { + return m_payloadFormatIndicator; + } + + const Crt::Optional<uint32_t> &PublishPacket::getMessageExpiryIntervalSec() const noexcept + { + return m_messageExpiryIntervalSec; + } + + const Crt::Optional<ByteCursor> &PublishPacket::getResponseTopic() const noexcept + { + return m_responseTopic; + } + + const Crt::Optional<ByteCursor> &PublishPacket::getCorrelationData() const noexcept + { + return m_correlationData; + } + + const Crt::Vector<uint32_t> &PublishPacket::getSubscriptionIdentifiers() const noexcept + { + return m_subscriptionIdentifiers; + } + + const Crt::Optional<ByteCursor> &PublishPacket::getContentType() const noexcept { return m_contentType; } + + const Crt::Vector<UserProperty> &PublishPacket::getUserProperties() const noexcept + { + return m_userProperties; + } + + PublishPacket::~PublishPacket() + { + aws_byte_buf_clean_up(&m_payloadStorage); + aws_byte_buf_clean_up(&m_correlationDataStorage); + aws_byte_buf_clean_up(&m_contentTypeStorage); + + if (m_userProperties.size() > 0) + { + aws_mem_release(m_allocator, m_userPropertiesStorage); + m_userProperties.clear(); + } + } + + DisconnectPacket::DisconnectPacket(Allocator *allocator) noexcept + : m_allocator(allocator), m_reasonCode(AWS_MQTT5_DRC_NORMAL_DISCONNECTION), + m_userPropertiesStorage(nullptr) + { + } + + bool DisconnectPacket::initializeRawOptions(aws_mqtt5_packet_disconnect_view &raw_options) noexcept + { + AWS_ZERO_STRUCT(raw_options); + + raw_options.reason_code = m_reasonCode; + + if (m_sessionExpiryIntervalSec.has_value()) + { + raw_options.session_expiry_interval_seconds = &m_sessionExpiryIntervalSec.value(); + } + + if (m_reasonString.has_value()) + { + m_reasonStringCursor = ByteCursorFromString(m_reasonString.value()); + raw_options.reason_string = &m_reasonStringCursor; + } + + if (m_serverReference.has_value()) + { + m_serverReferenceCursor = ByteCursorFromString(m_serverReference.value()); + raw_options.server_reference = &m_serverReferenceCursor; + } + + s_AllocateUnderlyingUserProperties(m_userPropertiesStorage, m_userProperties, m_allocator); + raw_options.user_properties = m_userPropertiesStorage; + raw_options.user_property_count = m_userProperties.size(); + + return true; + } + + DisconnectPacket &DisconnectPacket::withReasonCode(const DisconnectReasonCode code) noexcept + { + m_reasonCode = code; + return *this; + } + + DisconnectPacket &DisconnectPacket::withSessionExpiryIntervalSec(const uint32_t second) noexcept + { + m_sessionExpiryIntervalSec = second; + return *this; + } + + DisconnectPacket &DisconnectPacket::withReasonString(Crt::String reason) noexcept + { + m_reasonString = std::move(reason); + return *this; + } + + DisconnectPacket &DisconnectPacket::withServerReference(Crt::String server_reference) noexcept + { + m_serverReference = std::move(server_reference); + return *this; + } + + DisconnectPacket &DisconnectPacket::withUserProperties(const Vector<UserProperty> &userProperties) noexcept + { + m_userProperties = userProperties; + return *this; + } + + DisconnectPacket &DisconnectPacket::withUserProperties(Vector<UserProperty> &&userProperties) noexcept + { + m_userProperties = userProperties; + return *this; + } + + DisconnectPacket &DisconnectPacket::withUserProperty(UserProperty &&property) noexcept + { + m_userProperties.push_back(std::move(property)); + return *this; + } + + DisconnectReasonCode DisconnectPacket::getReasonCode() const noexcept { return m_reasonCode; } + + const Crt::Optional<uint32_t> &DisconnectPacket::getSessionExpiryIntervalSec() const noexcept + { + return m_sessionExpiryIntervalSec; + } + + const Crt::Optional<Crt::String> &DisconnectPacket::getReasonString() const noexcept + { + return m_reasonString; + } + + const Crt::Optional<Crt::String> &DisconnectPacket::getServerReference() const noexcept + { + return m_serverReference; + } + + const Crt::Vector<UserProperty> &DisconnectPacket::getUserProperties() const noexcept + { + return m_userProperties; + } + + DisconnectPacket::DisconnectPacket( + const aws_mqtt5_packet_disconnect_view &packet, + Allocator *allocator) noexcept + : m_allocator(allocator), m_userPropertiesStorage(nullptr) + { + m_reasonCode = packet.reason_code; + + setPacketOptional(m_sessionExpiryIntervalSec, packet.session_expiry_interval_seconds); + setPacketStringOptional(m_reasonString, packet.reason_string); + setPacketStringOptional(m_serverReference, packet.server_reference); + setUserProperties(m_userProperties, packet.user_properties, packet.user_property_count); + } + + DisconnectPacket::~DisconnectPacket() + { + if (m_userPropertiesStorage != nullptr) + { + aws_mem_release(m_allocator, m_userPropertiesStorage); + } + } + + PubAckPacket::PubAckPacket(const aws_mqtt5_packet_puback_view &packet, Allocator * /*allocator*/) noexcept + { + m_reasonCode = packet.reason_code; + setPacketStringOptional(m_reasonString, packet.reason_string); + setUserProperties(m_userProperties, packet.user_properties, packet.user_property_count); + } + + PubAckReasonCode PubAckPacket::getReasonCode() const noexcept { return m_reasonCode; } + + const Crt::Optional<Crt::String> &PubAckPacket::getReasonString() const noexcept { return m_reasonString; } + + const Crt::Vector<UserProperty> &PubAckPacket::getUserProperties() const noexcept + { + return m_userProperties; + } + + ConnAckPacket::ConnAckPacket( + const aws_mqtt5_packet_connack_view &packet, + Allocator * /*allocator*/) noexcept + { + m_sessionPresent = packet.session_present; + m_reasonCode = packet.reason_code; + setPacketOptional(m_sessionExpiryInterval, packet.session_expiry_interval); + setPacketOptional(m_receiveMaximum, packet.receive_maximum); + setPacketOptional(m_maximumQOS, packet.maximum_qos); + setPacketOptional(m_retainAvailable, packet.retain_available); + setPacketOptional(m_maximumPacketSize, packet.maximum_packet_size); + setPacketStringOptional(m_assignedClientIdentifier, packet.assigned_client_identifier); + setPacketOptional(m_topicAliasMaximum, packet.topic_alias_maximum); + setPacketStringOptional(m_reasonString, packet.reason_string); + setUserProperties(m_userProperties, packet.user_properties, packet.user_property_count); + setPacketOptional(m_wildcardSubscriptionsAvaliable, packet.wildcard_subscriptions_available); + setPacketOptional(m_subscriptionIdentifiersAvaliable, packet.subscription_identifiers_available); + setPacketOptional(m_sharedSubscriptionsAvaliable, packet.shared_subscriptions_available); + setPacketOptional(m_serverKeepAlive, packet.server_keep_alive); + setPacketStringOptional(m_responseInformation, packet.response_information); + setPacketStringOptional(m_serverReference, packet.server_reference); + } + + bool ConnAckPacket::getSessionPresent() const noexcept { return m_sessionPresent; } + + ConnectReasonCode ConnAckPacket::getReasonCode() const noexcept { return m_reasonCode; } + + const Crt::Optional<uint32_t> &ConnAckPacket::getSessionExpiryInterval() const noexcept + { + return m_sessionExpiryInterval; + } + + const Crt::Optional<uint16_t> &ConnAckPacket::getReceiveMaximum() const noexcept + { + return m_receiveMaximum; + } + + const Crt::Optional<QOS> &ConnAckPacket::getMaximumQOS() const noexcept { return m_maximumQOS; } + + const Crt::Optional<bool> &ConnAckPacket::getRetainAvailable() const noexcept { return m_retainAvailable; } + + const Crt::Optional<uint32_t> &ConnAckPacket::getMaximumPacketSize() const noexcept + { + return m_maximumPacketSize; + } + + const Crt::Optional<String> &ConnAckPacket::getAssignedClientIdentifier() const noexcept + { + return m_assignedClientIdentifier; + } + + const Crt::Optional<uint16_t> ConnAckPacket::getTopicAliasMaximum() const noexcept + { + return m_topicAliasMaximum; + } + + const Crt::Optional<String> &ConnAckPacket::getReasonString() const noexcept { return m_reasonString; } + + const Vector<UserProperty> &ConnAckPacket::getUserProperty() const noexcept { return m_userProperties; } + + const Crt::Optional<bool> &ConnAckPacket::getWildcardSubscriptionsAvaliable() const noexcept + { + return m_wildcardSubscriptionsAvaliable; + } + + const Crt::Optional<bool> &ConnAckPacket::getSubscriptionIdentifiersAvaliable() const noexcept + { + return m_subscriptionIdentifiersAvaliable; + } + + const Crt::Optional<bool> &ConnAckPacket::getSharedSubscriptionsAvaliable() const noexcept + { + return m_sharedSubscriptionsAvaliable; + } + + const Crt::Optional<uint16_t> &ConnAckPacket::getServerKeepAlive() const noexcept + { + return m_serverKeepAlive; + } + + const Crt::Optional<String> &ConnAckPacket::getResponseInformation() const noexcept + { + return m_responseInformation; + } + + const Crt::Optional<String> &ConnAckPacket::getServerReference() const noexcept + { + return m_serverReference; + } + + Subscription::Subscription(Allocator *allocator) + : m_allocator(allocator), m_topicFilter(""), m_qos(QOS::AWS_MQTT5_QOS_AT_MOST_ONCE), m_noLocal(false), + m_retain(false), m_retainHnadlingType(AWS_MQTT5_RHT_SEND_ON_SUBSCRIBE) + + { + } + + Subscription::Subscription(Crt::String topicFilter, Mqtt5::QOS qos, Allocator *allocator) + : m_allocator(allocator), m_topicFilter(std::move(topicFilter)), m_qos(qos), m_noLocal(false), + m_retain(false), m_retainHnadlingType(AWS_MQTT5_RHT_SEND_ON_SUBSCRIBE) + { + } + + Subscription &Subscription::withTopicFilter(Crt::String topicFilter) noexcept + { + m_topicFilter = std::move(topicFilter); + return *this; + } + + Subscription &Subscription::withQOS(Mqtt5::QOS qos) noexcept + { + m_qos = qos; + return *this; + } + Subscription &Subscription::withNoLocal(bool noLocal) noexcept + { + m_noLocal = noLocal; + return *this; + } + Subscription &Subscription::withRetain(bool retain) noexcept + { + m_retain = retain; + return *this; + } + Subscription &Subscription::withRetainHandlingType(RetainHandlingType retainHandlingType) noexcept + { + m_retainHnadlingType = retainHandlingType; + return *this; + } + + bool Subscription::initializeRawOptions(aws_mqtt5_subscription_view &raw_options) const noexcept + { + AWS_ZERO_STRUCT(raw_options); + raw_options.topic_filter = ByteCursorFromString(m_topicFilter); + raw_options.no_local = m_noLocal; + raw_options.qos = m_qos; + raw_options.retain_as_published = m_retain; + raw_options.retain_handling_type = m_retainHnadlingType; + return true; + } + + Subscription::Subscription(const Subscription &toCopy) noexcept + : m_allocator(toCopy.m_allocator), m_topicFilter(toCopy.m_topicFilter), m_qos(toCopy.m_qos), + m_noLocal(toCopy.m_noLocal), m_retain(toCopy.m_retain), + m_retainHnadlingType(toCopy.m_retainHnadlingType) + { + } + + Subscription::Subscription(Subscription &&toMove) noexcept + : m_allocator(toMove.m_allocator), m_topicFilter(std::move(toMove.m_topicFilter)), m_qos(toMove.m_qos), + m_noLocal(toMove.m_noLocal), m_retain(toMove.m_retain), + m_retainHnadlingType(toMove.m_retainHnadlingType) + { + } + + Subscription &Subscription::operator=(const Subscription &toCopy) noexcept + { + if (&toCopy != this) + { + m_allocator = toCopy.m_allocator; + m_qos = toCopy.m_qos; + m_topicFilter = toCopy.m_topicFilter; + m_noLocal = toCopy.m_noLocal; + m_retain = toCopy.m_retain; + m_retainHnadlingType = toCopy.m_retainHnadlingType; + } + return *this; + } + + Subscription &Subscription::operator=(Subscription &&toMove) noexcept + { + if (&toMove != this) + { + m_allocator = toMove.m_allocator; + m_qos = toMove.m_qos; + m_topicFilter = std::move(toMove.m_topicFilter); + m_noLocal = toMove.m_noLocal; + m_retain = toMove.m_retain; + m_retainHnadlingType = toMove.m_retainHnadlingType; + } + return *this; + } + + SubscribePacket::SubscribePacket(Allocator *allocator) noexcept + : m_allocator(allocator), m_subscriptionViewStorage(nullptr), m_userPropertiesStorage(nullptr) + { + } + + SubscribePacket &SubscribePacket::withUserProperties(const Vector<UserProperty> &userProperties) noexcept + { + m_userProperties = userProperties; + return *this; + } + + SubscribePacket &SubscribePacket::withUserProperties(Vector<UserProperty> &&userProperties) noexcept + { + m_userProperties = userProperties; + return *this; + } + + SubscribePacket &SubscribePacket::withUserProperty(UserProperty &&property) noexcept + { + m_userProperties.push_back(std::move(property)); + return *this; + } + + SubscribePacket &SubscribePacket::withSubscriptionIdentifier(uint32_t identifier) noexcept + { + m_subscriptionIdentifier = identifier; + return *this; + } + + SubscribePacket &SubscribePacket::withSubscriptions(const Crt::Vector<Subscription> &subscriptions) noexcept + { + m_subscriptions = subscriptions; + + return *this; + } + + SubscribePacket &SubscribePacket::withSubscriptions(Vector<Subscription> &&subscriptions) noexcept + { + m_subscriptions = subscriptions; + return *this; + } + + SubscribePacket &SubscribePacket::withSubscription(Subscription &&subscription) noexcept + { + m_subscriptions.push_back(subscription); + return *this; + } + + bool SubscribePacket::initializeRawOptions(aws_mqtt5_packet_subscribe_view &raw_options) noexcept + { + AWS_ZERO_STRUCT(raw_options); + + s_AllocateUnderlyingSubscription(m_subscriptionViewStorage, m_subscriptions, m_allocator); + raw_options.subscription_count = m_subscriptions.size(); + raw_options.subscriptions = m_subscriptionViewStorage; + + s_AllocateUnderlyingUserProperties(m_userPropertiesStorage, m_userProperties, m_allocator); + raw_options.user_properties = m_userPropertiesStorage; + raw_options.user_property_count = m_userProperties.size(); + + return true; + } + + SubscribePacket::~SubscribePacket() + { + if (m_userPropertiesStorage != nullptr) + { + aws_mem_release(m_allocator, m_userPropertiesStorage); + m_userPropertiesStorage = nullptr; + } + + if (m_subscriptionViewStorage != nullptr) + { + aws_mem_release(m_allocator, m_subscriptionViewStorage); + m_subscriptionViewStorage = nullptr; + } + } + + SubAckPacket::SubAckPacket(const aws_mqtt5_packet_suback_view &packet, Allocator * /*allocator*/) noexcept + { + setPacketStringOptional(m_reasonString, packet.reason_string); + setUserProperties(m_userProperties, packet.user_properties, packet.user_property_count); + for (size_t i = 0; i < packet.reason_code_count; i++) + { + m_reasonCodes.push_back(*(packet.reason_codes + i)); + } + } + + const Crt::Optional<Crt::String> &SubAckPacket::getReasonString() const noexcept { return m_reasonString; } + + const Crt::Vector<UserProperty> &SubAckPacket::getUserProperties() const noexcept + { + return m_userProperties; + } + + const Crt::Vector<SubAckReasonCode> &SubAckPacket::getReasonCodes() const noexcept { return m_reasonCodes; } + + UnsubscribePacket::UnsubscribePacket(Allocator *allocator) noexcept + : m_allocator(allocator), m_userPropertiesStorage(nullptr) + { + AWS_ZERO_STRUCT(m_topicFiltersList); + } + + UnsubscribePacket &UnsubscribePacket::withTopicFilter(Crt::String topicFilter) noexcept + { + m_topicFilters.push_back(std::move(topicFilter)); + return *this; + } + + UnsubscribePacket &UnsubscribePacket::withTopicFilters(Crt::Vector<String> topicFilters) noexcept + { + m_topicFilters = std::move(topicFilters); + + return *this; + } + + UnsubscribePacket &UnsubscribePacket::withUserProperties( + const Vector<UserProperty> &userProperties) noexcept + { + m_userProperties = userProperties; + return *this; + } + + UnsubscribePacket &UnsubscribePacket::withUserProperties(Vector<UserProperty> &&userProperties) noexcept + { + m_userProperties = userProperties; + return *this; + } + + UnsubscribePacket &UnsubscribePacket::withUserProperty(UserProperty &&property) noexcept + { + m_userProperties.push_back(std::move(property)); + return *this; + } + + bool UnsubscribePacket::initializeRawOptions(aws_mqtt5_packet_unsubscribe_view &raw_options) noexcept + { + AWS_ZERO_STRUCT(raw_options); + + s_AllocateStringVector(m_topicFiltersList, m_topicFilters, m_allocator); + raw_options.topic_filters = static_cast<aws_byte_cursor *>(m_topicFiltersList.data); + raw_options.topic_filter_count = m_topicFilters.size(); + + s_AllocateUnderlyingUserProperties(m_userPropertiesStorage, m_userProperties, m_allocator); + raw_options.user_properties = m_userPropertiesStorage; + raw_options.user_property_count = m_userProperties.size(); + + return true; + } + + UnsubscribePacket::~UnsubscribePacket() + { + aws_array_list_clean_up(&m_topicFiltersList); + AWS_ZERO_STRUCT(m_topicFiltersList); + + if (m_userPropertiesStorage != nullptr) + { + aws_mem_release(m_allocator, m_userPropertiesStorage); + m_userPropertiesStorage = nullptr; + } + } + + UnSubAckPacket::UnSubAckPacket(const aws_mqtt5_packet_unsuback_view &packet, Allocator *allocator) noexcept + { + (void)allocator; + + setPacketStringOptional(m_reasonString, packet.reason_string); + + for (size_t i = 0; i < packet.reason_code_count; i++) + { + m_reasonCodes.push_back(*(packet.reason_codes + i)); + } + setUserProperties(m_userProperties, packet.user_properties, packet.user_property_count); + } + + const Crt::Optional<Crt::String> &UnSubAckPacket::getReasonString() const noexcept + { + return m_reasonString; + } + + const Crt::Vector<UserProperty> &UnSubAckPacket::getUserProperties() const noexcept + { + return m_userProperties; + } + + const Crt::Vector<UnSubAckReasonCode> &UnSubAckPacket::getReasonCodes() const noexcept + { + return m_reasonCodes; + } + + NegotiatedSettings::NegotiatedSettings( + const aws_mqtt5_negotiated_settings &negotiated_settings, + Allocator *allocator) noexcept + { + (void)allocator; + + m_maximumQOS = negotiated_settings.maximum_qos; + m_sessionExpiryIntervalSec = negotiated_settings.session_expiry_interval; + m_receiveMaximumFromServer = negotiated_settings.receive_maximum_from_server; + + m_maximumPacketSizeBytes = negotiated_settings.maximum_packet_size_to_server; + m_serverKeepAliveSec = negotiated_settings.server_keep_alive; + + m_retainAvailable = negotiated_settings.retain_available; + m_wildcardSubscriptionsAvaliable = negotiated_settings.wildcard_subscriptions_available; + m_subscriptionIdentifiersAvaliable = negotiated_settings.subscription_identifiers_available; + m_sharedSubscriptionsAvaliable = negotiated_settings.shared_subscriptions_available; + m_rejoinedSession = negotiated_settings.rejoined_session; + + m_clientId = Crt::String( + (const char *)negotiated_settings.client_id_storage.buffer, + negotiated_settings.client_id_storage.len); + } + + Mqtt5::QOS NegotiatedSettings::getMaximumQOS() const noexcept { return m_maximumQOS; } + + uint32_t NegotiatedSettings::getSessionExpiryIntervalSec() const noexcept + { + return m_sessionExpiryIntervalSec; + } + + uint16_t NegotiatedSettings::getReceiveMaximumFromServer() const noexcept + { + return m_receiveMaximumFromServer; + } + + uint32_t NegotiatedSettings::getMaximumPacketSizeBytes() const noexcept { return m_maximumPacketSizeBytes; } + + uint16_t NegotiatedSettings::getServerKeepAlive() const noexcept { return m_serverKeepAliveSec; } + + bool NegotiatedSettings::getRetainAvailable() const noexcept { return m_retainAvailable; } + + bool NegotiatedSettings::getWildcardSubscriptionsAvaliable() const noexcept + { + return m_wildcardSubscriptionsAvaliable; + } + + bool NegotiatedSettings::getSubscriptionIdentifiersAvaliable() const noexcept + { + return m_subscriptionIdentifiersAvaliable; + } + + bool NegotiatedSettings::getSharedSubscriptionsAvaliable() const noexcept + { + return m_sharedSubscriptionsAvaliable; + } + + bool NegotiatedSettings::getRejoinedSession() const noexcept { return m_rejoinedSession; } + + const Crt::String &NegotiatedSettings::getClientId() const noexcept { return m_clientId; } + + PublishResult::PublishResult() : m_ack(nullptr), m_errorCode(0) {} + + PublishResult::PublishResult(std::shared_ptr<PubAckPacket> puback) : m_errorCode(0) { m_ack = puback; } + + PublishResult::PublishResult(int error) : m_ack(nullptr), m_errorCode(error) {} + + PublishResult::~PublishResult() noexcept { m_ack.reset(); } + + } // namespace Mqtt5 + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/source/mqtt/MqttClient.cpp b/contrib/restricted/aws/aws-crt-cpp/source/mqtt/MqttClient.cpp new file mode 100644 index 0000000000..e36c591237 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/source/mqtt/MqttClient.cpp @@ -0,0 +1,816 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ +#include <aws/crt/mqtt/MqttClient.h> + +#include <aws/crt/Api.h> +#include <aws/crt/StlAllocator.h> +#include <aws/crt/http/HttpProxyStrategy.h> +#include <aws/crt/http/HttpRequestResponse.h> +#include <aws/crt/io/Bootstrap.h> + +#include <utility> + +#define AWS_MQTT_MAX_TOPIC_LENGTH 65535 + +namespace Aws +{ + namespace Crt + { + namespace Mqtt + { + void MqttConnection::s_onConnectionInterrupted(aws_mqtt_client_connection *, int errorCode, void *userData) + { + auto connWrapper = reinterpret_cast<MqttConnection *>(userData); + if (connWrapper->OnConnectionInterrupted) + { + connWrapper->OnConnectionInterrupted(*connWrapper, errorCode); + } + } + + void MqttConnection::s_onConnectionResumed( + aws_mqtt_client_connection *, + ReturnCode returnCode, + bool sessionPresent, + void *userData) + { + auto connWrapper = reinterpret_cast<MqttConnection *>(userData); + if (connWrapper->OnConnectionResumed) + { + connWrapper->OnConnectionResumed(*connWrapper, returnCode, sessionPresent); + } + } + + void MqttConnection::s_onConnectionCompleted( + aws_mqtt_client_connection *, + int errorCode, + enum aws_mqtt_connect_return_code returnCode, + bool sessionPresent, + void *userData) + { + auto connWrapper = reinterpret_cast<MqttConnection *>(userData); + if (connWrapper->OnConnectionCompleted) + { + connWrapper->OnConnectionCompleted(*connWrapper, errorCode, returnCode, sessionPresent); + } + } + + void MqttConnection::s_onDisconnect(aws_mqtt_client_connection *, void *userData) + { + auto connWrapper = reinterpret_cast<MqttConnection *>(userData); + if (connWrapper->OnDisconnect) + { + connWrapper->OnDisconnect(*connWrapper); + } + } + + struct PubCallbackData + { + PubCallbackData() : connection(nullptr), allocator(nullptr) {} + + MqttConnection *connection; + OnMessageReceivedHandler onMessageReceived; + Allocator *allocator; + }; + + static void s_cleanUpOnPublishData(void *userData) + { + auto callbackData = reinterpret_cast<PubCallbackData *>(userData); + Crt::Delete(callbackData, callbackData->allocator); + } + + void MqttConnection::s_onPublish( + aws_mqtt_client_connection *, + const aws_byte_cursor *topic, + const aws_byte_cursor *payload, + bool dup, + enum aws_mqtt_qos qos, + bool retain, + void *userData) + { + auto callbackData = reinterpret_cast<PubCallbackData *>(userData); + + if (callbackData->onMessageReceived) + { + String topicStr(reinterpret_cast<char *>(topic->ptr), topic->len); + ByteBuf payloadBuf = aws_byte_buf_from_array(payload->ptr, payload->len); + callbackData->onMessageReceived( + *(callbackData->connection), topicStr, payloadBuf, dup, qos, retain); + } + } + + struct OpCompleteCallbackData + { + OpCompleteCallbackData() : connection(nullptr), topic(nullptr), allocator(nullptr) {} + + MqttConnection *connection; + OnOperationCompleteHandler onOperationComplete; + const char *topic; + Allocator *allocator; + }; + + void MqttConnection::s_onOpComplete( + aws_mqtt_client_connection *, + uint16_t packetId, + int errorCode, + void *userData) + { + auto callbackData = reinterpret_cast<OpCompleteCallbackData *>(userData); + + if (callbackData->onOperationComplete) + { + callbackData->onOperationComplete(*callbackData->connection, packetId, errorCode); + } + + if (callbackData->topic) + { + aws_mem_release( + callbackData->allocator, reinterpret_cast<void *>(const_cast<char *>(callbackData->topic))); + } + + Crt::Delete(callbackData, callbackData->allocator); + } + + struct SubAckCallbackData + { + SubAckCallbackData() : connection(nullptr), topic(nullptr), allocator(nullptr) {} + + MqttConnection *connection; + OnSubAckHandler onSubAck; + const char *topic; + Allocator *allocator; + }; + + void MqttConnection::s_onSubAck( + aws_mqtt_client_connection *, + uint16_t packetId, + const struct aws_byte_cursor *topic, + enum aws_mqtt_qos qos, + int errorCode, + void *userData) + { + auto callbackData = reinterpret_cast<SubAckCallbackData *>(userData); + + if (callbackData->onSubAck) + { + String topicStr(reinterpret_cast<char *>(topic->ptr), topic->len); + callbackData->onSubAck(*callbackData->connection, packetId, topicStr, qos, errorCode); + } + + if (callbackData->topic) + { + aws_mem_release( + callbackData->allocator, reinterpret_cast<void *>(const_cast<char *>(callbackData->topic))); + } + + Crt::Delete(callbackData, callbackData->allocator); + } + + struct MultiSubAckCallbackData + { + MultiSubAckCallbackData() : connection(nullptr), topic(nullptr), allocator(nullptr) {} + + MqttConnection *connection; + OnMultiSubAckHandler onSubAck; + const char *topic; + Allocator *allocator; + }; + + void MqttConnection::s_onMultiSubAck( + aws_mqtt_client_connection *, + uint16_t packetId, + const struct aws_array_list *topicSubacks, + int errorCode, + void *userData) + { + auto callbackData = reinterpret_cast<MultiSubAckCallbackData *>(userData); + + if (callbackData->onSubAck) + { + size_t length = aws_array_list_length(topicSubacks); + Vector<String> topics; + topics.reserve(length); + QOS qos = AWS_MQTT_QOS_AT_MOST_ONCE; + for (size_t i = 0; i < length; ++i) + { + aws_mqtt_topic_subscription *subscription = NULL; + aws_array_list_get_at(topicSubacks, &subscription, i); + topics.push_back( + String(reinterpret_cast<char *>(subscription->topic.ptr), subscription->topic.len)); + qos = subscription->qos; + } + + callbackData->onSubAck(*callbackData->connection, packetId, topics, qos, errorCode); + } + + if (callbackData->topic) + { + aws_mem_release( + callbackData->allocator, reinterpret_cast<void *>(const_cast<char *>(callbackData->topic))); + } + + Crt::Delete(callbackData, callbackData->allocator); + } + + void MqttConnection::s_connectionInit( + MqttConnection *self, + const char *hostName, + uint16_t port, + const Io::SocketOptions &socketOptions) + { + + self->m_hostName = String(hostName); + self->m_port = port; + self->m_socketOptions = socketOptions; + + self->m_underlyingConnection = aws_mqtt_client_connection_new(self->m_owningClient); + + if (self->m_underlyingConnection) + { + aws_mqtt_client_connection_set_connection_interruption_handlers( + self->m_underlyingConnection, + MqttConnection::s_onConnectionInterrupted, + self, + MqttConnection::s_onConnectionResumed, + self); + } + } + + void MqttConnection::s_onWebsocketHandshake( + struct aws_http_message *rawRequest, + void *user_data, + aws_mqtt_transform_websocket_handshake_complete_fn *complete_fn, + void *complete_ctx) + { + auto connection = reinterpret_cast<MqttConnection *>(user_data); + + Allocator *allocator = connection->m_owningClient->allocator; + // we have to do this because of private constructors. + auto toSeat = + reinterpret_cast<Http::HttpRequest *>(aws_mem_acquire(allocator, sizeof(Http::HttpRequest))); + toSeat = new (toSeat) Http::HttpRequest(allocator, rawRequest); + + std::shared_ptr<Http::HttpRequest> request = std::shared_ptr<Http::HttpRequest>( + toSeat, [allocator](Http::HttpRequest *ptr) { Crt::Delete(ptr, allocator); }); + + auto onInterceptComplete = + [complete_fn, + complete_ctx](const std::shared_ptr<Http::HttpRequest> &transformedRequest, int errorCode) { + complete_fn(transformedRequest->GetUnderlyingMessage(), errorCode, complete_ctx); + }; + + connection->WebsocketInterceptor(request, onInterceptComplete); + } + + MqttConnection::MqttConnection( + aws_mqtt_client *client, + const char *hostName, + uint16_t port, + const Io::SocketOptions &socketOptions, + const Crt::Io::TlsContext &tlsContext, + bool useWebsocket) noexcept + : m_owningClient(client), m_tlsContext(tlsContext), m_tlsOptions(tlsContext.NewConnectionOptions()), + m_onAnyCbData(nullptr), m_useTls(true), m_useWebsocket(useWebsocket) + { + s_connectionInit(this, hostName, port, socketOptions); + } + + MqttConnection::MqttConnection( + aws_mqtt_client *client, + const char *hostName, + uint16_t port, + const Io::SocketOptions &socketOptions, + bool useWebsocket) noexcept + : m_owningClient(client), m_onAnyCbData(nullptr), m_useTls(false), m_useWebsocket(useWebsocket) + { + s_connectionInit(this, hostName, port, socketOptions); + } + + MqttConnection::~MqttConnection() + { + if (*this) + { + aws_mqtt_client_connection_release(m_underlyingConnection); + + if (m_onAnyCbData) + { + auto pubCallbackData = reinterpret_cast<PubCallbackData *>(m_onAnyCbData); + Crt::Delete(pubCallbackData, pubCallbackData->allocator); + } + } + } + + MqttConnection::operator bool() const noexcept { return m_underlyingConnection != nullptr; } + + int MqttConnection::LastError() const noexcept { return aws_last_error(); } + + bool MqttConnection::SetWill(const char *topic, QOS qos, bool retain, const ByteBuf &payload) noexcept + { + ByteBuf topicBuf = aws_byte_buf_from_c_str(topic); + ByteCursor topicCur = aws_byte_cursor_from_buf(&topicBuf); + ByteCursor payloadCur = aws_byte_cursor_from_buf(&payload); + + return aws_mqtt_client_connection_set_will( + m_underlyingConnection, &topicCur, qos, retain, &payloadCur) == 0; + } + + bool MqttConnection::SetLogin(const char *userName, const char *password) noexcept + { + ByteBuf userNameBuf = aws_byte_buf_from_c_str(userName); + ByteCursor userNameCur = aws_byte_cursor_from_buf(&userNameBuf); + + ByteCursor *pwdCurPtr = nullptr; + ByteCursor pwdCur; + + if (password) + { + pwdCur = ByteCursorFromCString(password); + pwdCurPtr = &pwdCur; + } + return aws_mqtt_client_connection_set_login(m_underlyingConnection, &userNameCur, pwdCurPtr) == 0; + } + + bool MqttConnection::SetWebsocketProxyOptions( + const Http::HttpClientConnectionProxyOptions &proxyOptions) noexcept + { + m_proxyOptions = proxyOptions; + return true; + } + + bool MqttConnection::SetHttpProxyOptions( + const Http::HttpClientConnectionProxyOptions &proxyOptions) noexcept + { + m_proxyOptions = proxyOptions; + return true; + } + + bool MqttConnection::SetReconnectTimeout(uint64_t min_seconds, uint64_t max_seconds) noexcept + { + return aws_mqtt_client_connection_set_reconnect_timeout( + m_underlyingConnection, min_seconds, max_seconds) == 0; + } + + bool MqttConnection::Connect( + const char *clientId, + bool cleanSession, + uint16_t keepAliveTime, + uint32_t pingTimeoutMs, + uint32_t protocolOperationTimeoutMs) noexcept + { + aws_mqtt_connection_options options; + AWS_ZERO_STRUCT(options); + options.client_id = aws_byte_cursor_from_c_str(clientId); + options.host_name = aws_byte_cursor_from_array( + reinterpret_cast<const uint8_t *>(m_hostName.data()), m_hostName.length()); + options.tls_options = + m_useTls ? const_cast<aws_tls_connection_options *>(m_tlsOptions.GetUnderlyingHandle()) : nullptr; + options.port = m_port; + options.socket_options = &m_socketOptions.GetImpl(); + options.clean_session = cleanSession; + options.keep_alive_time_secs = keepAliveTime; + options.ping_timeout_ms = pingTimeoutMs; + options.protocol_operation_timeout_ms = protocolOperationTimeoutMs; + options.on_connection_complete = MqttConnection::s_onConnectionCompleted; + options.user_data = this; + + if (m_useWebsocket) + { + if (WebsocketInterceptor) + { + if (aws_mqtt_client_connection_use_websockets( + m_underlyingConnection, MqttConnection::s_onWebsocketHandshake, this, nullptr, nullptr)) + { + return false; + } + } + else + { + if (aws_mqtt_client_connection_use_websockets( + m_underlyingConnection, nullptr, nullptr, nullptr, nullptr)) + { + return false; + } + } + } + + if (m_proxyOptions) + { + struct aws_http_proxy_options proxyOptions; + m_proxyOptions->InitializeRawProxyOptions(proxyOptions); + + if (aws_mqtt_client_connection_set_http_proxy_options(m_underlyingConnection, &proxyOptions)) + { + return false; + } + } + + return aws_mqtt_client_connection_connect(m_underlyingConnection, &options) == AWS_OP_SUCCESS; + } + + bool MqttConnection::Disconnect() noexcept + { + return aws_mqtt_client_connection_disconnect( + m_underlyingConnection, MqttConnection::s_onDisconnect, this) == AWS_OP_SUCCESS; + } + + aws_mqtt_client_connection *MqttConnection::GetUnderlyingConnection() noexcept + { + return m_underlyingConnection; + } + + bool MqttConnection::SetOnMessageHandler(OnPublishReceivedHandler &&onPublish) noexcept + { + return SetOnMessageHandler( + [onPublish]( + MqttConnection &connection, const String &topic, const ByteBuf &payload, bool, QOS, bool) { + onPublish(connection, topic, payload); + }); + } + + bool MqttConnection::SetOnMessageHandler(OnMessageReceivedHandler &&onMessage) noexcept + { + auto pubCallbackData = Aws::Crt::New<PubCallbackData>(m_owningClient->allocator); + + if (!pubCallbackData) + { + return false; + } + + pubCallbackData->connection = this; + pubCallbackData->onMessageReceived = std::move(onMessage); + pubCallbackData->allocator = m_owningClient->allocator; + + if (!aws_mqtt_client_connection_set_on_any_publish_handler( + m_underlyingConnection, s_onPublish, pubCallbackData)) + { + m_onAnyCbData = reinterpret_cast<void *>(pubCallbackData); + return true; + } + + Aws::Crt::Delete(pubCallbackData, pubCallbackData->allocator); + return false; + } + + uint16_t MqttConnection::Subscribe( + const char *topicFilter, + QOS qos, + OnPublishReceivedHandler &&onPublish, + OnSubAckHandler &&onSubAck) noexcept + { + return Subscribe( + topicFilter, + qos, + [onPublish]( + MqttConnection &connection, const String &topic, const ByteBuf &payload, bool, QOS, bool) { + onPublish(connection, topic, payload); + }, + std::move(onSubAck)); + } + + uint16_t MqttConnection::Subscribe( + const char *topicFilter, + QOS qos, + OnMessageReceivedHandler &&onMessage, + OnSubAckHandler &&onSubAck) noexcept + { + auto pubCallbackData = Crt::New<PubCallbackData>(m_owningClient->allocator); + + if (!pubCallbackData) + { + return 0; + } + + pubCallbackData->connection = this; + pubCallbackData->onMessageReceived = std::move(onMessage); + pubCallbackData->allocator = m_owningClient->allocator; + + auto subAckCallbackData = Crt::New<SubAckCallbackData>(m_owningClient->allocator); + + if (!subAckCallbackData) + { + Crt::Delete(pubCallbackData, m_owningClient->allocator); + return 0; + } + + subAckCallbackData->connection = this; + subAckCallbackData->allocator = m_owningClient->allocator; + subAckCallbackData->onSubAck = std::move(onSubAck); + subAckCallbackData->topic = nullptr; + subAckCallbackData->allocator = m_owningClient->allocator; + + ByteBuf topicFilterBuf = aws_byte_buf_from_c_str(topicFilter); + ByteCursor topicFilterCur = aws_byte_cursor_from_buf(&topicFilterBuf); + + uint16_t packetId = aws_mqtt_client_connection_subscribe( + m_underlyingConnection, + &topicFilterCur, + qos, + s_onPublish, + pubCallbackData, + s_cleanUpOnPublishData, + s_onSubAck, + subAckCallbackData); + + if (!packetId) + { + Crt::Delete(pubCallbackData, pubCallbackData->allocator); + Crt::Delete(subAckCallbackData, subAckCallbackData->allocator); + } + + return packetId; + } + + uint16_t MqttConnection::Subscribe( + const Vector<std::pair<const char *, OnPublishReceivedHandler>> &topicFilters, + QOS qos, + OnMultiSubAckHandler &&onSubAck) noexcept + { + Vector<std::pair<const char *, OnMessageReceivedHandler>> newTopicFilters; + newTopicFilters.reserve(topicFilters.size()); + for (const auto &pair : topicFilters) + { + const OnPublishReceivedHandler &pubHandler = pair.second; + newTopicFilters.emplace_back( + pair.first, + [pubHandler]( + MqttConnection &connection, const String &topic, const ByteBuf &payload, bool, QOS, bool) { + pubHandler(connection, topic, payload); + }); + } + return Subscribe(newTopicFilters, qos, std::move(onSubAck)); + } + + uint16_t MqttConnection::Subscribe( + const Vector<std::pair<const char *, OnMessageReceivedHandler>> &topicFilters, + QOS qos, + OnMultiSubAckHandler &&onSubAck) noexcept + { + uint16_t packetId = 0; + auto subAckCallbackData = Crt::New<MultiSubAckCallbackData>(m_owningClient->allocator); + + if (!subAckCallbackData) + { + return 0; + } + + aws_array_list multiPub; + AWS_ZERO_STRUCT(multiPub); + + if (aws_array_list_init_dynamic( + &multiPub, m_owningClient->allocator, topicFilters.size(), sizeof(aws_mqtt_topic_subscription))) + { + Crt::Delete(subAckCallbackData, m_owningClient->allocator); + return 0; + } + + for (auto &topicFilter : topicFilters) + { + auto pubCallbackData = Crt::New<PubCallbackData>(m_owningClient->allocator); + + if (!pubCallbackData) + { + goto clean_up; + } + + pubCallbackData->connection = this; + pubCallbackData->onMessageReceived = topicFilter.second; + pubCallbackData->allocator = m_owningClient->allocator; + + ByteBuf topicFilterBuf = aws_byte_buf_from_c_str(topicFilter.first); + ByteCursor topicFilterCur = aws_byte_cursor_from_buf(&topicFilterBuf); + + aws_mqtt_topic_subscription subscription; + subscription.on_cleanup = s_cleanUpOnPublishData; + subscription.on_publish = s_onPublish; + subscription.on_publish_ud = pubCallbackData; + subscription.qos = qos; + subscription.topic = topicFilterCur; + + aws_array_list_push_back(&multiPub, reinterpret_cast<const void *>(&subscription)); + } + + subAckCallbackData->connection = this; + subAckCallbackData->allocator = m_owningClient->allocator; + subAckCallbackData->onSubAck = std::move(onSubAck); + subAckCallbackData->topic = nullptr; + subAckCallbackData->allocator = m_owningClient->allocator; + + packetId = aws_mqtt_client_connection_subscribe_multiple( + m_underlyingConnection, &multiPub, s_onMultiSubAck, subAckCallbackData); + + clean_up: + if (!packetId) + { + size_t length = aws_array_list_length(&multiPub); + for (size_t i = 0; i < length; ++i) + { + aws_mqtt_topic_subscription *subscription = NULL; + aws_array_list_get_at_ptr(&multiPub, reinterpret_cast<void **>(&subscription), i); + auto pubCallbackData = reinterpret_cast<PubCallbackData *>(subscription->on_publish_ud); + Crt::Delete(pubCallbackData, m_owningClient->allocator); + } + + Crt::Delete(subAckCallbackData, m_owningClient->allocator); + } + + aws_array_list_clean_up(&multiPub); + + return packetId; + } + + uint16_t MqttConnection::Unsubscribe( + const char *topicFilter, + OnOperationCompleteHandler &&onOpComplete) noexcept + { + auto opCompleteCallbackData = Crt::New<OpCompleteCallbackData>(m_owningClient->allocator); + + if (!opCompleteCallbackData) + { + return 0; + } + + opCompleteCallbackData->connection = this; + opCompleteCallbackData->allocator = m_owningClient->allocator; + opCompleteCallbackData->onOperationComplete = std::move(onOpComplete); + opCompleteCallbackData->topic = nullptr; + ByteBuf topicFilterBuf = aws_byte_buf_from_c_str(topicFilter); + ByteCursor topicFilterCur = aws_byte_cursor_from_buf(&topicFilterBuf); + + uint16_t packetId = aws_mqtt_client_connection_unsubscribe( + m_underlyingConnection, &topicFilterCur, s_onOpComplete, opCompleteCallbackData); + + if (!packetId) + { + Crt::Delete(opCompleteCallbackData, m_owningClient->allocator); + } + + return packetId; + } + + uint16_t MqttConnection::Publish( + const char *topic, + QOS qos, + bool retain, + const ByteBuf &payload, + OnOperationCompleteHandler &&onOpComplete) noexcept + { + + auto opCompleteCallbackData = Crt::New<OpCompleteCallbackData>(m_owningClient->allocator); + if (!opCompleteCallbackData) + { + return 0; + } + + size_t topicLen = strnlen(topic, AWS_MQTT_MAX_TOPIC_LENGTH) + 1; + char *topicCpy = + reinterpret_cast<char *>(aws_mem_calloc(m_owningClient->allocator, topicLen, sizeof(char))); + + if (!topicCpy) + { + Crt::Delete(opCompleteCallbackData, m_owningClient->allocator); + } + + memcpy(topicCpy, topic, topicLen); + + opCompleteCallbackData->connection = this; + opCompleteCallbackData->allocator = m_owningClient->allocator; + opCompleteCallbackData->onOperationComplete = std::move(onOpComplete); + opCompleteCallbackData->topic = topicCpy; + ByteCursor topicCur = aws_byte_cursor_from_array(topicCpy, topicLen - 1); + + ByteCursor payloadCur = aws_byte_cursor_from_buf(&payload); + uint16_t packetId = aws_mqtt_client_connection_publish( + m_underlyingConnection, + &topicCur, + qos, + retain, + &payloadCur, + s_onOpComplete, + opCompleteCallbackData); + + if (!packetId) + { + aws_mem_release(m_owningClient->allocator, reinterpret_cast<void *>(topicCpy)); + Crt::Delete(opCompleteCallbackData, m_owningClient->allocator); + } + + return packetId; + } + + const MqttConnectionOperationStatistics &MqttConnection::GetOperationStatistics() noexcept + { + aws_mqtt_connection_operation_statistics m_operationStatisticsNative = {0, 0, 0, 0}; + if (m_underlyingConnection != nullptr) + { + aws_mqtt_client_connection_get_stats(m_underlyingConnection, &m_operationStatisticsNative); + m_operationStatistics.incompleteOperationCount = + m_operationStatisticsNative.incomplete_operation_count; + m_operationStatistics.incompleteOperationSize = + m_operationStatisticsNative.incomplete_operation_size; + m_operationStatistics.unackedOperationCount = m_operationStatisticsNative.unacked_operation_count; + m_operationStatistics.unackedOperationSize = m_operationStatisticsNative.unacked_operation_size; + } + return m_operationStatistics; + } + + MqttClient::MqttClient(Io::ClientBootstrap &bootstrap, Allocator *allocator) noexcept + : m_client(aws_mqtt_client_new(allocator, bootstrap.GetUnderlyingHandle())) + { + } + + MqttClient::MqttClient(Allocator *allocator) noexcept + : m_client(aws_mqtt_client_new( + allocator, + Crt::ApiHandle::GetOrCreateStaticDefaultClientBootstrap()->GetUnderlyingHandle())) + { + } + + MqttClient::~MqttClient() + { + aws_mqtt_client_release(m_client); + m_client = nullptr; + } + + MqttClient::MqttClient(MqttClient &&toMove) noexcept : m_client(toMove.m_client) + { + toMove.m_client = nullptr; + } + + MqttClient &MqttClient::operator=(MqttClient &&toMove) noexcept + { + if (&toMove != this) + { + m_client = toMove.m_client; + toMove.m_client = nullptr; + } + + return *this; + } + + MqttClient::operator bool() const noexcept { return m_client != nullptr; } + + int MqttClient::LastError() const noexcept { return aws_last_error(); } + + std::shared_ptr<MqttConnection> MqttClient::NewConnection( + const char *hostName, + uint16_t port, + const Io::SocketOptions &socketOptions, + const Crt::Io::TlsContext &tlsContext, + bool useWebsocket) noexcept + { + if (!tlsContext) + { + AWS_LOGF_ERROR( + AWS_LS_MQTT_CLIENT, + "id=%p Trying to call MqttClient::NewConnection using an invalid TlsContext.", + (void *)m_client); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return nullptr; + } + + // If you're reading this and asking.... why is this so complicated? Why not use make_shared + // or allocate_shared? Well, MqttConnection constructors are private and stl is dumb like that. + // so, we do it manually. + Allocator *allocator = m_client->allocator; + MqttConnection *toSeat = + reinterpret_cast<MqttConnection *>(aws_mem_acquire(allocator, sizeof(MqttConnection))); + if (!toSeat) + { + return nullptr; + } + + toSeat = new (toSeat) MqttConnection(m_client, hostName, port, socketOptions, tlsContext, useWebsocket); + return std::shared_ptr<MqttConnection>(toSeat, [allocator](MqttConnection *connection) { + connection->~MqttConnection(); + aws_mem_release(allocator, reinterpret_cast<void *>(connection)); + }); + } + + std::shared_ptr<MqttConnection> MqttClient::NewConnection( + const char *hostName, + uint16_t port, + const Io::SocketOptions &socketOptions, + bool useWebsocket) noexcept + + { + // If you're reading this and asking.... why is this so complicated? Why not use make_shared + // or allocate_shared? Well, MqttConnection constructors are private and stl is dumb like that. + // so, we do it manually. + Allocator *allocator = m_client->allocator; + MqttConnection *toSeat = + reinterpret_cast<MqttConnection *>(aws_mem_acquire(m_client->allocator, sizeof(MqttConnection))); + if (!toSeat) + { + return nullptr; + } + + toSeat = new (toSeat) MqttConnection(m_client, hostName, port, socketOptions, useWebsocket); + return std::shared_ptr<MqttConnection>(toSeat, [allocator](MqttConnection *connection) { + connection->~MqttConnection(); + aws_mem_release(allocator, reinterpret_cast<void *>(connection)); + }); + } + } // namespace Mqtt + } // namespace Crt +} // namespace Aws diff --git a/contrib/restricted/aws/aws-crt-cpp/ya.make b/contrib/restricted/aws/aws-crt-cpp/ya.make new file mode 100644 index 0000000000..962f99ecb7 --- /dev/null +++ b/contrib/restricted/aws/aws-crt-cpp/ya.make @@ -0,0 +1,102 @@ +# Generated by devtools/yamaker from nixpkgs 23.05. + +LIBRARY() + +LICENSE( + Apache-2.0 AND + MIT +) + +LICENSE_TEXTS(.yandex_meta/licenses.list.txt) + +VERSION(0.19.8) + +ORIGINAL_SOURCE(https://github.com/awslabs/aws-crt-cpp/archive/v0.19.8.tar.gz) + +PEERDIR( + contrib/restricted/aws/aws-c-auth + contrib/restricted/aws/aws-c-cal + contrib/restricted/aws/aws-c-common + contrib/restricted/aws/aws-c-event-stream + contrib/restricted/aws/aws-c-http + contrib/restricted/aws/aws-c-io + contrib/restricted/aws/aws-c-mqtt + contrib/restricted/aws/aws-c-s3 + contrib/restricted/aws/aws-c-sdkutils +) + +ADDINCL( + GLOBAL contrib/restricted/aws/aws-crt-cpp/include +) + +NO_COMPILER_WARNINGS() + +NO_UTIL() + +CFLAGS( + -DAWS_AUTH_USE_IMPORT_EXPORT + -DAWS_CAL_USE_IMPORT_EXPORT + -DAWS_CHECKSUMS_USE_IMPORT_EXPORT + -DAWS_COMMON_USE_IMPORT_EXPORT + -DAWS_COMPRESSION_USE_IMPORT_EXPORT + -DAWS_CRT_CPP_USE_IMPORT_EXPORT + -DAWS_EVENT_STREAM_USE_IMPORT_EXPORT + -DAWS_HTTP_USE_IMPORT_EXPORT + -DAWS_IO_USE_IMPORT_EXPORT + -DAWS_MQTT_USE_IMPORT_EXPORT + -DAWS_MQTT_WITH_WEBSOCKETS + -DAWS_S3_USE_IMPORT_EXPORT + -DAWS_SDKUTILS_USE_IMPORT_EXPORT + -DAWS_USE_EPOLL + -DCJSON_HIDE_SYMBOLS + -DS2N_CLONE_SUPPORTED + -DS2N_CPUID_AVAILABLE + -DS2N_FALL_THROUGH_SUPPORTED + -DS2N_FEATURES_AVAILABLE + -DS2N_KYBER512R3_AVX2_BMI2 + -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD5_SHA1_HASH + -DS2N_LIBCRYPTO_SUPPORTS_EVP_MD_CTX_SET_PKEY_CTX + -DS2N_LIBCRYPTO_SUPPORTS_EVP_RC4 + -DS2N_MADVISE_SUPPORTED + -DS2N_PLATFORM_SUPPORTS_KTLS + -DS2N_STACKTRACE + -DS2N___RESTRICT__SUPPORTED +) + +SRCS( + source/Allocator.cpp + source/Api.cpp + source/DateTime.cpp + source/ImdsClient.cpp + source/JsonObject.cpp + source/StringUtils.cpp + source/Types.cpp + source/UUID.cpp + source/auth/Credentials.cpp + source/auth/Sigv4Signing.cpp + source/crypto/HMAC.cpp + source/crypto/Hash.cpp + source/endpoints/RuleEngine.cpp + source/external/cJSON.cpp + source/http/HttpConnection.cpp + source/http/HttpConnectionManager.cpp + source/http/HttpProxyStrategy.cpp + source/http/HttpRequestResponse.cpp + source/io/Bootstrap.cpp + source/io/ChannelHandler.cpp + source/io/EventLoopGroup.cpp + source/io/HostResolver.cpp + source/io/Pkcs11.cpp + source/io/SocketOptions.cpp + source/io/Stream.cpp + source/io/TlsOptions.cpp + source/io/Uri.cpp + source/iot/Mqtt5Client.cpp + source/iot/MqttClient.cpp + source/iot/MqttCommon.cpp + source/mqtt/Mqtt5Client.cpp + source/mqtt/Mqtt5Packets.cpp + source/mqtt/MqttClient.cpp +) + +END() |