Add doxygen comments (#611)

This commit is contained in:
Anton Kolesnyk 2020-09-08 11:00:06 -07:00 committed by GitHub
parent a4b1677c38
commit 949d143284
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 1165 additions and 160 deletions

View File

@ -1,11 +1,19 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief Common definitions.
*/
#pragma once
#include <azure/core/internal/contract.hpp>
#include <string>
/**
* @brief Used in implementations to mark an unreferenced function parameter.
*/
#define AZURE_UNREFERENCED_PARAMETER(x) ((void)(x));
namespace Azure { namespace Core { namespace Details {

View File

@ -1,6 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief Context for canceling long running operations.
*/
#pragma once
#include <atomic>
@ -12,23 +17,30 @@
namespace Azure { namespace Core {
/**
* @ A base abstract class for the `std::unique_ptr` value representation in #ContextValue.
*/
struct ValueBase
{
virtual ~ValueBase() {}
};
/*
* @brief ContextValue exists as a substitute for variant which isn't available until C++17
/**
* @brief Represents a value that is associated with context.
* @rmark Exists as a substitute for variant which isn't available until C++17.
*/
class ContextValue {
public:
/**
* @brief A type of context value.
*/
enum class ContextValueType
{
Undefined,
Bool,
Int,
StdString,
UniquePtr
Undefined, ///< Undefined.
Bool, ///< `bool`.
Int, ///< `int`.
StdString, ///< `std::string`
UniquePtr ///< `std::unique_ptr<ValueBase>`
};
private:
@ -47,26 +59,64 @@ namespace Azure { namespace Core {
#pragma warning(disable : 26495)
#endif
/**
* @brief Create a context value with undefined value type.
*/
ContextValue() noexcept : m_contextValueType(ContextValueType::Undefined) {}
/**
* @brief Create a context value with `bool` value type.
*
* @param b Boolean value.
*/
ContextValue(bool b) noexcept : m_contextValueType(ContextValueType::Bool), m_b(b) {}
/**
* @brief Create a context value with `int` value type.
*
* @param i Integer value.
*/
ContextValue(int i) noexcept : m_contextValueType(ContextValueType::Int), m_i(i) {}
/**
* @brief Create a context value with `std::string` value type.
*
* @param s String value.
*/
ContextValue(const std::string& s) : m_contextValueType(ContextValueType::StdString), m_s(s) {}
ContextValue(const char* s)
: m_contextValueType(ContextValueType::StdString), m_s(s)
{
}
/**
* @brief Create a context value with `std::string` value type.
*
* @param s String value represented as 0-terminated C-string.
*/
ContextValue(const char* s) : m_contextValueType(ContextValueType::StdString), m_s(s) {}
/**
* @brief Create a context value with `std::string` value type, using `std::move` semantics.
*
* @param s String value.
*/
ContextValue(std::string&& s)
: m_contextValueType(ContextValueType::StdString), m_s(std::move(s))
{
}
/**
* @brief Create a context value with `std::unique_ptr<ValueBase>` value type.
*
* @param p Smart pointer to #ValueBase.
*/
ContextValue(std::unique_ptr<ValueBase>&& p) noexcept
: m_contextValueType(ContextValueType::UniquePtr), m_p(std::move(p))
{
}
/**
* @brief Move constructor.
*
* @param other An rvalue reference to another instance of #ContextValue.
*/
ContextValue(ContextValue&& other) noexcept : m_contextValueType(other.m_contextValueType)
{
switch (m_contextValueType)
@ -91,6 +141,9 @@ namespace Azure { namespace Core {
#pragma warning(pop)
#endif
/**
* @brief #ContextValue destructor.
*/
~ContextValue()
{
switch (m_contextValueType)
@ -110,11 +163,22 @@ namespace Azure { namespace Core {
ContextValue& operator=(const ContextValue& other) = delete;
/**
* @brief Get the context value.
*
* @tparam Expected The type of value to get.
*/
template <class ExpectedType> const ExpectedType& Get() const noexcept;
/**
* @brief Get the context value type.
*/
ContextValueType Alternative() const noexcept { return m_contextValueType; }
};
/**
* @brief Get the context value type as boolean (`bool`).
*/
template <> inline const bool& ContextValue::Get() const noexcept
{
if (m_contextValueType != ContextValueType::Bool)
@ -124,6 +188,9 @@ namespace Azure { namespace Core {
return m_b;
}
/**
* @brief Get the context value type as integer (`int`).
*/
template <> inline const int& ContextValue::Get() const noexcept
{
if (m_contextValueType != ContextValueType::Int)
@ -133,6 +200,9 @@ namespace Azure { namespace Core {
return m_i;
}
/**
* @brief Get the context value type as string (`std::string`).
*/
template <> inline const std::string& ContextValue::Get() const noexcept
{
if (m_contextValueType != ContextValueType::StdString)
@ -142,6 +212,9 @@ namespace Azure { namespace Core {
return m_s;
}
/**
* @brief Get the context value type as a pointer (`std::unique_ptr<ValueBase>`).
*/
template <> inline const std::unique_ptr<ValueBase>& ContextValue::Get() const noexcept
{
if (m_contextValueType != ContextValueType::UniquePtr)
@ -151,8 +224,14 @@ namespace Azure { namespace Core {
return m_p;
}
/**
* @brief A context is a node within a tree that represents expiration times and key/value pairs.
*/
class Context {
public:
/**
* @brief A type used to provide a point in time for context expiration.
*/
using time_point = std::chrono::system_clock::time_point;
private:
@ -195,24 +274,58 @@ namespace Azure { namespace Core {
}
public:
/**
* @brief Construct a new context with no expiration, and no value associated.
*/
Context() : m_contextSharedState(std::make_shared<ContextSharedState>()) {}
/**
* @brief Copy constructor.
*/
Context& operator=(const Context&) = default;
/**
* @brief Create a context with expiration.
*
* @param cancelWhen A point in time after which a context expires.
*
* @return A child context with expiration.
*/
Context WithDeadline(time_point cancelWhen) const
{
return Context{std::make_shared<ContextSharedState>(
m_contextSharedState, cancelWhen, std::string(), ContextValue{})};
}
/**
* @brief Create a context without an expiration, but with \p key and \p value associated with
* it.
*
* @param key A key to associate with this context.
* @param value A value to associate with this context.
*
* @return A child context with no expiration and the \p key and \p value associated with it.
*/
Context WithValue(const std::string& key, ContextValue&& value) const
{
return Context{std::make_shared<ContextSharedState>(
m_contextSharedState, time_point::max(), key, std::move(value))};
}
/**
* @
*/
time_point CancelWhen() const;
/**
* @brief Get a value associated with a \p key parameter within this context or the branch of
* contexts this context belongs to.
*
* @param key A key assiciated with a context to find.
*
* @return A value associated with the context found; an empty value if a specific value can't
* be found.
*/
const ContextValue& operator[](const std::string& key) const
{
if (!key.empty())
@ -230,6 +343,15 @@ namespace Azure { namespace Core {
return empty;
}
/**
* @brief Check whether the context has a key matching \p key parameter in it itself, or in the
* branch the context belongs to.
*
* @param key A key assiciated with a context to find.
*
* @return `true` if this context, or the tree branch this context belongs to has a \p key
* associsted with it. `false` otherwise.
*/
bool HasKey(const std::string& key) const
{
if (!key.empty())
@ -245,12 +367,18 @@ namespace Azure { namespace Core {
return false;
}
/**
* @brief Cancels the context.
*/
void Cancel()
{
m_contextSharedState->CancelAtMsecSinceEpoch
= ContextSharedState::ToMsecSinceEpoch(time_point::min());
}
/**
* @brief Throw an exception if the context was canceled.
*/
void ThrowIfCanceled() const
{
if (CancelWhen() < std::chrono::system_clock::now())
@ -261,5 +389,8 @@ namespace Azure { namespace Core {
}
};
/**
* @brief Get the application context (root).
*/
Context& GetApplicationContext();
}} // namespace Azure::Core

View File

@ -1,6 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief Credentials used for authentication with many (not all) Azure SDK client libraries.
*/
#pragma once
#include "azure/core/context.hpp"
@ -14,17 +19,37 @@
namespace Azure { namespace Core { namespace Credentials {
/**
* @brief Represents an access token.
*/
struct AccessToken
{
/**
* @brief Token string.
*/
std::string Token;
/**
* @brief Token expiration.
*/
std::chrono::system_clock::time_point ExpiresOn;
};
/**
* @brief Token credential.
*/
class TokenCredential {
public:
/**
* @brief Get an authentication token.
*
* @param context #Context so that operation can be canceled.
* @param scopes Authentication scopes.
*/
virtual AccessToken GetToken(Context const& context, std::vector<std::string> const& scopes)
const = 0;
/// Destructor.
virtual ~TokenCredential() = default;
protected:
@ -35,6 +60,10 @@ namespace Azure { namespace Core { namespace Credentials {
void operator=(TokenCredential const&) = delete;
};
/**
* @brief This class is used by Azure SDK clients to authenticate with the Azure service using a
* tenant ID, client ID and client secret.
*/
class ClientSecretCredential : public TokenCredential {
private:
static std::string const g_aadGlobalAuthority;
@ -45,6 +74,19 @@ namespace Azure { namespace Core { namespace Credentials {
std::string m_authority;
public:
/**
* @brief Construct a Client Secret credential.
*
* @param tenantId Tenant ID.
* @param clientId Client ID.
* @param clientSecret Client Secret.
* @param authority Authentication authority URL to set. If omitted, initializes credential with
* default authority (Azure AD global authority - "https://login.microsoftonline.com/").
*
* @note Example of a \p authority string: "https://login.microsoftonline.us/". See national
* clouds' Azure AD authentication endpoints:
* https://docs.microsoft.com/en-us/azure/active-directory/develop/authentication-national-cloud.
*/
explicit ClientSecretCredential(
std::string tenantId,
std::string clientId,
@ -59,15 +101,24 @@ namespace Azure { namespace Core { namespace Credentials {
const override;
};
/**
* @brief An exception that gets thrown when authentication error occurs.
*/
class AuthenticationException : public std::runtime_error {
public:
explicit AuthenticationException(std::string const& msg) : std::runtime_error(msg) {}
};
/**
* @brief An environment credential.
*/
class EnvironmentCredential : public TokenCredential {
std::unique_ptr<TokenCredential> m_credentialImpl;
public:
/**
* Constructs an environment credential.
*/
explicit EnvironmentCredential();
AccessToken GetToken(Context const& context, std::vector<std::string> const& scopes)

View File

@ -1,6 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief Authentication policies.
*/
#pragma once
#include <azure/core/credentials/credentials.hpp>
@ -12,6 +17,9 @@
namespace Azure { namespace Core { namespace Credentials { namespace Policy {
/**
* @brief Bearer Token authentication policy.
*/
class BearerTokenAuthenticationPolicy : public Http::HttpPolicy {
private:
std::shared_ptr<TokenCredential const> const m_credential;
@ -24,6 +32,12 @@ namespace Azure { namespace Core { namespace Credentials { namespace Policy {
void operator=(BearerTokenAuthenticationPolicy const&) = delete;
public:
/**
* @brief Construct a Bearer Token authentication policy with single authentication scope.
*
* @param credential A #TokenCredential to use with this policy.
* @param scope Authentication scope.
*/
explicit BearerTokenAuthenticationPolicy(
std::shared_ptr<TokenCredential const> credential,
std::string scope)
@ -32,6 +46,12 @@ namespace Azure { namespace Core { namespace Credentials { namespace Policy {
m_scopes.emplace_back(std::move(scope));
}
/**
* @brief Construct a Bearer Token authentication policy with multiple authentication scopes.
*
* @param credential A #TokenCredential to use with this policy.
* @param scopes A vector of authentication scopes.
*/
explicit BearerTokenAuthenticationPolicy(
std::shared_ptr<TokenCredential const> credential,
std::vector<std::string> scopes)
@ -39,6 +59,16 @@ namespace Azure { namespace Core { namespace Credentials { namespace Policy {
{
}
/**
* @brief Construct a Bearer Token authentication policy with multiple authentication scopes.
*
* @tparam A type of scopes sequence iterator.
*
* @param credential A #TokenCredential to use with this policy.
* @param scopesBegin An iterator pointing to begin of the sequence of scopes to use.
* @param scopesEnd An iterator pointing to an element after the last element in sequence of
* scopes to use.
*/
template <typename ScopesIterator>
explicit BearerTokenAuthenticationPolicy(
std::shared_ptr<TokenCredential const> credential,

View File

@ -1,6 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief BodyStream is used to read data to/from a service.
*/
#pragma once
#ifdef POSIX
@ -26,36 +31,74 @@
namespace Azure { namespace Core { namespace Http {
// BodyStream is used to read data to/from a service
/**
*@brief Used to read data to/from a service.
*/
class BodyStream {
public:
/// Destructor.
virtual ~BodyStream() = default;
// Returns the length of the data; used with the HTTP Content-Length header
/**
* @brief Get the length of the data.
* @remark Used with the HTTP `Content-Length` header.
*/
virtual int64_t Length() const = 0;
// Resets the stream back to the beginning (for retries)
// Derived classes that send data in an HTTP request MUST override this and implement it
// properly.
/*
* @brief Resets the stream back to the beginning (for retries).
* @remark Derived classes that send data in an HTTP request MUST override this and implement it
* properly.
*/
virtual void Rewind()
{
throw "Not Implemented"; // TODO: Replace with best practice as defined by guideline
};
// Reads more data; throws if error/canceled
// return copied size
/**
* @brief Read portion of data into a buffer.
* @remark Throws if error/canceled.
*
* @param conntext #Context so that operation can be canceled.
* @param buffer Pointer to a first byte of the byte buffer to read the data into.
* @param count Size of the buffer to read the data into.
*
* @return Number of bytes read.
*/
virtual int64_t Read(Context const& context, uint8_t* buffer, int64_t count) = 0;
// Keep reading until buffer is all fill out of the end of stream content is reached
/**
* @brief Read #BodyStream into a buffer until the buffer is filled, or until the stream is read
* to end.
*
* @param conntext #Context so that operation can be canceled.
* @param body #BodyStream to read.
* @param buffer Pointer to a first byte of the byte buffer to read the data into.
* @param count Size of the buffer to read the data into.
*
* @return Number of bytes read.
*/
static int64_t ReadToCount(
Context const& context,
BodyStream& body,
uint8_t* buffer,
int64_t count);
/**
* @brief Read #BodyStream until the stream is read to end, allocating memory for the entirety
* of contents.
*
* @param conntext #Context so that operation can be canceled.
* @param body #BodyStream to read.
*
* @return A vector of bytes containing the entirety of data read from the \p body.
*/
static std::vector<uint8_t> ReadToEnd(Context const& context, BodyStream& body);
};
/**
* @brief #BodyStream providing data from an initialized memory buffer.
*/
class MemoryBodyStream : public BodyStream {
private:
const uint8_t* m_data;
@ -66,12 +109,23 @@ namespace Azure { namespace Core { namespace Http {
// Forbid constructor for rval so we don't end up storing dangling ptr
MemoryBodyStream(std::vector<uint8_t> const&&) = delete;
/**
* @brief Construct using vector of bytes.
*
* @param buffer Vector of bytes with the contents to provide the data from to the readers.
*/
MemoryBodyStream(std::vector<uint8_t> const& buffer)
: MemoryBodyStream(buffer.data(), static_cast<int64_t>(buffer.size()))
{
}
// cast as vector from ptr and length
/**
* @brief Construct using buffer pointer and its size.
*
* @param data Pointer to a first byte of the buffer with the contents to provide the data from
* to the readers.
* @param length Size of the buffer.
*/
explicit MemoryBodyStream(const uint8_t* data, int64_t length) : m_data(data), m_length(length)
{
}
@ -83,9 +137,13 @@ namespace Azure { namespace Core { namespace Http {
void Rewind() override { m_offset = 0; }
};
// Use for request with no body
/**
* @brief Empty #BodyStream.
* @remark Used for requests with no body.
*/
class NullBodyStream : public Azure::Core::Http::BodyStream {
public:
/// Constructor.
explicit NullBodyStream() {}
int64_t Length() const override { return 0; }
@ -100,6 +158,9 @@ namespace Azure { namespace Core { namespace Http {
return 0;
};
/**
* @brief Gets a singleton instance of a #NullBodyStream.
*/
static NullBodyStream* GetNullBodyStream()
{
static NullBodyStream nullBodyStream;
@ -108,6 +169,9 @@ namespace Azure { namespace Core { namespace Http {
};
#ifdef POSIX
/**
* @brief #BodyStream providing its data from a file.
*/
class FileBodyStream : public BodyStream {
private:
// in mutable
@ -118,6 +182,13 @@ namespace Azure { namespace Core { namespace Http {
int64_t m_offset;
public:
/**
* @brief Construct from a file.
*
* @param fd File descriptor.
* @param offset Offset in the file to start providing the data from.
* @param length Length of the data, in bytes, to provide.
*/
FileBodyStream(int fd, int64_t offset, int64_t length)
: m_fd(fd), m_baseOffset(offset), m_length(length), m_offset(0)
{
@ -133,6 +204,9 @@ namespace Azure { namespace Core { namespace Http {
#endif
#ifdef WINDOWS
/**
* @brief #BodyStream providing its data from a file.
*/
class FileBodyStream : public BodyStream {
private:
// in mutable
@ -143,6 +217,13 @@ namespace Azure { namespace Core { namespace Http {
int64_t m_offset;
public:
/**
* @brief Construct from a file.
*
* @param hFile File handle.
* @param offset Offset in the file to start providing the data from.
* @param length Length of the data, in bytes, to provide.
*/
FileBodyStream(HANDLE hFile, int64_t offset, int64_t length)
: m_hFile(hFile), m_baseOffset(offset), m_length(length), m_offset(0)
{
@ -157,6 +238,9 @@ namespace Azure { namespace Core { namespace Http {
};
#endif // Windows
/**
* @brief #BodyStream that provides its data from another #BodyStream.
*/
class LimitBodyStream : public BodyStream {
private:
BodyStream* m_inner;
@ -164,6 +248,12 @@ namespace Azure { namespace Core { namespace Http {
int64_t m_bytesRead = 0;
public:
/**
* @brief Construct from another #BodyStream.
*
* @param inner #BodyStream to provide the data from to the readers.
* @param max_length Maximum number of bytes to provide to the readers.
*/
LimitBodyStream(BodyStream* inner, int64_t max_length)
: m_inner(inner), m_length(std::min(inner->Length(), max_length))
{

View File

@ -1,8 +1,10 @@
#pragma once
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @brief #HttpTransport implementation via CURL.
*/
#pragma once
#include "azure/core/http/http.hpp"
@ -41,6 +43,9 @@ namespace Azure { namespace Core { namespace Http {
constexpr static int c_DefaultConnectionExpiredMilliseconds = 1000 * 60;
} // namespace Details
/**
* @brief CURL HTTP connection.
*/
class CurlConnection {
private:
CURL* m_handle;
@ -48,16 +53,40 @@ namespace Azure { namespace Core { namespace Http {
std::chrono::steady_clock::time_point m_lastUseTime;
public:
/**
* @Brief Construct CURL HTTP connection.
*
* @param host HTTP connection host name.
*/
CurlConnection(std::string const& host) : m_handle(curl_easy_init()), m_host(host) {}
/**
* @brief Destructor.
* @detail Cleans up CURL (invokes `curl_easy_cleanup()`).
*/
~CurlConnection() { curl_easy_cleanup(this->m_handle); }
/**
* @brief Get CURL handle.
* @return CURL handle for the HTTP connection.
*/
CURL* GetHandle() { return this->m_handle; }
/**
* @brief Get HTTP connection host.
* @return HTTP connection host name.
*/
std::string GetHost() const { return this->m_host; }
/**
* @brief Update last usage time for the connection.
*/
void updateLastUsageTime() { this->m_lastUseTime = std::chrono::steady_clock::now(); }
/**
* @brief Checks whether this CURL connection is expired.
* @return `true` if this connextion is onsidered expired, `false` oterwise.
*/
bool isExpired()
{
auto connectionOnWaitingTimeMs = std::chrono::duration_cast<std::chrono::milliseconds>(
@ -66,6 +95,9 @@ namespace Azure { namespace Core { namespace Http {
}
};
/**
* CURL HTTP connection pool.
*/
struct CurlConnectionPool
{
#ifdef TESTING_BUILD
@ -74,28 +106,36 @@ namespace Azure { namespace Core { namespace Http {
friend class Azure::Core::Test::TransportAdapter_ConnectionPoolCleaner_Test;
#endif
/*
* Mutex for accessing connection pool for thread-safe reading and writing
/**
* @brief Mutex for accessing connection pool for thread-safe reading and writing.
*/
static std::mutex s_connectionPoolMutex;
/*
* Keeps an unique key for each host and creates a connection pool for each key.
* This way getting a connection for an specific host can be done in O(1) instead of looping a
* single connection list to find the first connection for the required host.
/**
* @brief Keeps an unique key for each host and creates a connection pool for each key.
*
* There might be multiple connections for each host.
* @detail This way getting a connection for an specific host can be done in O(1) instead of
* looping a single connection list to find the first connection for the required host.
*
* @remark There might be multiple connections for each host.
*/
static std::map<std::string, std::list<std::unique_ptr<CurlConnection>>> s_connectionPoolIndex;
/*
* Finds a connection to be re-used from the connection pool. If there is not any available
* connection, a new connection is created.
/**
* @brief Finds a connection to be re-used from the connection pool.
* @remark If there is not any available connection, a new connection is created.
*
* @param request HTTP request to get #CurlConnection for.
*
* @return #CurlConnection to use.
*/
static std::unique_ptr<CurlConnection> GetCurlConnection(Request& request);
/**
* Moves a connection back to the pool to be re-used
* @brief Moves a connection back to the pool to be re-used.
*
* @param connection CURL HTTP connection to add to the pool.
* @param lastStatusCode The most recent HTTP status code received from the \p connection.
*/
static void MoveConnectionBackToPool(
std::unique_ptr<CurlConnection> connection,
@ -413,9 +453,19 @@ namespace Azure { namespace Core { namespace Http {
* @brief Function used when working with Streams to manually write from the HTTP Request to
* the wire.
*
* @param context #Context so that operation can be canceled.
*
* @return CURL_OK when response is sent successfully.
*/
CURLcode SendRawHttp(Context const& context);
/**
* @brief Upload body.
*
* @param context #Context so that operation can be canceled.
*
* @return Curl code.
*/
CURLcode UploadBody(Context const& context);
/**
@ -433,6 +483,8 @@ namespace Azure { namespace Core { namespace Http {
* @brief This function is used after sending an HTTP request to the server to read the HTTP
* RawResponse from wire until the end of headers only.
*
* @param reUseInternalBUffer Indicates whether the internal buffer should be reused.
*
* @return CURL_OK when an HTTP response is created.
*/
void ReadStatusLineAndHeadersFromRawResponse(bool reUseInternalBUffer = false);
@ -456,8 +508,15 @@ namespace Azure { namespace Core { namespace Http {
*/
int64_t ReadFromSocket(uint8_t* buffer, int64_t bufferSize);
/**
* @brief Last HTTP status code read.
*/
Http::HttpStatusCode m_lastStatusCode;
/**
* @brief check whether an end of file has been reached.
* @return `true` if end of file has been reached, `false` otherwise.
*/
bool IsEOF()
{
return this->m_isChunkedResponseType ? this->m_chunkSize == 0

View File

@ -1,6 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief HTTP request and response functionality.
*/
#pragma once
#include "azure/core/http/body_stream.hpp"
@ -37,99 +42,116 @@ namespace Azure { namespace Core { namespace Http {
}
} // namespace Details
/**
* @brief HTTP transport implementation used.
*/
enum class TransportKind
{
// TODO move this to Factory
Curl,
WinHttp
Curl, ///< CURL.
WinHttp ///< WinHTTP.
};
/**
* @brief Defines the possible HTTP status codes.
*/
enum class HttpStatusCode
{
/// No HTTP status code.
None = 0,
// 1xx (information) Status Codes:
Continue = 100,
SwitchingProtocols = 101,
Processing = 102,
EarlyHints = 103,
// === 1xx (information) Status Codes: ===
Continue = 100, ///< HTTP 100 Continue.
SwitchingProtocols = 101, ///< HTTP 101 Switching Protocols.
Processing = 102, ///< HTTP 102 Processing.
EarlyHints = 103, ///< HTTP 103 Early Hints.
// 2xx (successful) Status Codes:
Ok = 200,
Created = 201,
Accepted = 202,
NonAuthoritativeInformation = 203,
NoContent = 204,
ResetContent = 205,
PartialContent = 206,
MultiStatus = 207,
AlreadyReported = 208,
ImUsed = 226,
// === 2xx (successful) Status Codes: ===
Ok = 200, ///< HTTP 200 OK.
Created = 201, ///< HTTP 201 Created.
Accepted = 202, ///< HTTP 202 Accepted.
NonAuthoritativeInformation = 203, ///< HTTP 203 Non-Authoritative Information.
NoContent = 204, ///< HTTP 204 No Content.
ResetContent = 205, ///< HTTP 205 Rest Content.
PartialContent = 206, ///< HTTP 206 Partial Content.
MultiStatus = 207, ///< HTTP 207 Multi-Status.
AlreadyReported = 208, ///< HTTP 208 Already Reported.
IMUsed = 226, ///< HTTP 226 IM Used.
// 3xx (redirection) Status Codes:
MultipleChoices = 300,
MovedPermanently = 301,
Found = 302,
SeeOther = 303,
NotModified = 304,
UseProxy = 305,
TemporaryRedirect = 307,
PermanentRedirect = 308,
// === 3xx (redirection) Status Codes: ===
MultipleChoices = 300, ///< HTTP 300 Multiple Choices.
MovedPermanently = 301, ///< HTTP 301 Moved Permanently.
Found = 302, ///< HTTP 302 Found.
SeeOther = 303, ///< HTTP 303 See Other.
NotModified = 304, ///< HTTP 304 Not Modified.
UseProxy = 305, ///< HTTP 305 Use Proxy.
TemporaryRedirect = 307, ///< HTTP 307 Temporary Redirect.
PermanentRedirect = 308, ///< HTTP 308 Permanent Redirect.
// 4xx (client error) Status Codes:
BadRequest = 400,
Unauthorized = 401,
PaymentRequired = 402,
Forbidden = 403,
NotFound = 404,
MethodNotAllowed = 405,
NotAcceptable = 406,
ProxyAuthenticationRequired = 407,
RequestTimeout = 408,
Conflict = 409,
Gone = 410,
LengthRequired = 411,
PreconditionFailed = 412,
PayloadTooLarge = 413,
UriTooLong = 414,
UnsupportedMediaType = 415,
RangeNotSatisfiable = 416,
ExpectationFailed = 417,
MisdirectedRequest = 421,
UnprocessableEntity = 422,
Locked = 423,
FailedDependency = 424,
TooEarly = 425,
UpgradeRequired = 426,
PreconditionRequired = 428,
TooManyRequests = 429,
RequestHeaderFieldsTooLarge = 431,
UnavailableForLegalReasons = 451,
// === 4xx (client error) Status Codes: ===
BadRequest = 400, ///< HTTP 400 Bad Request.
Unauthorized = 401, ///< HTTP 401 Unauthorized.
PaymentRequired = 402, ///< HTTP 402 Payment Required.
Forbidden = 403, ///< HTTP 403 Forbidden.
NotFound = 404, ///< HTTP 404 Not Found.
MethodNotAllowed = 405, ///< HTTP 405 Method Not Allowed.
NotAcceptable = 406, ///< HTTP 406 Not Acceptable.
ProxyAuthenticationRequired = 407, ///< HTTP 407 Proxy Authentication Required.
RequestTimeout = 408, ///< HTTP 408 Request Timeout.
Conflict = 409, ///< HTTP 409 Conflict.
Gone = 410, ///< HTTP 410 Gone.
LengthRequired = 411, ///< HTTP 411 Length Required.
PreconditionFailed = 412, ///< HTTP 412 Precondition Failed.
PayloadTooLarge = 413, ///< HTTP 413 Payload Too Large.
UriTooLong = 414, ///< HTTP 414 URI Too Long.
UnsupportedMediaType = 415, ///< HTTP 415 Unsupported Media Type.
RangeNotSatisfiable = 416, ///< HTTP 416 Range Not Satisfiable.
ExpectationFailed = 417, ///< HTTP 417 Expectation Failed.
MisdirectedRequest = 421, ///< HTTP 421 Misdirected Request.
UnprocessableEntity = 422, ///< HTTP 422 Unprocessable Entity.
Locked = 423, ///< HTTP 423 Locked.
FailedDependency = 424, ///< HTTP 424 Failed Dependency.
TooEarly = 425, ///< HTTP 425 Too Early.
UpgradeRequired = 426, ///< HTTP 426 Upgrade Required.
PreconditionRequired = 428, ///< HTTP 428 Precondition Required.
TooManyRequests = 429, ///< HTTP 429 Too Many Requests.
RequestHeaderFieldsTooLarge = 431, ///< HTTP 431 Request Header Fields Too Large.
UnavailableForLegalReasons = 451, ///< HTTP 451 Unavailable For Legal Reasons.
// 5xx (server error) Status Codes:
InternalServerError = 500,
NotImplemented = 501,
BadGateway = 502,
ServiceUnavailable = 503,
GatewayTimeout = 504,
HttpVersionNotSupported = 505,
VariantAlsoNegotiates = 506,
InsufficientStorage = 507,
LoopDetected = 508,
NotExtended = 510,
NetworkAuthenticationRequired = 511,
// === 5xx (server error) Status Codes: ===
InternalServerError = 500, ///< HTTP 500 Internal Server Error.
NotImplemented = 501, ///< HTTP 501 Not Implemented.
BadGateway = 502, ///< HTTP 502 Bad Gateway.
ServiceUnavailable = 503, ///< HTTP 503 Unavailable.
GatewayTimeout = 504, ///< HTTP 504 Gateway Timeout.
HttpVersionNotSupported = 505, ///< HTTP 505 HTTP Version Not Supported.
VariantAlsoNegotiates = 506, ///< HTTP 506 Variant Also Negotiates.
InsufficientStorage = 507, ///< HTTP 507 Insufficient Storage.
LoopDetected = 508, ///< HTTP 508 Loop Detected.
NotExtended = 510, ///< HTTP 510 Not Extended.
NetworkAuthenticationRequired = 511, ///< HTTP 511 Network Authentication Required.
};
/**
* HTTP request method.
*/
enum class HttpMethod
{
Get,
Head,
Post,
Put,
Delete,
Patch,
Get, ///< GET
Head, ///< HEAD
Post, ///< POST
Put, ///< PUT
Delete, ///< DELETE
Patch, ///< PATCH
};
/**
* @brief Get a string representation for a value of #HttpMethod.
*
* @param method A value of #HttpMethod value.
*
* @return String name that corresponds to a value of #HttpMethod type.
*/
inline std::string HttpMethodToString(const HttpMethod& method)
{
switch (method)
@ -151,15 +173,21 @@ namespace Azure { namespace Core { namespace Http {
}
}
/**
* Type of HTTP response body.
*/
enum class BodyType
{
Buffer,
Stream,
Buffer, ///< Buffer.
Stream, ///< Stream.
};
// Url represent the location where a request will be performed. It can be parsed and init from
// a string that contains all Url parts (scheme, host, path, etc).
// Authority is not currently supported.
/**
* @brief URL.
*/
class Url {
// Let Request class to be able to set retry enabled ON
friend class Request;
@ -198,39 +226,76 @@ namespace Azure { namespace Core { namespace Http {
}
public:
// Create empty Url instance. Usually for building Url from scratch
/**
* @brief Construct an empty URL.
*/
Url() {}
// Create Url from an url-encoded string. Usually from pre-built url with query parameters (like
// SaS) url is expected to be already url-encoded.
/**
* @brief Construct a URL from a URL-encoded string.
*
* @param encodedUrl URL string that has all its expected parts already URL-encoded.
*/
explicit Url(const std::string& encodedUrl);
/************* Builder Url functions ****************/
/******** API for building Url from scratch. Override state ********/
/**
* @brief Set URL scheme.
*
* @param scheme URL scheme.
*/
void SetScheme(const std::string& scheme) { m_scheme = scheme; }
/**
* @brief Set URL host.
*
* @param host URL host.
* @param isHostEncoded `true` if \p host is URL-encoded, `false` otherwise.
*/
void SetHost(const std::string& host, bool isHostEncoded = false)
{
m_host = isHostEncoded ? host : EncodeHost(host);
}
/**
* @brief Set URL port.
*
* @param port URL port.
*/
void SetPort(uint16_t port) { m_port = port; }
/**
* @brief Set URL path.
*
* @param path URL path.
* @param isPathEncoded `true` if \p fragment is URL-encoded, `false` otherwise.
*/
void SetPath(const std::string& path, bool isPathEncoded = false)
{
m_encodedPath = isPathEncoded ? path : EncodePath(path);
}
/**
* @brief Set URL fragment.
*
* @param fragment URL fragment.
* @param isFragmentEncoded `true` if \p fragment is URL-encoded, `false` otherwise.
*/
void SetFragment(const std::string& fragment, bool isFragmentEncoded = false)
{
m_fragment = isFragmentEncoded ? fragment : EncodeFragment(fragment);
}
/******** API for mutating Url state ********/
// ===== APIs for mutating URL state: ======
// Path is mostly expected to be appended without url-encoding. Be default, path will be encoded
// before it is added to Url. \p isPathEncoded can set to true to avoid encoding.
/**
* @brief Append an element of URL path.
*
* @param path URL path element to append.
* @param isPathEncoded `true` if \p path is URL-encoded, `false` otherwise.
*/
void AppendPath(const std::string& path, bool isPathEncoded = false)
{
if (!m_encodedPath.empty() && m_encodedPath.back() != '/')
@ -240,13 +305,16 @@ namespace Azure { namespace Core { namespace Http {
m_encodedPath += isPathEncoded ? path : EncodePath(path);
}
// the value from query parameter is mostly expected to be non-url-encoded and it will be
// encoded before adding to url by default. Use \p isValueEncoded = true when the value is
// already encoded.
//
// Note: a query key can't contain any chars that needs to be url-encoded. (by RFC).
//
// Note: AppendQuery override previous query parameters.
/**
* @brief Append an HTTP query parameter.
*
* @note Overrides previous query parameters.
* @remark A query key can't contain any characters that need to be URL-encoded (per RFC).
*
* @param key HTTP query parameter.
* @param value HTTP quary parameter value.
* @param isValueEncoded `true` if \p value is URL-encoded, `false` otherwise.
*/
void AppendQuery(const std::string& key, const std::string& value, bool isValueEncoded = false)
{
std::string encoded_value = isValueEncoded ? Decode(value) : value;
@ -260,10 +328,20 @@ namespace Azure { namespace Core { namespace Http {
}
}
// query must be encoded.
/**
* @brief Append a HTTP query.
*
* @note All the required HTTP query parts should be URL-encoded.
*
* @param encodedQueries HTTP query.
*/
void AppendQueries(const std::string& encodedQueries);
// removes a query parameter
/**
* @brief Removes a HTTP query parameter.
*
* @param key HTTP query parameter to remove.
*/
void RemoveQuery(const std::string& key)
{
m_queryParameters.erase(key);
@ -271,27 +349,45 @@ namespace Azure { namespace Core { namespace Http {
}
/************** API to read values from Url ***************/
/**
* @brief Get URL host.
*/
std::string GetHost() const { return m_host; }
/**
* @brief Get URL path.
*/
const std::string& GetPath() const { return m_encodedPath; }
// Copy from query parameters list. Query parameters from retry map have preference and will
// override any value from the initial query parameters from the request
//
// Note: Query values added with url-encoding will be encoded in the list. No decoding is done
// on values.
/**
* @brief Get all the query paramters in the URL.
*
* @note Retry parameters have preference and will override any value from the initial query
* parameters.
* @note All the values are URL-encoded.
*
* @return URL query parameters.
*/
const std::map<std::string, std::string> GetQuery() const
{
return Details::MergeMaps(m_retryQueryParameters, m_queryParameters);
}
/**
* @brief Get relative URL.
*/
std::string GetRelativeUrl() const;
// Url with encoded query parameters
/**
* @brief Get relative URL.
* @remark All the query parameters are URL-encoded.
*/
std::string GetAbsoluteUrl() const;
};
/**
* @brief HTTP request.
*/
class Request {
friend class RetryPolicy;
#ifdef TESTING_BUILD
@ -321,17 +417,39 @@ namespace Azure { namespace Core { namespace Http {
void StopRetry(); // only called by retry policy
public:
/**
* @brief Construct an HTTP request.
*
* @param httpMethod HTTP method.
* @param url URL.
* @param bodyStream HTTP #BodyStream.
* @param downloadViaStream
*/
explicit Request(HttpMethod httpMethod, Url url, BodyStream* bodyStream, bool downloadViaStream)
: m_method(std::move(httpMethod)), m_url(std::move(url)), m_bodyStream(bodyStream),
m_retryModeEnabled(false), m_isDownloadViaStream(downloadViaStream)
{
}
/**
* @brief Construct an HTTP request.
*
* @param httpMethod HTTP method.
* @param url URL.
* @param bodyStream HTTP #BodyStream.
*/
explicit Request(HttpMethod httpMethod, Url url, BodyStream* bodyStream)
: Request(httpMethod, std::move(url), bodyStream, false)
{
}
/**
* @brief Construct an HTTP request.
*
* @param httpMethod HTTP method.
* @param url URL.
* @param downloadViaStream
*/
// Typically used for GET with no request body that can return bodyStream
explicit Request(HttpMethod httpMethod, Url url, bool downloadViaStream)
: Request(
@ -342,41 +460,105 @@ namespace Azure { namespace Core { namespace Http {
{
}
/**
* @brief Construct an HTTP request.
*
* @param httpMethod HTTP method.
* @param url URL.
*/
// Typically used for GET with no request body.
explicit Request(HttpMethod httpMethod, Url url)
: Request(httpMethod, std::move(url), NullBodyStream::GetNullBodyStream(), false)
{
}
/**
* @brief Add an HTTP header.
*
* @param name HTTP header name.
* @param value HTTP header value.
*/
void AddHeader(std::string const& name, std::string const& value);
/**
* @brief Remove an HTTP header.
*
* @param name HTTP header name.
*/
void RemoveHeader(std::string const& name);
/**
* @brief Set upload chunk size.
*
* @param size Upload chunk size.
*/
void SetUploadChunkSize(int64_t size) { this->m_uploadChunkSize = size; }
// Methods used by transport layer (and logger) to send request
/**
* @brief Get HTTP method.
*/
HttpMethod GetMethod() const;
/**
* @brief Get HTTP headers.
*/
std::map<std::string, std::string> GetHeaders() const;
/**
* @brief Get HTTP body as #BodyStream.
*/
BodyStream* GetBodyStream() { return this->m_bodyStream; }
/**
* @brief Get HTTP message prior to HTTP body.
*/
std::string GetHTTPMessagePreBody() const;
/**
* @brief Get upload chunk size.
*/
int64_t GetUploadChunkSize() { return this->m_uploadChunkSize; }
/**
* @brief
*/
bool IsDownloadViaStream() { return this->m_isDownloadViaStream; }
/**
* @brief Get URL.
*/
Url& GetUrl() { return this->m_url; }
/**
* @brief Get URL.
*/
Url const& GetUrl() const { return this->m_url; }
};
/*
* RawResponse exceptions
*/
/**
* @brief Couldn't resolve HTTP host.
*/
struct CouldNotResolveHostException : public std::runtime_error
{
explicit CouldNotResolveHostException(std::string const& msg) : std::runtime_error(msg) {}
};
// Any other excpetion from transport layer without an specific exception defined above
// Any other exception from transport layer without an specific exception defined above
/**
* @brief HTTP transport layer error.
*/
struct TransportException : public std::runtime_error
{
explicit TransportException(std::string const& msg) : std::runtime_error(msg) {}
};
/**
* @brief Raw HTTP response.
*/
class RawResponse {
private:
@ -401,6 +583,14 @@ namespace Azure { namespace Core { namespace Http {
}
public:
/**
* @brief Construct raw HTTP response.
*
* @param majorVersion HTTP protocol version major number.
* @param minorVersion HTTP protocol version minor number.
* @param statusCode HTTP status code.
* @param reasonPhrase HTP reason phrase.
*/
explicit RawResponse(
int32_t majorVersion,
int32_t minorVersion,
@ -410,27 +600,92 @@ namespace Azure { namespace Core { namespace Http {
{
}
// Methods used to build HTTP response
// ===== Methods used to build HTTP response =====
/**
* @brief Add header to the HTTP response.
*
* @param name HTTP response header name.
* @param value HTTP response header value.
*/
void AddHeader(std::string const& name, std::string const& value);
/**
* @brief Add header to the HTTP response.
*
* @param header HTTP response header in RFCnnnn format (`OWS header-value OWS`).
*/
// rfc form header-name: OWS header-value OWS
void AddHeader(std::string const& header);
/**
* @brief Add header to the HTTP response.
* @detail HTTP response header should be in RFCnnnn format (`OWS header-value OWS`).
*
* @param begin Pointer to the first byte of the header string in RFCnnnn format.
* @param last Pointer to the last byte of the header string in RFCnnnn format.
*/
void AddHeader(uint8_t const* const begin, uint8_t const* const last);
/**
* @brief Set #BodyStream for this HTTP response.
*
* @param stream HTTP #BodyStream.
*/
void SetBodyStream(std::unique_ptr<BodyStream> stream);
/**
* @brief Set HTTP response body for this HTTP response.
*
* @param body HTTP response body bytes.
*/
void SetBody(std::vector<uint8_t> body) { this->m_body = std::move(body); }
// adding getters for version and stream body. Clang will complain on Mac if we have unused
// fields in a class
/**
* @brief Get major number of the HTTP response protocol version.
*/
int32_t GetMajorVersion() const { return this->m_majorVersion; }
/**
* @brief Get minor number of the HTTP response protocol version.
*/
int32_t GetMinorVersion() const { return this->m_minorVersion; }
/**
* @brief Get HTTP status code of the HTTP response.
*/
HttpStatusCode GetStatusCode() const;
/**
* @brief Get HTTP reason phrase code of the HTTP response.
*/
std::string const& GetReasonPhrase() const;
/**
* @brief Get HTTP response headers.
*/
std::map<std::string, std::string> const& GetHeaders() const;
/**
* @brief Get HTTP response body as #BodyStream.
*/
std::unique_ptr<BodyStream> GetBodyStream()
{
// If m_bodyStream was moved before. nullpr is returned
return std::move(this->m_bodyStream);
}
/**
* @brief Get HTTP response body as vector of bytes.
*/
std::vector<uint8_t>& GetBody() { return this->m_body; }
/**
* @brief Get HTTP response body as vector of bytes.
*/
std::vector<uint8_t> const& GetBody() const { return this->m_body; }
};

View File

@ -1,6 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief HTTP pipeline is a stack of HTTP policies.
* @remark See #policy.hpp
*/
#pragma once
#include "azure/core/context.hpp"
@ -12,11 +18,26 @@
namespace Azure { namespace Core { namespace Http {
/**
* @brief HTTP pipeline is a stack of HTTP policies that get applied sequentially.
*
* @details Every client is expected to have its own HTTP pipeline, consisting of sequence of
* individual HTTP policies. Policies shape the behavior of how a HTTP request is being handled,
* ranging from retrying and logging, up to sending a HTTP request over the wire.
*
* @remark See #policy.hpp
*/
class HttpPipeline {
protected:
std::vector<std::unique_ptr<HttpPolicy>> m_policies;
public:
/**
* @brief Construct HTTP pipeline with the sequence of HTTP policies provided.
*
* @param policies A sequence of #HttpPolicy representing a stack, first element corresponding
* to the top of the stack.
*/
explicit HttpPipeline(const std::vector<std::unique_ptr<HttpPolicy>>& policies)
{
m_policies.reserve(policies.size());
@ -26,11 +47,18 @@ namespace Azure { namespace Core { namespace Http {
}
}
/**
* @brief Construct HTTP pipeline with the sequence of HTTP policies provided.
*
* @param policies A sequence of #HttpPolicy representing a stack, first element corresponding
* to the top of the stack.
*/
explicit HttpPipeline(std::vector<std::unique_ptr<HttpPolicy>>&& policies)
: m_policies(std::move(policies))
{
}
/// Copy constructor.
HttpPipeline(const HttpPipeline& other)
{
m_policies.reserve(other.m_policies.size());
@ -41,11 +69,12 @@ namespace Azure { namespace Core { namespace Http {
}
/**
* @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>
* @brief Start the HTTP pipeline.
*
* @param ctx #Context so that operation can be canceled.
* @param request The HTTP request to be processed.
*
* @return HTTP response after the request has been processed.
*/
std::unique_ptr<RawResponse> Send(Context const& ctx, Request& request) const
{

View File

@ -1,6 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief Utilities to be used by HTTP transport policy implementations.
*/
#pragma once
#include "azure/core/azure.hpp"
@ -17,16 +22,38 @@ namespace Azure { namespace Core { namespace Http {
class NextHttpPolicy;
/**
* @brief HTTP policy.
* An HTTP pipeline inside SDK clients is an stack sequence of HTTP policies.
*/
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
/**
* @brief Apply this HTTP policy.
*
* @param context #Context so that operation can be canceled.
* @param request An HTTP #Request being sent.
* @param policy #NextHttpPolicy to invoke after this policy has been applied.
*
* @return An HTTP #RawResponse after this policy, and all subsequent HTTP policies in the stack
* sequence of policies have been applied.
*/
virtual std::unique_ptr<RawResponse> Send(
Context const& context,
Request& request,
NextHttpPolicy policy) const = 0;
/// Destructor.
virtual ~HttpPolicy() {}
/**
* @brief Creates a clone of this HTTP policy.
* @return A clone of this HTTP policy.
*/
virtual std::unique_ptr<HttpPolicy> Clone() const = 0;
protected:
@ -36,11 +63,20 @@ namespace Azure { namespace Core { namespace Http {
HttpPolicy& operator=(const HttpPolicy& other) = default;
};
// Represents the next HTTP policy in the stack sequence of policies.
class NextHttpPolicy {
const std::size_t m_index;
const std::vector<std::unique_ptr<HttpPolicy>>* m_policies;
public:
/**
* @brief Construct an abstraction representing a next line in the stack sequence of policies,
* from the caller's perspective.
*
* @param index An sequential index of this policy in the stack sequence of policies.
* @param policies A vector of unique pointers next in the line to be invoked after the current
* policy.
*/
explicit NextHttpPolicy(
std::size_t index,
const std::vector<std::unique_ptr<HttpPolicy>>* policies)
@ -48,14 +84,33 @@ namespace Azure { namespace Core { namespace Http {
{
}
/**
* @brief Apply this HTTP policy.
*
* @param context #Context so that operation can be canceled.
* @param request An HTTP #Request being sent.
*
* @return An HTTP #RawResponse after this policy, and all subsequent HTTP policies in the stack
* sequence of policies have been applied.
*/
std::unique_ptr<RawResponse> Send(Context const& ctx, Request& req);
};
/**
* @brief Applying this policy sends an HTTP request over the wire.
* @remark This policy must be the bottom policy in the stack of the HTTP policy stack.
*/
class TransportPolicy : public HttpPolicy {
private:
std::shared_ptr<HttpTransport> m_transport;
public:
/**
* @brief Construct an HTTP transport policy.
*
* @param transport A pointer to the #HttpTransport implementation to use when this policy gets
* applied (#Send).
*/
explicit TransportPolicy(std::shared_ptr<HttpTransport> transport)
: m_transport(std::move(transport))
{
@ -72,13 +127,29 @@ namespace Azure { namespace Core { namespace Http {
NextHttpPolicy nextHttpPolicy) const override;
};
/**
* @brief Options for the #RetryPolicy.
*/
struct RetryOptions
{
/**
* @brief Maximum number of attempts to retry.
*/
int MaxRetries = 3;
/**
* @brief Mimimum amount of time between retry attempts.
*/
std::chrono::milliseconds RetryDelay = std::chrono::seconds(4);
/**
* @brief Mimimum amount of time between retry attempts.
*/
decltype(RetryDelay) MaxRetryDelay = std::chrono::minutes(2);
/**
* @brief HTTP status codes to retry on.
*/
std::vector<HttpStatusCode> StatusCodes{
HttpStatusCode::RequestTimeout,
HttpStatusCode::InternalServerError,
@ -88,11 +159,19 @@ namespace Azure { namespace Core { namespace Http {
};
};
/**
* @brief HTTP retry policy.
*/
class RetryPolicy : public HttpPolicy {
private:
RetryOptions m_retryOptions;
public:
/**
* Constructs HTTP retry policy with the provided #RetryOptions.
*
* @param options HTTP #RetryOptions.
*/
explicit RetryPolicy(RetryOptions options) : m_retryOptions(std::move(options)) {}
std::unique_ptr<HttpPolicy> Clone() const override
@ -106,11 +185,20 @@ namespace Azure { namespace Core { namespace Http {
NextHttpPolicy nextHttpPolicy) const override;
};
/**
* @brief HTTP Request ID policy.
*
* @details Applies an HTTP header with a unique ID to each HTTP request, so that each individual
* request can be traced for troubleshooting.
*/
class RequestIdPolicy : public HttpPolicy {
private:
constexpr static const char* RequestIdHeader = "x-ms-client-request-id";
public:
/**
* @brief Constructs HTTP request ID policy.
*/
explicit RequestIdPolicy() {}
std::unique_ptr<HttpPolicy> Clone() const override
@ -130,6 +218,13 @@ namespace Azure { namespace Core { namespace Http {
}
};
/**
* @brief HTTP telemetry policy.
*
* @details Applies an HTTP header with a component name and version to each HTTP request,
* includes Azure SDK version information, and operating system information.
* @remark See https://azure.github.io/azure-sdk/general_azurecore.html#telemetry-policy.
*/
class TelemetryPolicy : public HttpPolicy {
std::string m_telemetryId;
@ -141,11 +236,25 @@ namespace Azure { namespace Core { namespace Http {
std::string const& applicationId);
public:
/**
* @brief Construct HTTP telemetry policy with component name and component version.
*
* @param componentName Azure SDK component name (e.g. "storage.blobs")
* @param componentVersion Azure SDK component version (e.g. "11.0.0")
*/
explicit TelemetryPolicy(std::string const& componentName, std::string const& componentVersion)
: TelemetryPolicy(componentName, componentVersion, g_emptyApplicationId)
{
}
/**
* @brief Construct HTTP telemetry policy with component name, component version, and an
* applicatin ID.
*
* @param componentName Azure SDK component name (e.g. "storage.blobs")
* @param componentVersion Azure SDK component version (e.g. "11.0.0")
* @param applicationId Customer Application ID (e.g. "AzCopy")
*/
explicit TelemetryPolicy(
std::string const& componentName,
std::string const& componentVersion,
@ -165,8 +274,17 @@ namespace Azure { namespace Core { namespace Http {
NextHttpPolicy nextHttpPolicy) const override;
};
/**
* @brief Logs every HTTP request.
*
* @detail Logs every HTTP request, response, or retry attempt (see #LogClassification)
* @remark See #logging.hpp
*/
class LoggingPolicy : public HttpPolicy {
public:
/**
* @brief Constructs HTTP logging policy.
*/
explicit LoggingPolicy() {}
std::unique_ptr<HttpPolicy> Clone() const override
@ -180,11 +298,20 @@ namespace Azure { namespace Core { namespace Http {
NextHttpPolicy nextHttpPolicy) const override;
};
/**
* @brief Log classigications being used to designate log messages from HTTP #LoggingPolicy.
*/
class LogClassification : private Azure::Core::Logging::Details::LogClassificationProvider<
Azure::Core::Logging::Details::Facility::Core> {
public:
/// HTTP request.
static constexpr auto const Request = Classification(1);
/// HTTP response.
static constexpr auto const Response = Classification(2);
/// HTTP retry attempt.
static constexpr auto const Retry = Classification(3);
};
}}} // namespace Azure::Core::Http

View File

@ -1,6 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief Utilities to be used by HTTP transport implementations.
*/
#pragma once
#include "azure/core/context.hpp"
@ -8,14 +13,25 @@
namespace Azure { namespace Core { namespace Http {
/**
* @brief Base class for all HTTP transport implementaions.
*/
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
/**
* @brief Send an HTTP request over the wire.
*
* @param context #Context so that operation can be canceled.
* @param request An HTTP #Request to send.
*/
// TODO - Should this be const
virtual std::unique_ptr<RawResponse> Send(Context const& context, Request& request) = 0;
/// Destructor.
virtual ~HttpTransport() {}
protected:

View File

@ -1,8 +1,10 @@
#pragma once
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @brief #HttpTransport implementation via WinHttp.
*/
#pragma once
#include "azure/core/http/http.hpp"
@ -13,13 +15,17 @@
namespace Azure { namespace Core { namespace Http {
/**
* @brief #HttpTransport implementation via WinHttp.
*/
class WinHttpTansport : public HttpTransport {
private:
public:
/// Constructor.
WinHttpTansport();
~WinHttpTansport();
~WinHttpTansport() override;
virtual std::unique_ptr<RawResponse> Send(Context const& context, Request& request);
virtual std::unique_ptr<RawResponse> Send(Context const& context, Request& request) override;
};
}}} // namespace Azure::Core::Http

View File

@ -1,6 +1,12 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief This header defines the types and functions your application uses to be notified of Azure
* SDK client library log messages.
*/
#pragma once
#include "azure/core/azure.hpp"
@ -15,10 +21,31 @@ namespace Azure { namespace Core { namespace Logging {
class LogClassification;
class LogClassifications;
/**
* @brief Defines the signature of the callback function that application developers must write in
* order to receive Azure SDK log messages.
*
* @param classification The log message classification.
* @param classification The log message.
*/
typedef std::function<void(LogClassification const& classification, std::string const& message)>
LogListener;
/**
* @brief Set the function that will be invoked to report an SDK log message.
*
* @param logListener A #LogListener function that will be invoked when the SDK reports a log
* message matching one of the log classifications passed to #SetLogClassifications(). If null, no
* function will be invoked.
*/
void SetLogListener(LogListener logListener);
/**
* @brief Allows the application to specify which log classification types it is interested in
* receiving.
*
* @param logClassifications Log classification values.
*/
void SetLogClassifications(LogClassifications logClassifications);
namespace Details {
@ -33,6 +60,9 @@ namespace Azure { namespace Core { namespace Logging {
class LogClassificationsPrivate;
} // namespace Details
/**
* @brief Represents a set of log classifications.
*/
class LogClassifications {
friend class Details::LogClassificationsPrivate;
@ -42,17 +72,28 @@ namespace Azure { namespace Core { namespace Logging {
explicit LogClassifications(bool all) : m_all(all) {}
public:
/**
* @brief Initialize the list of log classifications with `std::initializer_list`.
* @param list An initializer list.
*/
LogClassifications(std::initializer_list<LogClassification> list)
: m_classifications(list), m_all(false)
{
}
/**
* @brief Initialize the list of log classifications with `std::set`.
* @param set A set of classifications.
*/
explicit LogClassifications(std::set<LogClassification> set)
: m_classifications(std::move(set)), m_all(false)
{
}
};
/**
* @brief Represents a log classification.
*/
class LogClassification {
template <Details::Facility> friend class Details::LogClassificationProvider;
friend struct std::less<LogClassification>;
@ -70,17 +111,34 @@ namespace Azure { namespace Core { namespace Logging {
}
public:
/**
* @brief Compare log classification to another one.
* @param other Another log classification to compare to.
* @return `true` if this log classification equals to \p other, `false` otherwise.
*/
constexpr bool operator==(LogClassification const& other) const
{
return m_value == other.m_value;
}
/**
* @brief Compare log classification to another one.
* @param other Another log classification to compare to.
* @return `true` if this log classification does not equal to \p other, `false` otherwise.
*/
constexpr bool operator!=(LogClassification const& other) const
{
return m_value != other.m_value;
}
/**
* @brief Represents a list of all classifications.
*/
static LogClassifications const All;
/**
* @brief Represents an empty list of classifications.
*/
static LogClassifications const None;
};

View File

@ -1,6 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief Manages an optional contained value, i.e. a value that may or may not be present.
*/
#pragma once
#include <cstdlib> // for abort
@ -16,6 +21,11 @@ namespace Azure { namespace Core {
};
} // namespace Details
/**
* @brief Manages an optional contained value, i.e. a value that may or may not be present.
*
* @tparam T A type to represent contained values.
*/
template <class T> class Nullable {
union
{
@ -27,9 +37,19 @@ namespace Azure { namespace Core {
bool m_hasValue;
public:
/**
* @brief Construct a #Nullable that represents the absence of value.
*/
constexpr Nullable() : m_disengaged{}, m_hasValue(false) {}
/**
* @brief Construct a #Nullable having an \p initialValue.
*
* @param initialValue A non-absent value to initialize with.
*/
constexpr Nullable(const T& initialValue) : m_value(initialValue), m_hasValue(true) {}
/// Copy constructor.
Nullable(const Nullable& other) noexcept(std::is_nothrow_copy_constructible<T>::value)
: m_hasValue(other.m_hasValue)
{
@ -39,6 +59,7 @@ namespace Azure { namespace Core {
}
}
/// Move constructor.
Nullable(Nullable&& other) noexcept(std::is_nothrow_move_constructible<T>::value)
: m_hasValue(other.m_hasValue)
{
@ -48,6 +69,9 @@ namespace Azure { namespace Core {
}
}
/**
* @brief Destroy the contained value, if there is one.
*/
~Nullable()
{
if (m_hasValue)
@ -56,6 +80,9 @@ namespace Azure { namespace Core {
}
}
/**
* @brief Destroy any contained value, if there is one.
*/
void Reset() noexcept /* enforces termination */
{
if (m_hasValue)
@ -65,6 +92,11 @@ namespace Azure { namespace Core {
}
}
/**
* @brief Exchange the contents.
*
* @param other An instance to exchange the contents with.
*/
// this assumes that swap can't throw if T is nothrow move constructible because
// is_nothrow_swappable is added in C++17
void Swap(Nullable& other) noexcept(std::is_nothrow_move_constructible<T>::value)
@ -91,15 +123,19 @@ namespace Azure { namespace Core {
}
}
// Intentionally lowercase to follow the Swappable requirements
// https://en.cppreference.com/w/cpp/named_req/Swappable
//
/**
* @brief Invokes #Swap while having a lowercase name that satisfies `swappable` requirements
* (see details).
*
* @details Swappable requirements: https://en.cppreference.com/w/cpp/named_req/Swappable
*/
friend void swap(Nullable& lhs, Nullable& rhs) noexcept(
std::is_nothrow_move_constructible<T>::value)
{
lhs.Swap(rhs);
}
/// Assignment operator.
Nullable& operator=(const Nullable& other)
{
// this copy and swap may be inefficient for some Ts but
@ -108,6 +144,7 @@ namespace Azure { namespace Core {
return *this;
}
/// Assignment operator with move semantics.
Nullable& operator=(Nullable&& other) noexcept(std::is_nothrow_move_constructible<T>::value)
{
// this move and swap may be inefficient for some Ts but
@ -116,6 +153,13 @@ namespace Azure { namespace Core {
return *this;
}
/**
* @brief Assignment operator from another type.
*
* @tparam U
*
* @param other
*/
template <
class U = T,
typename std::enable_if<
@ -148,6 +192,12 @@ namespace Azure { namespace Core {
return *this;
}
/**
* @brief Construct the contained value in-place.
*
* @detail If this instance already contains a value before the call, the contained value is
* destroyed by calling its destructor.
*/
template <class... U>
T& Emplace(U&&... Args) noexcept(std::is_nothrow_constructible<T, U...>::value)
{
@ -156,8 +206,16 @@ namespace Azure { namespace Core {
return m_value;
}
/**
* @brief Check whether a value is contained.
*
* @return `true` If a value is contained, `false` if value is absent.
*/
bool HasValue() const noexcept { return m_hasValue; }
/**
* @brief Get the contained value.
*/
const T& GetValue() const& noexcept
{
if (!m_hasValue)
@ -170,6 +228,9 @@ namespace Azure { namespace Core {
return m_value;
}
/**
* @brief Get the contained value reference.
*/
T& GetValue() & noexcept
{
if (!m_hasValue)
@ -182,6 +243,9 @@ namespace Azure { namespace Core {
return m_value;
}
/**
* @brief Get the contained value (as rvalue reference).
*/
T&& GetValue() && noexcept
{
if (!m_hasValue)
@ -194,14 +258,21 @@ namespace Azure { namespace Core {
return std::move(m_value);
}
/**
* @brief `operator bool` on the condition of #HasValue.
*/
explicit operator bool() const noexcept { return HasValue(); }
/**
* @brief Get the contained value, returns \p other if value is absent.
* @param other A value to return when no value is contained.
* @return A contained value (when present), or \p other.
*/
template <
class U = T,
typename std::enable_if<
std::is_convertible<
const T&,
typename std::remove_cv<T>::type>::value && std::is_convertible<U, T>::value,
std::is_convertible<const T&, typename std::remove_cv<T>::type>::value
&& std::is_convertible<U, T>::value,
int>::type
= 0>
constexpr typename std::remove_cv<T>::type ValueOr(U&& other) const&
@ -214,12 +285,16 @@ namespace Azure { namespace Core {
return static_cast<typename std::remove_cv<T>::type>(std::forward<U>(other));
}
/**
* @brief Get the contained value, returns \p other if value is absent.
* @param other A value to return when no value is contained.
* @return A contained value (when present), or \p other.
*/
template <
class U = T,
typename std::enable_if<
std::is_convertible<
T,
typename std::remove_cv<T>::type>::value && std::is_convertible<U, T>::value,
std::is_convertible<T, typename std::remove_cv<T>::type>::value
&& std::is_convertible<U, T>::value,
int>::type
= 0>
constexpr typename std::remove_cv<T>::type ValueOr(U&& other) &&

View File

@ -1,33 +1,72 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief Wraps raw HTTP response into a response of a specific type.
*/
#pragma once
#include <azure/core/http/http.hpp>
namespace Azure { namespace Core {
/**
* @brief Wraps raw HTTP response into a response of a specific type.
*
* @tparam T A specific type of value to get from the raw HTTP response type.
*/
template <class T> class Response {
T m_value;
std::unique_ptr<Http::RawResponse> m_rawResponse;
public:
/**
* @brief Initialize a #Response<T> with an initial value.
*
* @param initialValue Initial value.
* @param rawResponse Raw HTTP response.
*/
// Require a raw response to create a Response T
explicit Response(T initialValue, std::unique_ptr<Http::RawResponse>&& rawResponse)
: m_value(std::move(initialValue)), m_rawResponse(std::move(rawResponse))
{
}
/**
* @brief Get raw HTTP response.
*/
// Do not give up raw response ownership.
Http::RawResponse& GetRawResponse() { return *this->m_rawResponse; }
/**
* @brief Get a pointer to a value of a specific type.
*/
const T* operator->() const { return &this->m_value; };
/**
* @brief Get a tpointer to a value of a specific type.
*/
T* operator->() { return &this->m_value; };
/**
* @brief Get value of a specific type.
*/
T& operator*() { return this->m_value; };
/**
* @brief Get value of a specific type.
*/
const T& operator*() const { return this->m_value; };
/**
* @brief Get an rvalue reference to the value of a specific type.
*/
T&& ExtractValue() { return std::move(this->m_value); }
/**
* @brief Get a smaprt pointer rvalue reference to the value of a specific type.
*/
std::unique_ptr<Http::RawResponse>&& ExtractRawResponse()
{
return std::move(this->m_rawResponse);

View File

@ -1,6 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief Universally unique identifier.
*/
#pragma once
#include <cstring>
@ -10,7 +15,9 @@
#include <utility> // for swap and move
namespace Azure { namespace Core {
/**
* @brief Universally unique identifier.
*/
class Uuid {
private:
@ -24,13 +31,13 @@ namespace Azure { namespace Core {
static constexpr uint8_t ReservedFuture = 0x00;
private:
Uuid(uint8_t const uuid[UuidSize])
{
std::memcpy(m_uuid, uuid, UuidSize);
}
Uuid(uint8_t const uuid[UuidSize]) { std::memcpy(m_uuid, uuid, UuidSize); }
public:
/**
* Gets UUID as a string.
* @detail A string is in canonical format (4-2-2-2-6 lowercase hex and dashes only)
*/
std::string GetUuidString()
{
// Guid is 36 characters
@ -61,7 +68,11 @@ namespace Azure { namespace Core {
return std::string(s);
}
static Uuid CreateUuid() {
/**
* @brief Create a new random UUID.
*/
static Uuid CreateUuid()
{
std::random_device rd;
std::mt19937 gen(rd());

View File

@ -1,6 +1,11 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief Provides version information.
*/
#pragma once
#include <string>
@ -12,12 +17,27 @@
namespace Azure { namespace Core {
/**
* @brief Provides version information.
*/
class Version {
public:
/// Major numeric identifier.
static constexpr int Major = AZURE_CORE_VERSION_MAJOR;
/// Minor numeric identifier.
static constexpr int Minor = AZURE_CORE_VERSION_MINOR;
/// Patch numeric identifier.
static constexpr int Patch = AZURE_CORE_VERSION_PATCH;
/// Optional pre-release identifier. SDK is in a pre-release state when not empty.
static std::string const PreRelease;
/**
* @brief The version in string format used for telemetry following the `semver.org` standard
* (https://semver.org).
*/
static std::string const VersionString();
private: