diff --git a/sdk/core/azure-core/CMakeLists.txt b/sdk/core/azure-core/CMakeLists.txt index 1ccd4cd3c..fb8e95377 100644 --- a/sdk/core/azure-core/CMakeLists.txt +++ b/sdk/core/azure-core/CMakeLists.txt @@ -170,6 +170,7 @@ if(BUILD_TESTING) if(DEFINED ENV{AZURE_CORE_ENABLE_JSON_TESTS}) add_subdirectory(test/nlohmann-json-test) endif() + add_subdirectory(test/fault-injector) endif() if (BUILD_PERFORMANCE_TESTS) diff --git a/sdk/core/azure-core/test/fault-injector/CMakeLists.txt b/sdk/core/azure-core/test/fault-injector/CMakeLists.txt new file mode 100644 index 000000000..da99308b2 --- /dev/null +++ b/sdk/core/azure-core/test/fault-injector/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: MIT + +cmake_minimum_required (VERSION 3.13) + +set(azure-core-test-fault-injector) + +project (azure-core-test-fault-injector LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +add_executable ( + azure-core-test-fault-injector + main.cpp +) + +target_link_libraries(azure-core-test-fault-injector PRIVATE azure-core) diff --git a/sdk/core/azure-core/test/fault-injector/main.cpp b/sdk/core/azure-core/test/fault-injector/main.cpp new file mode 100644 index 000000000..e4a3ffe66 --- /dev/null +++ b/sdk/core/azure-core/test/fault-injector/main.cpp @@ -0,0 +1,106 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @brief Validates the Azure Core transport adapters with fault responses from server. + * + * @note This test requires the Http-fault-injector + * (https://github.com/Azure/azure-sdk-tools/tree/master/tools/http-fault-injector) running. Follow + * the instructions to install and run the server before running this test. + * + */ + +#include + +#if defined(BUILD_CURL_HTTP_TRANSPORT_ADAPTER) +#include +#endif + +#if defined(BUILD_TRANSPORT_WINHTTP_ADAPTER) +#include "azure/core/http/win_http_transport.hpp" +#endif + +#include +#include +#include + +/** + * @brief The options to set the #FaultInjectionClient behavior like the injection server and the + * http client implementation to use. + * + */ +struct FaultInjectionClientOptions +{ + Azure::Core::Url m_url; + std::shared_ptr m_transport; +}; + +/** + * @brief An special http policy to redirect requests to the Fault injector server. + * + */ +class FaultInjectionClient : public Azure::Core::Http::HttpTransport { +private: + FaultInjectionClientOptions m_options; + +public: + FaultInjectionClient(FaultInjectionClientOptions options) : m_options(std::move(options)) {} + + std::unique_ptr Send( + Azure::Core::Http::Request& request, + Azure::Core::Context const& context) override + { + auto redirectRequest = Azure::Core::Http::Request( + request.GetMethod(), Azure::Core::Url(m_options.m_url.GetAbsoluteUrl())); + for (auto& header : request.GetHeaders()) + { + redirectRequest.SetHeader(header.first, header.second); + } + + { + auto& url = request.GetUrl(); + auto port = url.GetPort(); + redirectRequest.SetHeader( + "Host", url.GetHost() + (port != 0 ? ":" + std::to_string(port) : "")); + } + + return m_options.m_transport->Send(redirectRequest, context); + } +}; + +int main() +{ + /* The transport adapter must allow insecure SSL certs. + If both curl and winHttp are available, curl is preferred for this test.for*/ +#if defined(BUILD_CURL_HTTP_TRANSPORT_ADAPTER) + Azure::Core::Http::CurlTransportOptions curlOptions; + curlOptions.SslVerifyPeer = false; + auto implementationClient = std::make_shared(curlOptions); + +#elif (BUILD_TRANSPORT_WINHTTP_ADAPTER) + // TODO: make winHTTP to support insecure SSL certs + Azure::Core::Http::WinHttpTransportOptions winHttpOptions; + auto implementationClient = std::make_shared(winHttpOptions); +#endif + + FaultInjectionClientOptions options; + options.m_url = Azure::Core::Url("https://localhost:7778"); + options.m_transport = implementationClient; + FaultInjectionClient client(options); + + std::cout << "Sending request..." << std::endl; + + Azure::Core::Context context; + auto request = Azure::Core::Http::Request( + Azure::Core::Http::HttpMethod::Get, Azure::Core::Url("https://www.example.org")); + auto response = client.Send(request, context); + // Make sure to pull all bytes from network. + auto body = response->ExtractBodyStream()->ReadToEnd(); + + std::cout << "Status Code: " + << static_cast::type>( + response->GetStatusCode()) + << std::endl; + + return 0; +} diff --git a/sdk/storage/azure-storage-blobs/CMakeLists.txt b/sdk/storage/azure-storage-blobs/CMakeLists.txt index 6c21f4ee7..797c9a499 100644 --- a/sdk/storage/azure-storage-blobs/CMakeLists.txt +++ b/sdk/storage/azure-storage-blobs/CMakeLists.txt @@ -104,6 +104,8 @@ if(BUILD_TESTING) ) target_link_libraries(azure-storage-test PRIVATE azure-storage-blobs) + + add_subdirectory(test/fault-injector) endif() if(BUILD_STORAGE_SAMPLES) diff --git a/sdk/storage/azure-storage-blobs/test/fault-injector/CMakeLists.txt b/sdk/storage/azure-storage-blobs/test/fault-injector/CMakeLists.txt new file mode 100644 index 000000000..bde8c7d19 --- /dev/null +++ b/sdk/storage/azure-storage-blobs/test/fault-injector/CMakeLists.txt @@ -0,0 +1,17 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# SPDX-License-Identifier: MIT + +cmake_minimum_required (VERSION 3.13) + +set(azure-storage-blobs-test-fault-injector) + +project (azure-storage-blobs-test-fault-injector LANGUAGES CXX) +set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +add_executable ( + azure-storage-blobs-test-fault-injector + main.cpp +) + +target_link_libraries(azure-storage-blobs-test-fault-injector PRIVATE azure-storage-blobs) diff --git a/sdk/storage/azure-storage-blobs/test/fault-injector/main.cpp b/sdk/storage/azure-storage-blobs/test/fault-injector/main.cpp new file mode 100644 index 000000000..475eca295 --- /dev/null +++ b/sdk/storage/azure-storage-blobs/test/fault-injector/main.cpp @@ -0,0 +1,113 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @brief Validates the Azure Storage blobs SDK client with fault responses from server. + * + * @note This test requires the Http-fault-injector + * (https://github.com/Azure/azure-sdk-tools/tree/master/tools/http-fault-injector) running. Follow + * the instructions to install and run the server before running this test. + * + */ + +#if defined(_MSC_VER) +// For using std::getenv() +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include + +#if defined(BUILD_CURL_HTTP_TRANSPORT_ADAPTER) +#include +#endif + +#if defined(BUILD_TRANSPORT_WINHTTP_ADAPTER) +#include "azure/core/http/win_http_transport.hpp" +#endif + +#include +#include +#include + +/** + * @brief The options to set the #FaultInjectionClient behavior like the injection server and the + * http client implementation to use. + * + */ +struct FaultInjectionClientOptions +{ + Azure::Core::Url m_url; + std::shared_ptr m_transport; +}; + +/** + * @brief An special http policy to redirect requests to the Fault injector server. + * + */ +class FaultInjectionClient : public Azure::Core::Http::HttpTransport { +private: + FaultInjectionClientOptions m_options; + +public: + FaultInjectionClient(FaultInjectionClientOptions options) : m_options(std::move(options)) {} + + std::unique_ptr Send( + Azure::Core::Http::Request& request, + Azure::Core::Context const& context) override + { + auto redirectRequest = Azure::Core::Http::Request( + request.GetMethod(), Azure::Core::Url(m_options.m_url.GetAbsoluteUrl())); + for (auto& header : request.GetHeaders()) + { + redirectRequest.SetHeader(header.first, header.second); + } + + { + auto& url = request.GetUrl(); + auto port = url.GetPort(); + redirectRequest.SetHeader( + "Host", url.GetHost() + (port != 0 ? ":" + std::to_string(port) : "")); + } + + return m_options.m_transport->Send(redirectRequest, context); + } +}; + +int main() +{ + /* The transport adapter must allow insecure SSL certs. + If both curl and winHttp are available, curl is preferred for this test.for*/ +#if defined(BUILD_CURL_HTTP_TRANSPORT_ADAPTER) + Azure::Core::Http::CurlTransportOptions curlOptions; + curlOptions.SslVerifyPeer = false; + auto implementationClient = std::make_shared(curlOptions); + +#elif (BUILD_TRANSPORT_WINHTTP_ADAPTER) + // TODO: make winHTTP to support insecure SSL certs + Azure::Core::Http::WinHttpTransportOptions winHttpOptions; + auto implementationClient = std::make_shared(winHttpOptions); +#endif + + std::string connectionString(std::getenv("STORAGE_CONNECTION_STRING")); + + // Set the options for the FaultInjectorClient + FaultInjectionClientOptions options; + options.m_url = Azure::Core::Url("https://localhost:7778"); + options.m_transport = implementationClient; + + // Set the FaultInjectorClient as the transport adapter for the blobs client. + Azure::Storage::Blobs::BlobClientOptions blobClientOptions; + blobClientOptions.Transport.Transport = std::make_shared(options); + + auto blobClient = Azure::Storage::Blobs::BlobClient::CreateFromConnectionString( + connectionString, "sample", "sample.txt", blobClientOptions); + + std::cout << "Sending request..." << std::endl; + + auto response = blobClient.Download(); + auto content = response.Value.BodyStream->ReadToEnd(); + + std::cout << "Content: " << std::string(content.begin(), content.end()) << std::endl; + + return 0; +}