diff --git a/.clang-format b/.clang-format index abbabdd83..0e09041b0 100644 --- a/.clang-format +++ b/.clang-format @@ -1,5 +1,5 @@ Language: Cpp -BasedOnStyle: Microsoft +BasedOnStyle: LLVM AlignAfterOpenBracket: AlwaysBreak AlignConsecutiveAssignments: false AlignConsecutiveDeclarations: false diff --git a/CMakeLists.txt b/CMakeLists.txt index 11fe8aa32..ccdb64ec4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,6 +37,4 @@ include(global_compile_options) # sub-projects add_subdirectory(sdk/core/azure-core) -add_subdirectory(sdk/platform/http_client/curl) # will work only if BUILD_CURL_TRANSPORT=ON add_subdirectory(sdk/samples/http_client/curl) # will work only if BUILD_CURL_TRANSPORT=ON -add_subdirectory(sdk/platform/http_client/nohttp) # will work only if !BUILD_CURL_TRANSPORT=ON diff --git a/CMakeSettings.json b/CMakeSettings.json index 26d779428..b1a94d21b 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -1,16 +1,16 @@ { - "configurations": [ - { - "name": "x64-Debug", - "generator": "Ninja", - "configurationType": "Debug", - "inheritEnvironments": [ "msvc_x64_x64" ], - "buildRoot": "${projectDir}\\out\\build\\${name}", - "installRoot": "${projectDir}\\out\\install\\${name}", - "cmakeCommandArgs": "-DINSTALL_GTEST=OFF -DBUILD_TESTING=ON", - "buildCommandArgs": "-v", - "ctestCommandArgs": "", - "variables": [] - } - ] -} \ No newline at end of file + "configurations": [ + { + "name": "x64-Debug", + "generator": "Ninja", + "configurationType": "Debug", + "inheritEnvironments": [ "msvc_x64_x64" ], + "buildRoot": "${projectDir}\\out\\build\\${name}", + "installRoot": "${projectDir}\\out\\install\\${name}", + "cmakeCommandArgs": "-DINSTALL_GTEST=OFF -DBUILD_TESTING=OFF -DBUILD_CURL_TRANSPORT=ON", + "buildCommandArgs": "-v", + "ctestCommandArgs": "", + "variables": [] + } + ] +} diff --git a/eng/pipelines/templates/jobs/archetype-sdk-client.yml b/eng/pipelines/templates/jobs/archetype-sdk-client.yml index 5a54af7d5..cf9174b56 100644 --- a/eng/pipelines/templates/jobs/archetype-sdk-client.yml +++ b/eng/pipelines/templates/jobs/archetype-sdk-client.yml @@ -9,48 +9,48 @@ jobs: matrix: Linux_x64: vm.image: 'ubuntu-18.04' - vcpkg.deps: '' + vcpkg.deps: 'curl[ssl]' VCPKG_DEFAULT_TRIPLET: 'x64-linux' Win_x86: vm.image: 'windows-2019' - vcpkg.deps: '' + vcpkg.deps: 'curl[winssl]' VCPKG_DEFAULT_TRIPLET: 'x86-windows-static' CMAKE_GENERATOR: 'Visual Studio 16 2019' CMAKE_GENERATOR_PLATFORM: Win32 Win_x64: vm.image: 'windows-2019' - vcpkg.deps: '' + vcpkg.deps: 'curl[winssl]' VCPKG_DEFAULT_TRIPLET: 'x64-windows-static' CMAKE_GENERATOR: 'Visual Studio 16 2019' CMAKE_GENERATOR_PLATFORM: x64 MacOS_x64: vm.image: 'macOS-10.14' - vcpkg.deps: '' + vcpkg.deps: 'curl[ssl]' VCPKG_DEFAULT_TRIPLET: 'x64-osx' # Unit testing ON Linux_x64_with_unit_test: vm.image: 'ubuntu-18.04' - vcpkg.deps: '' + vcpkg.deps: 'curl[ssl]' VCPKG_DEFAULT_TRIPLET: 'x64-linux' build.args: ' -DBUILD_TESTING=ON' Win_x86_with_unit_test: vm.image: 'windows-2019' - vcpkg.deps: '' + vcpkg.deps: 'curl[winssl]' VCPKG_DEFAULT_TRIPLET: 'x86-windows-static' CMAKE_GENERATOR: 'Visual Studio 16 2019' CMAKE_GENERATOR_PLATFORM: Win32 build.args: ' -DBUILD_TESTING=ON' Win_x64_with_unit_test: vm.image: 'windows-2019' - vcpkg.deps: '' + vcpkg.deps: 'curl[winssl]' VCPKG_DEFAULT_TRIPLET: 'x64-windows-static' CMAKE_GENERATOR: 'Visual Studio 16 2019' CMAKE_GENERATOR_PLATFORM: x64 build.args: ' -DBUILD_TESTING=ON' MacOS_x64_with_unit_test: vm.image: 'macOS-10.14' - vcpkg.deps: '' + vcpkg.deps: 'curl[ssl]' VCPKG_DEFAULT_TRIPLET: 'x64-osx' build.args: ' -DBUILD_TESTING=ON' pool: diff --git a/sdk/core/azure-core/CMakeLists.txt b/sdk/core/azure-core/CMakeLists.txt index 4d5da5e4d..7002f1d62 100644 --- a/sdk/core/azure-core/CMakeLists.txt +++ b/sdk/core/azure-core/CMakeLists.txt @@ -2,20 +2,33 @@ # SPDX-License-Identifier: MIT cmake_minimum_required (VERSION 3.12) +set(TARGET_NAME "azure-core") +project(${TARGET_NAME} LANGUAGES CXX) -project (azure-core LANGUAGES CXX) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED True) +find_package(CURL CONFIG) +if(NOT CURL_FOUND) + find_package(CURL REQUIRED) +endif() + add_library ( - azure-core - src/credentials/credentials - src/http/http - src/http/request - src/http/response + ${TARGET_NAME} + src/context.cpp + src/credentials/credentials.cpp + src/http/http.cpp + src/http/policy.cpp + src/http/request.cpp + src/http/response.cpp + src/http/curl/curl_transport.cpp + src/http/winhttp/win_http_transport.cpp ) -target_include_directories (azure-core PUBLIC $ $) +target_include_directories (${TARGET_NAME} PUBLIC $ $) # make sure that users can consume the project as a library. -add_library (Azure::Core ALIAS azure-core) +add_library (Azure::Core ALIAS ${TARGET_NAME}) + +target_include_directories(${TARGET_NAME} PUBLIC ${CURL_INCLUDE_DIRS}) +target_link_libraries(${TARGET_NAME} PRIVATE CURL::libcurl) diff --git a/sdk/core/azure-core/inc/azure.hpp b/sdk/core/azure-core/inc/azure.hpp index 8c3a4be7d..f93395713 100644 --- a/sdk/core/azure-core/inc/azure.hpp +++ b/sdk/core/azure-core/inc/azure.hpp @@ -5,6 +5,10 @@ #include +#define AZURE_UNREFERENCED_PARAMETER(x) ((void) (x)); + namespace Azure { namespace Core { + + }} // namespace Azure::Core diff --git a/sdk/core/azure-core/inc/context.hpp b/sdk/core/azure-core/inc/context.hpp new file mode 100644 index 000000000..6b94b4301 --- /dev/null +++ b/sdk/core/azure-core/inc/context.hpp @@ -0,0 +1,210 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include +#include +#include +#include + +namespace Azure { namespace Core { + + struct ValueBase + { + virtual ~ValueBase() {} + }; + + /* + * @brief ContextValue exists as a substitute for variant which isn't available until C++17 + */ + class ContextValue { + enum class ContextValueType + { + Undefined, + Bool, + Int, + StdString, + UniquePtr + }; + + ContextValueType m_contextValueType; + union + { + bool m_b; // if m_contextValueType == ContextValueType::Bool + int m_i; // if m_contextValueType == ContextValueType::Int + std::string m_s; // if m_contextValueType == ContextValueType::StdString + std::unique_ptr m_p; // if m_contextValueType == ContextValueType::UniquePtr + }; + + public: + ContextValue() noexcept : m_contextValueType(ContextValueType::Undefined) {} + ContextValue(bool b) noexcept : m_contextValueType(ContextValueType::Bool), m_b(b) {} + ContextValue(int i) noexcept : m_contextValueType(ContextValueType::Int), m_i(i) {} + ContextValue(const std::string& s) : m_contextValueType(ContextValueType::StdString), m_s(s) {} + ContextValue(std::string&& s) noexcept + : m_contextValueType(ContextValueType::UniquePtr), m_s(std::move(s)) + { + } + template < + class DerivedFromValueBase, + typename std:: + enable_if::value, int>::type + = 0> + ContextValue(std::unique_ptr&& p) noexcept + : m_contextValueType(ContextValueType::UniquePtr), m_p(std::move(p)) + { + } + + ContextValue(ContextValue&& other) noexcept : m_contextValueType(other.m_contextValueType) + { + switch (m_contextValueType) + { + case ContextValueType::Bool: + m_b = other.m_b; + break; + case ContextValueType::Int: + m_i = other.m_i; + break; + case ContextValueType::StdString: + ::new (&m_s) std::string(std::move(other.m_s)); + break; + case ContextValueType::UniquePtr: + ::new (&m_p) std::unique_ptr(std::move(other.m_p)); + break; + case ContextValueType::Undefined: + break; + } + } + + ~ContextValue() + { + switch (m_contextValueType) + { + case ContextValueType::StdString: + m_s.~basic_string(); + break; + case ContextValueType::UniquePtr: + m_p.~unique_ptr(); + break; + case ContextValueType::Bool: + case ContextValueType::Int: + case ContextValueType::Undefined: + break; + } + } + + ContextValue& operator=(const ContextValue& other) = delete; + + template const ExpectedType& Get() const noexcept; + + ContextValueType Alternative() const noexcept { return m_contextValueType; } + }; + + template <> inline const bool& ContextValue::Get() const noexcept + { + if (m_contextValueType != ContextValueType::Bool) + { + abort(); + } + return m_b; + } + + template <> inline const int& ContextValue::Get() const noexcept + { + if (m_contextValueType != ContextValueType::Int) + { + abort(); + } + return m_i; + } + + template <> inline const std::string& ContextValue::Get() const noexcept + { + if (m_contextValueType != ContextValueType::StdString) + { + abort(); + } + return m_s; + } + + template <> inline const std::unique_ptr& ContextValue::Get() const noexcept + { + if (m_contextValueType != ContextValueType::UniquePtr) + { + abort(); + } + return m_p; + } + + class Context { + public: + using time_point = std::chrono::system_clock::time_point; + + private: + struct ContextSharedState + { + std::shared_ptr Parent; + time_point CancelAt; + std::string Key; + ContextValue Value; + + explicit ContextSharedState() : CancelAt(time_point::max()) {} + + explicit ContextSharedState( + const std::shared_ptr& parent, + time_point cancelAt, + const std::string& key, + ContextValue&& value) + : Parent(parent), CancelAt(cancelAt), Key(key), Value(std::move(value)) + { + } + }; + + std::shared_ptr m_contextSharedState; + + explicit Context(std::shared_ptr impl) + : m_contextSharedState(std::move(impl)) + { + } + + public: + Context() : m_contextSharedState(std::make_shared()) {} + + Context& operator=(const Context&) = default; + + Context WithDeadline(time_point cancelWhen) + { + return Context{std::make_shared( + m_contextSharedState, cancelWhen, std::string(), ContextValue{})}; + } + + Context WithValue(const std::string& key, ContextValue&& value) + { + return Context{std::make_shared( + m_contextSharedState, time_point::max(), key, std::move(value))}; + } + + time_point CancelWhen(); + + const ContextValue& operator[](const std::string& key) + { + if (!key.empty()) + { + for (auto ptr = m_contextSharedState; ptr; ptr = ptr->Parent) + { + if (ptr->Key == key) + { + return ptr->Value; + } + } + } + + static ContextValue empty; + return empty; + } + + void Cancel() { m_contextSharedState->CancelAt = time_point::min(); } + }; + +}} // namespace Azure::Core \ No newline at end of file diff --git a/sdk/core/azure-core/inc/credentials/credentials.hpp b/sdk/core/azure-core/inc/credentials/credentials.hpp index 2b329eeab..9d7724775 100644 --- a/sdk/core/azure-core/inc/credentials/credentials.hpp +++ b/sdk/core/azure-core/inc/credentials/credentials.hpp @@ -3,6 +3,8 @@ #pragma once +#include "azure.hpp" + #include #include #include @@ -15,10 +17,7 @@ namespace Azure { namespace Core { namespace Credentials { } class Credential { - virtual void SetScopes(std::string const& scopes) - { - (void)scopes; - } + virtual void SetScopes(std::string const& scopes) { AZURE_UNREFERENCED_PARAMETER(scopes); } public: class Internal; @@ -55,9 +54,7 @@ namespace Azure { namespace Core { namespace Credentials { protected: TokenCredential() = default; - TokenCredential(TokenCredential const& other, int) : Credential(other) - { - } + TokenCredential(TokenCredential const& other, int) : Credential(other) {} void Init(TokenCredential const& other); virtual std::string GetToken(); diff --git a/sdk/core/azure-core/inc/http/curl/curl.hpp b/sdk/core/azure-core/inc/http/curl/curl.hpp new file mode 100644 index 000000000..943af009e --- /dev/null +++ b/sdk/core/azure-core/inc/http/curl/curl.hpp @@ -0,0 +1,90 @@ +#pragma once + +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "http/http.hpp" +#include "http/policy.hpp" + +#include +#include +#include + +namespace Azure { namespace Core { namespace Http { + + class CurlTransport : public HttpTransport { + private: + CurlTransport(const CurlTransport&) = delete; + CurlTransport& operator=(const CurlTransport&) = delete; + + // for every client instance, create a default response + std::unique_ptr m_response; + bool m_firstHeader; + + CURL* m_pCurl; + + static size_t WriteHeadersCallBack(void* contents, size_t size, size_t nmemb, void* userp); + static size_t WriteBodyCallBack(void* contents, size_t size, size_t nmemb, void* userp); + + // setHeaders() + CURLcode SetUrl(Request& request) + { + return curl_easy_setopt(m_pCurl, CURLOPT_URL, request.GetEncodedUrl().c_str()); + } + + CURLcode SetHeaders(Request& request) + { + auto headers = request.GetHeaders(); + if (headers.size() == 0) + { + return CURLE_OK; + } + + // creates a slist for bulding curl headers + struct curl_slist* headerList = NULL; + + // insert headers + for (auto header : headers) + { + // TODO: check result is not null or trow + headerList = curl_slist_append(headerList, (header.first + ":" + header.second).c_str()); + } + + // set all headers from slist + return curl_easy_setopt(m_pCurl, CURLOPT_HTTPHEADER, headerList); + } + + CURLcode SetWriteResponse() + { + auto settingUp = curl_easy_setopt(m_pCurl, CURLOPT_HEADERFUNCTION, WriteHeadersCallBack); + if (settingUp != CURLE_OK) + { + return settingUp; + } + settingUp = curl_easy_setopt(m_pCurl, CURLOPT_HEADERDATA, (void*)this); + if (settingUp != CURLE_OK) + { + return settingUp; + } + // TODO: set up cache size. user should be able to set it up + settingUp = curl_easy_setopt(m_pCurl, CURLOPT_WRITEFUNCTION, WriteBodyCallBack); + if (settingUp != CURLE_OK) + { + return settingUp; + } + + return curl_easy_setopt(m_pCurl, CURLOPT_WRITEDATA, (void*)this); + } + + CURLcode Perform(Context& context, Request& request); + + public: + CurlTransport(); + ~CurlTransport(); + + std::unique_ptr Send(Context& context, Request& request) override; + }; + +}}} // namespace Azure::Core::Http diff --git a/sdk/core/azure-core/inc/http/http.hpp b/sdk/core/azure-core/inc/http/http.hpp index a9489ddb4..2c2ce9743 100644 --- a/sdk/core/azure-core/inc/http/http.hpp +++ b/sdk/core/azure-core/inc/http/http.hpp @@ -12,6 +12,13 @@ namespace Azure { namespace Core { namespace Http { + enum class TransportKind + { + //TODO move this to Factory + Curl, + WinHttp + }; + // BodyStream is used to read data to/from a service class BodyStream { public: @@ -306,8 +313,4 @@ namespace Azure { namespace Core { namespace Http { Http::BodyStream* GetBodyStream() { return m_bodyStream; } }; - class Client { - public: - static std::unique_ptr Send(Request& request); - }; }}} // namespace Azure::Core::Http diff --git a/sdk/core/azure-core/inc/http/pipeline.hpp b/sdk/core/azure-core/inc/http/pipeline.hpp new file mode 100644 index 000000000..ad6acf039 --- /dev/null +++ b/sdk/core/azure-core/inc/http/pipeline.hpp @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "context.hpp" +#include "http.hpp" +#include "policy.hpp" +#include "transport.hpp" + +#include + +namespace Azure { namespace Core { namespace Http { + + class HttpPipeline { + protected: + std::vector> m_policies; + + public: + HttpPipeline(std::vector>& policies) + { + m_policies.reserve(policies.size()); + for (auto&& policy : policies) + { + m_policies.emplace_back(policy->Clone()); + } + } + + HttpPipeline(const HttpPipeline& other) + { + m_policies.reserve(other.m_policies.size()); + for (auto&& policy : m_policies) + { + m_policies.emplace_back(policy->Clone()); + } + } + + /** + * @brief Starts the pipeline + * @param ctx A cancellation token. Can also be used to provide overrides to individual policies + * @param request The request to be processed + * @return unique_ptr + */ + std::unique_ptr Send(Context& ctx, Request& request) const + { + return m_policies[0]->Send(ctx, request, NextHttpPolicy(0, &m_policies)); + } + }; +}}} // namespace Azure::Core::Http diff --git a/sdk/core/azure-core/inc/http/policy.hpp b/sdk/core/azure-core/inc/http/policy.hpp new file mode 100644 index 000000000..5dd944f41 --- /dev/null +++ b/sdk/core/azure-core/inc/http/policy.hpp @@ -0,0 +1,112 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "azure.hpp" +#include "context.hpp" +#include "http.hpp" +#include "transport.hpp" + +namespace Azure { namespace Core { namespace Http { + + class NextHttpPolicy; + + class HttpPolicy { + public: + // If we get a response that goes up the stack + // Any errors in the pipeline throws an exception + // At the top of the pipeline we might want to turn certain responses into exceptions + virtual std::unique_ptr Send( + Context& context, + Request& request, + NextHttpPolicy policy) const = 0; + virtual ~HttpPolicy() {} + virtual HttpPolicy* Clone() const = 0; + + protected: + HttpPolicy() = default; + HttpPolicy(const HttpPolicy& other) = default; + HttpPolicy(HttpPolicy&& other) = default; + HttpPolicy& operator=(const HttpPolicy& other) = default; + }; + + class NextHttpPolicy { + const std::size_t m_index; + const std::vector>* m_policies; + + public: + explicit NextHttpPolicy( + std::size_t index, + const std::vector>* policies) + : m_index(index), m_policies(policies) + { + } + + std::unique_ptr Send(Context& ctx, Request& req); + }; + + class TransportPolicy : public HttpPolicy { + private: + std::shared_ptr m_transport; + + public: + explicit TransportPolicy(std::shared_ptr transport) + : m_transport(std::move(transport)) + { + } + + HttpPolicy* Clone() const override { return new TransportPolicy(m_transport); } + + std::unique_ptr Send(Context& ctx, Request& request, NextHttpPolicy nextHttpPolicy) + const override + { + AZURE_UNREFERENCED_PARAMETER(nextHttpPolicy); + /** + * The transport policy is always the last policy. + * Call the transport and return + */ + return m_transport->Send(ctx, request); + } + }; + + struct RetryOptions + { + int16_t MaxRetries = 5; + int32_t RetryDelayMsec = 500; + }; + + class RetryPolicy : public HttpPolicy { + private: + RetryOptions m_retryOptions; + + public: + explicit RetryPolicy(RetryOptions options) : m_retryOptions(options) {} + + HttpPolicy* Clone() const override { return new RetryPolicy(m_retryOptions); } + + std::unique_ptr Send(Context& ctx, Request& request, NextHttpPolicy nextHttpPolicy) + const override + { + // Do real work here + // nextPolicy->Process(ctx, message, ) + return nextHttpPolicy.Send(ctx, request); + } + }; + + class RequestIdPolicy : public HttpPolicy { + + public: + explicit RequestIdPolicy() {} + + HttpPolicy* Clone() const override { return new RequestIdPolicy(); } + + std::unique_ptr Send(Context& ctx, Request& request, NextHttpPolicy nextHttpPolicy) + const override + { + // Do real work here + return nextHttpPolicy.Send(ctx, request); + } + }; + +}}} // namespace Azure::Core::Http diff --git a/sdk/core/azure-core/inc/http/transport.hpp b/sdk/core/azure-core/inc/http/transport.hpp new file mode 100644 index 000000000..aa6ee742a --- /dev/null +++ b/sdk/core/azure-core/inc/http/transport.hpp @@ -0,0 +1,28 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "context.hpp" +#include "http.hpp" + +namespace Azure { namespace Core { namespace Http { + + class HttpTransport { + public: + // If we get a response that goes up the stack + // Any errors in the pipeline throws an exception + // At the top of the pipeline we might want to turn certain responses into exceptions + + //TODO - Should this be const + virtual std::unique_ptr Send(Context& context, Request& request) = 0; + virtual ~HttpTransport() {} + + protected: + HttpTransport() = default; + HttpTransport(const HttpTransport& other) = default; + HttpTransport(HttpTransport&& other) = default; + HttpTransport& operator=(const HttpTransport& other) = default; + }; + +}}} // namespace Azure::Core::Http diff --git a/sdk/core/azure-core/inc/http/winhttp/win_http_client.hpp b/sdk/core/azure-core/inc/http/winhttp/win_http_client.hpp new file mode 100644 index 000000000..867a237d5 --- /dev/null +++ b/sdk/core/azure-core/inc/http/winhttp/win_http_client.hpp @@ -0,0 +1,26 @@ +#pragma once + +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#pragma once + +#include "http/http.hpp" +#include "http/policy.hpp" + +#include +#include + +namespace Azure { namespace Core { namespace Http { + + class WinHttpTansport : public HttpTransport { + private: + + public: + WinHttpTansport(); + ~WinHttpTansport(); + + virtual std::unique_ptr Send(Context& context, Request& request) ; + }; + +}}} // namespace Azure::Core::Http diff --git a/sdk/core/azure-core/src/context.cpp b/sdk/core/azure-core/src/context.cpp new file mode 100644 index 000000000..4e1918ba0 --- /dev/null +++ b/sdk/core/azure-core/src/context.cpp @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include + +using namespace Azure::Core; +using time_point = std::chrono::system_clock::time_point; + +Context& GetApplicationContext() +{ + static Context ctx; + return ctx; +} + +time_point Context::CancelWhen() +{ + auto result = time_point::max(); + for (auto ptr = m_contextSharedState; ptr; ptr = ptr->Parent) + { + if (result > ptr->CancelAt) + { + result = ptr->CancelAt; + } + } + + return result; +} diff --git a/sdk/core/azure-core/src/credentials/credentials.cpp b/sdk/core/azure-core/src/credentials/credentials.cpp index fe34aa8e8..da59354de 100644 --- a/sdk/core/azure-core/src/credentials/credentials.cpp +++ b/sdk/core/azure-core/src/credentials/credentials.cpp @@ -98,8 +98,8 @@ void ClientSecretCredential::RefreshToken( std::chrono::system_clock::time_point& newExpiration) { // TODO: get token using scopes, tenantId, clientId, and clientSecretId. - (void)newTokenString; - (void)newExpiration; + AZURE_UNREFERENCED_PARAMETER(newTokenString); + AZURE_UNREFERENCED_PARAMETER(newExpiration); } ClientSecretCredential::ClientSecretCredential( diff --git a/sdk/platform/http_client/curl/src/curl_client.cpp b/sdk/core/azure-core/src/http/curl/curl_transport.cpp similarity index 78% rename from sdk/platform/http_client/curl/src/curl_client.cpp rename to sdk/core/azure-core/src/http/curl/curl_transport.cpp index 7483876ba..6312ab375 100644 --- a/sdk/platform/http_client/curl/src/curl_client.cpp +++ b/sdk/core/azure-core/src/http/curl/curl_transport.cpp @@ -1,52 +1,51 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// SPDX-License-Identifier: MIT -#include -#include +#include "azure.hpp" +#include "http/curl/curl.hpp" +#include "http/http.hpp" -#include -#include +#include using namespace Azure::Core::Http; -using namespace std; -std::unique_ptr CurlClient::Send() +CurlTransport::CurlTransport() : HttpTransport() { m_pCurl = curl_easy_init(); } + +CurlTransport::~CurlTransport() { curl_easy_cleanup(m_pCurl); } + +std::unique_ptr CurlTransport::Send(Context& context, Request& request) { - auto performing = Perform(); + auto performing = Perform(context, request); if (performing != CURLE_OK) { switch (performing) { - case CURLE_COULDNT_RESOLVE_HOST: - { + case CURLE_COULDNT_RESOLVE_HOST: { throw Azure::Core::Http::CouldNotResolveHostException(); } - case CURLE_WRITE_ERROR: - { + case CURLE_WRITE_ERROR: { throw Azure::Core::Http::ErrorWhileWrittingResponse(); } - default: - { + default: { throw Azure::Core::Http::TransportException(); } } } - - return move(this->m_response); + return std::move(m_response); } -CURLcode CurlClient::Perform() +CURLcode CurlTransport::Perform(Context& context, Request& request) { + AZURE_UNREFERENCED_PARAMETER(context); + m_firstHeader = true; - auto settingUp = SetUrl(); + auto settingUp = SetUrl(request); if (settingUp != CURLE_OK) { return settingUp; } - settingUp = SetHeaders(); + settingUp = SetHeaders(request); if (settingUp != CURLE_OK) { return settingUp; @@ -83,7 +82,7 @@ static std::unique_ptr ParseAndSetFirstHeader(std::string const& heade // So this memory gets delegated outside Curl Transport as a shared ptr so memory will be // eventually released return std::make_unique( - majorVersion, minorVersion, HttpStatusCode(statusCode), reasonPhrase); + (uint16_t)majorVersion, (uint16_t)minorVersion, HttpStatusCode(statusCode), reasonPhrase); } static void ParseHeader(std::string const& header, std::unique_ptr& response) @@ -110,13 +109,13 @@ static void ParseHeader(std::string const& header, std::unique_ptr& re } // Callback function for curl. This is called for every header that curl get from network -size_t CurlClient::WriteHeadersCallBack(void* contents, size_t size, size_t nmemb, void* userp) +size_t CurlTransport::WriteHeadersCallBack(void* contents, size_t size, size_t nmemb, void* userp) { // No need to check for overflow, Curl already allocated this size internally for contents size_t const expected_size = size * nmemb; // cast client - CurlClient* client = static_cast(userp); + CurlTransport* client = static_cast(userp); // convert response to standard string std::string const& response = std::string((char*)contents, expected_size); @@ -140,13 +139,13 @@ size_t CurlClient::WriteHeadersCallBack(void* contents, size_t size, size_t nmem // callback function for libcurl. It would be called as many times as need to ready a body from // network -size_t CurlClient::WriteBodyCallBack(void* contents, size_t size, size_t nmemb, void* userp) +size_t CurlTransport::WriteBodyCallBack(void* contents, size_t size, size_t nmemb, void* userp) { // No need to check for overflow, Curl already allocated this size internally for contents size_t const expected_size = size * nmemb; // cast client - CurlClient* client = static_cast(userp); + CurlTransport* client = static_cast(userp); if (client->m_response != nullptr) // only if a response has been created { diff --git a/sdk/core/azure-core/src/http/policy.cpp b/sdk/core/azure-core/src/http/policy.cpp new file mode 100644 index 000000000..3a812e855 --- /dev/null +++ b/sdk/core/azure-core/src/http/policy.cpp @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include +#include + +using namespace Azure::Core::Http; + +std::unique_ptr NextHttpPolicy::Send(Context& ctx, Request& req) +{ + if (m_policies == nullptr) + throw; + + if (m_index == m_policies->size() - 1) + { + //All the policies have run without running a transport policy + throw; + } + + return (*m_policies)[m_index + 1]->Send(ctx, req, NextHttpPolicy{m_index + 1, m_policies}); +} diff --git a/sdk/core/azure-core/src/http/winhttp/win_http_transport.cpp b/sdk/core/azure-core/src/http/winhttp/win_http_transport.cpp new file mode 100644 index 000000000..75a260d92 --- /dev/null +++ b/sdk/core/azure-core/src/http/winhttp/win_http_transport.cpp @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +#include "azure.hpp" +#include "http/http.hpp" +#include "http/winhttp/win_http_client.hpp" + +#include + +using namespace Azure::Core::Http; + +WinHttpTansport::WinHttpTansport() : + HttpTransport() +{ +} + +WinHttpTansport::~WinHttpTansport() {} + + +std::unique_ptr WinHttpTansport::Send(Context& context, Request& request) +{ + AZURE_UNREFERENCED_PARAMETER(context); + AZURE_UNREFERENCED_PARAMETER(request); + + throw; +} diff --git a/sdk/platform/http_client/curl/CMakeLists.txt b/sdk/platform/http_client/curl/CMakeLists.txt deleted file mode 100644 index 24a266705..000000000 --- a/sdk/platform/http_client/curl/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# SPDX-License-Identifier: MIT - -if (BUILD_CURL_TRANSPORT) - -cmake_minimum_required (VERSION 3.12) -set(TARGET_NAME "azure-transport-curl") - -project(${TARGET_NAME} LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED True) - -find_package(CURL CONFIG) -if(NOT CURL_FOUND) - find_package(CURL REQUIRED) -endif() - -add_library ( - ${TARGET_NAME} STATIC - src/azure_transport_curl - src/curl_client - ) - -target_include_directories (${TARGET_NAME} PRIVATE $ $) - -# make sure that users can consume the project as a library. -add_library (Azure::curl ALIAS ${TARGET_NAME}) - -target_include_directories(${TARGET_NAME} PUBLIC ${CURL_INCLUDE_DIRS}) -target_link_libraries(${TARGET_NAME} PRIVATE azure-core CURL::libcurl) - -endif() diff --git a/sdk/platform/http_client/curl/LICENSE b/sdk/platform/http_client/curl/LICENSE deleted file mode 100644 index 51b6a76e5..000000000 --- a/sdk/platform/http_client/curl/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - MIT License - - Copyright (c) Microsoft Corporation. All rights reserved. - - 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. diff --git a/sdk/platform/http_client/curl/inc/curl_client.hpp b/sdk/platform/http_client/curl/inc/curl_client.hpp deleted file mode 100644 index e3c3e1cb2..000000000 --- a/sdk/platform/http_client/curl/inc/curl_client.hpp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) `Microsoft Corporation. All rights reserved. -// SPDX-License-Identifier: MIT - -#pragma once - -#include -#include -#include - -class CurlClient { -private: - CurlClient(const CurlClient&) = delete; - CurlClient& operator=(const CurlClient&) = delete; - - Azure::Core::Http::Request& m_request; - // for every client instance, create a default response - std::unique_ptr m_response; - bool m_firstHeader; - - CURL* m_pCurl; - - static size_t WriteHeadersCallBack(void* contents, size_t size, size_t nmemb, void* userp); - static size_t WriteBodyCallBack(void* contents, size_t size, size_t nmemb, void* userp); - - // setHeaders() - CURLcode SetUrl() - { - return curl_easy_setopt(m_pCurl, CURLOPT_URL, this->m_request.GetEncodedUrl().c_str()); - } - - CURLcode SetHeaders() - { - auto headers = this->m_request.GetHeaders(); - if (headers.size() == 0) - { - return CURLE_OK; - } - - // creates a slist for bulding curl headers - struct curl_slist* headerList = NULL; - - // insert headers - for (auto header : headers) - { - // TODO: check result is not null or trow - headerList = curl_slist_append(headerList, (header.first + ":" + header.second).c_str()); - } - - // set all headers from slist - return curl_easy_setopt(m_pCurl, CURLOPT_HTTPHEADER, headerList); - } - - CURLcode SetWriteResponse() - { - auto settingUp = curl_easy_setopt(m_pCurl, CURLOPT_HEADERFUNCTION, WriteHeadersCallBack); - if (settingUp != CURLE_OK) - { - return settingUp; - } - settingUp = curl_easy_setopt(m_pCurl, CURLOPT_HEADERDATA, (void*)this); - if (settingUp != CURLE_OK) - { - return settingUp; - } - // TODO: set up cache size. user should be able to set it up - settingUp = curl_easy_setopt(m_pCurl, CURLOPT_WRITEFUNCTION, WriteBodyCallBack); - if (settingUp != CURLE_OK) - { - return settingUp; - } - - return curl_easy_setopt(m_pCurl, CURLOPT_WRITEDATA, (void*)this); - } - - CURLcode Perform(); - -public: - CurlClient(Azure::Core::Http::Request& request) : m_request(request) - { - m_pCurl = curl_easy_init(); - } - // client curl struct on destruct - ~CurlClient() { curl_easy_cleanup(m_pCurl); } - - std::unique_ptr Send(); -}; diff --git a/sdk/platform/http_client/curl/src/azure_transport_curl.cpp b/sdk/platform/http_client/curl/src/azure_transport_curl.cpp deleted file mode 100644 index 93da784cf..000000000 --- a/sdk/platform/http_client/curl/src/azure_transport_curl.cpp +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// SPDX-License-Identifier: MIT - -#include -#include - -using namespace Azure::Core::Http; - -// implement send method -std::unique_ptr Client::Send(Request& request) -{ - CurlClient client(request); - // return request response - return client.Send(); -} diff --git a/sdk/platform/http_client/nohttp/CMakeLists.txt b/sdk/platform/http_client/nohttp/CMakeLists.txt deleted file mode 100644 index 7d29f02ef..000000000 --- a/sdk/platform/http_client/nohttp/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# SPDX-License-Identifier: MIT - -if (NOT BUILD_CURL_TRANSPORT) - -cmake_minimum_required (VERSION 3.12) -set(TARGET_NAME "azure-transport-nothhp") - -project(${TARGET_NAME} LANGUAGES CXX) -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED True) - -add_library ( - ${TARGET_NAME} STATIC - src/azure_transport_nohttp.cpp -) - -target_link_libraries(${TARGET_NAME} PRIVATE azure-core) - -# make sure that users can consume the project as a library. -add_library (Azure::nohttp ALIAS ${TARGET_NAME}) - -endif() diff --git a/sdk/platform/http_client/nohttp/LICENSE b/sdk/platform/http_client/nohttp/LICENSE deleted file mode 100644 index 51b6a76e5..000000000 --- a/sdk/platform/http_client/nohttp/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ - MIT License - - Copyright (c) Microsoft Corporation. All rights reserved. - - 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. diff --git a/sdk/platform/http_client/nohttp/src/azure_transport_nohttp.cpp b/sdk/platform/http_client/nohttp/src/azure_transport_nohttp.cpp deleted file mode 100644 index e04372a48..000000000 --- a/sdk/platform/http_client/nohttp/src/azure_transport_nohttp.cpp +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// SPDX-License-Identifier: MIT - -#include -#include - -using namespace Azure::Core::Http; - -// implement send method -std::unique_ptr Client::Send(Request& request) -{ - (void)request; - throw; -} diff --git a/sdk/samples/http_client/curl/CMakeLists.txt b/sdk/samples/http_client/curl/CMakeLists.txt index cae181cc1..92db8d9a4 100644 --- a/sdk/samples/http_client/curl/CMakeLists.txt +++ b/sdk/samples/http_client/curl/CMakeLists.txt @@ -15,6 +15,6 @@ add_executable ( src/azure_core_with_curl ) -target_link_libraries(${TARGET_NAME} PRIVATE azure-core azure-transport-curl) +target_link_libraries(${TARGET_NAME} PRIVATE azure-core) endif() diff --git a/sdk/samples/http_client/curl/src/azure_core_with_curl.cpp b/sdk/samples/http_client/curl/src/azure_core_with_curl.cpp index ba6e950c9..20ea30c7a 100644 --- a/sdk/samples/http_client/curl/src/azure_core_with_curl.cpp +++ b/sdk/samples/http_client/curl/src/azure_core_with_curl.cpp @@ -7,10 +7,14 @@ */ #include +#include "http/pipeline.hpp" +#include + #include #include using namespace Azure::Core; +using namespace Azure::Core::Http; using namespace std; int main() @@ -18,14 +22,29 @@ int main() string host("https://httpbin.org/get"); cout << "testing curl from transport" << endl << "Host: " << host << endl; - auto request = Http::Request(Http::HttpMethod::Get, host); - request.AddHeader("one", "header"); - request.AddHeader("other", "header2"); - request.AddHeader("header", "value"); - try { - std::shared_ptr response = Http::Client::Send(request); + auto request = Http::Request(Http::HttpMethod::Get, host); + request.AddHeader("one", "header"); + request.AddHeader("other", "header2"); + request.AddHeader("header", "value"); + + //Create the Transport + std::shared_ptr transport = std::make_unique(); + + std::vector> policies; + policies.push_back(std::make_unique()); + + RetryOptions retryOptions; + policies.push_back(std::make_unique(retryOptions)); + + // Add the transport policy + policies.push_back(std::make_unique(std::move(transport))); + + auto httpPipeline = Http::HttpPipeline(policies); + + auto context = Context(); + std::shared_ptr response = httpPipeline.Send(context, request); if (response == nullptr) { @@ -34,7 +53,7 @@ int main() } cout << static_cast::type>( - response->GetStatusCode()) + response->GetStatusCode()) << endl; cout << response->GetReasonPhrase() << endl; cout << "headers:" << endl;