HttpPipeline and HttpPolicies (#63)

* HttpPipeline
This commit is contained in:
Rick Winter 2020-05-29 10:57:10 -07:00 committed by GitHub
parent 0b1eb288db
commit 602f75744a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 702 additions and 292 deletions

View File

@ -1,5 +1,5 @@
Language: Cpp
BasedOnStyle: Microsoft
BasedOnStyle: LLVM
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false

View File

@ -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

View File

@ -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": []
}
]
}
"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": []
}
]
}

View File

@ -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:

View File

@ -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 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc> $<INSTALL_INTERFACE:include/az_core>)
target_include_directories (${TARGET_NAME} PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc> $<INSTALL_INTERFACE:include/az_core>)
# 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)

View File

@ -5,6 +5,10 @@
#include <internal/contract.hpp>
#define AZURE_UNREFERENCED_PARAMETER(x) ((void) (x));
namespace Azure { namespace Core {
}} // namespace Azure::Core

View File

@ -0,0 +1,210 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <chrono>
#include <memory>
#include <string>
#include <type_traits>
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<ValueBase> 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<std::is_convertible<DerivedFromValueBase*, ValueBase*>::value, int>::type
= 0>
ContextValue(std::unique_ptr<DerivedFromValueBase>&& 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<ValueBase>(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<ValueBase>();
break;
case ContextValueType::Bool:
case ContextValueType::Int:
case ContextValueType::Undefined:
break;
}
}
ContextValue& operator=(const ContextValue& other) = delete;
template <class ExpectedType> 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<ValueBase>& 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<ContextSharedState> Parent;
time_point CancelAt;
std::string Key;
ContextValue Value;
explicit ContextSharedState() : CancelAt(time_point::max()) {}
explicit ContextSharedState(
const std::shared_ptr<ContextSharedState>& parent,
time_point cancelAt,
const std::string& key,
ContextValue&& value)
: Parent(parent), CancelAt(cancelAt), Key(key), Value(std::move(value))
{
}
};
std::shared_ptr<ContextSharedState> m_contextSharedState;
explicit Context(std::shared_ptr<ContextSharedState> impl)
: m_contextSharedState(std::move(impl))
{
}
public:
Context() : m_contextSharedState(std::make_shared<ContextSharedState>()) {}
Context& operator=(const Context&) = default;
Context WithDeadline(time_point cancelWhen)
{
return Context{std::make_shared<ContextSharedState>(
m_contextSharedState, cancelWhen, std::string(), ContextValue{})};
}
Context WithValue(const std::string& key, ContextValue&& value)
{
return Context{std::make_shared<ContextSharedState>(
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

View File

@ -3,6 +3,8 @@
#pragma once
#include "azure.hpp"
#include <chrono>
#include <memory>
#include <mutex>
@ -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();

View File

@ -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 <curl/curl.h>
#include <type_traits>
#include <vector>
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<Azure::Core::Http::Response> 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<Response> Send(Context& context, Request& request) override;
};
}}} // namespace Azure::Core::Http

View File

@ -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<Response> Send(Request& request);
};
}}} // namespace Azure::Core::Http

View File

@ -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 <vector>
namespace Azure { namespace Core { namespace Http {
class HttpPipeline {
protected:
std::vector<std::unique_ptr<HttpPolicy>> m_policies;
public:
HttpPipeline(std::vector<std::unique_ptr<HttpPolicy>>& 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<Response>
*/
std::unique_ptr<Response> Send(Context& ctx, Request& request) const
{
return m_policies[0]->Send(ctx, request, NextHttpPolicy(0, &m_policies));
}
};
}}} // namespace Azure::Core::Http

View File

@ -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<Response> 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<std::unique_ptr<HttpPolicy>>* m_policies;
public:
explicit NextHttpPolicy(
std::size_t index,
const std::vector<std::unique_ptr<HttpPolicy>>* policies)
: m_index(index), m_policies(policies)
{
}
std::unique_ptr<Response> Send(Context& ctx, Request& req);
};
class TransportPolicy : public HttpPolicy {
private:
std::shared_ptr<HttpTransport> m_transport;
public:
explicit TransportPolicy(std::shared_ptr<HttpTransport> transport)
: m_transport(std::move(transport))
{
}
HttpPolicy* Clone() const override { return new TransportPolicy(m_transport); }
std::unique_ptr<Response> 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<Response> 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<Response> Send(Context& ctx, Request& request, NextHttpPolicy nextHttpPolicy)
const override
{
// Do real work here
return nextHttpPolicy.Send(ctx, request);
}
};
}}} // namespace Azure::Core::Http

View File

@ -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<Response> 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

View File

@ -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 <type_traits>
#include <vector>
namespace Azure { namespace Core { namespace Http {
class WinHttpTansport : public HttpTransport {
private:
public:
WinHttpTansport();
~WinHttpTansport();
virtual std::unique_ptr<Response> Send(Context& context, Request& request) ;
};
}}} // namespace Azure::Core::Http

View File

@ -0,0 +1,27 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <context.hpp>
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;
}

View File

@ -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(

View File

@ -1,52 +1,51 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <curl_client.hpp>
#include <http/http.hpp>
#include "azure.hpp"
#include "http/curl/curl.hpp"
#include "http/http.hpp"
#include <iostream>
#include <memory>
#include <string>
using namespace Azure::Core::Http;
using namespace std;
std::unique_ptr<Response> CurlClient::Send()
CurlTransport::CurlTransport() : HttpTransport() { m_pCurl = curl_easy_init(); }
CurlTransport::~CurlTransport() { curl_easy_cleanup(m_pCurl); }
std::unique_ptr<Response> 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<Response> 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<Response>(
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>& response)
@ -110,13 +109,13 @@ static void ParseHeader(std::string const& header, std::unique_ptr<Response>& 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<CurlClient*>(userp);
CurlTransport* client = static_cast<CurlTransport*>(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<CurlClient*>(userp);
CurlTransport* client = static_cast<CurlTransport*>(userp);
if (client->m_response != nullptr) // only if a response has been created
{

View File

@ -0,0 +1,21 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <http/http.hpp>
#include <http/policy.hpp>
using namespace Azure::Core::Http;
std::unique_ptr<Response> 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});
}

View File

@ -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 <string>
using namespace Azure::Core::Http;
WinHttpTansport::WinHttpTansport() :
HttpTransport()
{
}
WinHttpTansport::~WinHttpTansport() {}
std::unique_ptr<Response> WinHttpTansport::Send(Context& context, Request& request)
{
AZURE_UNREFERENCED_PARAMETER(context);
AZURE_UNREFERENCED_PARAMETER(request);
throw;
}

View File

@ -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 $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/inc> $<INSTALL_INTERFACE:include/azure_curl>)
# 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()

View File

@ -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.

View File

@ -1,86 +0,0 @@
// Copyright (c) `Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#pragma once
#include <curl/curl.h>
#include <http/http.hpp>
#include <memory>
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<Azure::Core::Http::Response> 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<Azure::Core::Http::Response> Send();
};

View File

@ -1,15 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <curl_client.hpp>
#include <http/http.hpp>
using namespace Azure::Core::Http;
// implement send method
std::unique_ptr<Response> Client::Send(Request& request)
{
CurlClient client(request);
// return request response
return client.Send();
}

View File

@ -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()

View File

@ -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.

View File

@ -1,14 +0,0 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include <http/http.hpp>
#include <memory>
using namespace Azure::Core::Http;
// implement send method
std::unique_ptr<Response> Client::Send(Request& request)
{
(void)request;
throw;
}

View File

@ -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()

View File

@ -7,10 +7,14 @@
*/
#include <http/http.hpp>
#include "http/pipeline.hpp"
#include <http/curl/curl.hpp>
#include <iostream>
#include <memory>
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<Http::Response> 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<HttpTransport> transport = std::make_unique<CurlTransport>();
std::vector<std::unique_ptr<HttpPolicy>> policies;
policies.push_back(std::make_unique<RequestIdPolicy>());
RetryOptions retryOptions;
policies.push_back(std::make_unique<RetryPolicy>(retryOptions));
// Add the transport policy
policies.push_back(std::make_unique<TransportPolicy>(std::move(transport)));
auto httpPipeline = Http::HttpPipeline(policies);
auto context = Context();
std::shared_ptr<Http::Response> response = httpPipeline.Send(context, request);
if (response == nullptr)
{
@ -34,7 +53,7 @@ int main()
}
cout << static_cast<typename std::underlying_type<Http::HttpStatusCode>::type>(
response->GetStatusCode())
response->GetStatusCode())
<< endl;
cout << response->GetReasonPhrase() << endl;
cout << "headers:" << endl;