[Core] Refactor RequestFailedException with Http information (#2026)

* KeyVault Exception
This commit is contained in:
Victor Vazquez 2021-04-05 12:11:31 -07:00 committed by GitHub
parent a7fdea42e6
commit dfdaf25223
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 453 additions and 381 deletions

View File

@ -49,7 +49,9 @@ set(
inc/azure/core/credentials/token_credential_options.hpp
inc/azure/core/cryptography/hash.hpp
inc/azure/core/diagnostics/logger.hpp
inc/azure/core/http/http_status_code.hpp
inc/azure/core/http/http.hpp
inc/azure/core/http/raw_response.hpp
inc/azure/core/http/policies/policy.hpp
inc/azure/core/http/transport.hpp
inc/azure/core/internal/client_options.hpp
@ -104,6 +106,7 @@ set(
src/datetime.cpp
src/environment_log_level_listener.cpp
src/environment_log_level_listener_private.hpp
src/exception.cpp
src/logger.cpp
src/operation_status.cpp
src/strings.cpp

View File

@ -8,7 +8,13 @@
#pragma once
#include "azure/core/http/http_status_code.hpp"
#include "azure/core/http/raw_response.hpp"
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
namespace Azure { namespace Core {
/**
@ -17,11 +23,74 @@ namespace Azure { namespace Core {
*/
class RequestFailedException : public std::runtime_error {
public:
/**
* @brief The Http response code.
*
*/
Azure::Core::Http::HttpStatusCode StatusCode = Azure::Core::Http::HttpStatusCode::None;
/**
* @brief The Http reason phrase from the response.
*
*/
std::string ReasonPhrase;
/**
* @brief The client request header from the Http response.
*
*/
std::string ClientRequestId;
/**
* @brief The request id header from the Http response.
*
*/
std::string RequestId;
/**
* @brief The error code from service returned in the Http response.
*
*/
std::string ErrorCode;
/**
* @brief The error message from the service returned in the Http response.
*
*/
std::string Message;
/**
* @brief The entire Http raw response.
*
*/
std::unique_ptr<Azure::Core::Http::RawResponse> RawResponse;
/**
* @brief Construct a new Request Failed Exception object.
*
* @remark An Exception without an Http raw response represent an exception happend
* before sending the request to the server. There is no response yet.
*
* @param message The error description.
*/
explicit RequestFailedException(std::string const& message) : std::runtime_error(message) {}
explicit RequestFailedException(std::string const& message)
: std::runtime_error(message), Message(message)
{
}
/**
* @brief Construct a new Request Failed Exception object with an Http raw response.
*
* @remark The Http raw response is parsed to get the always expected information for all Azure
* Services like the status code, reason phrase and some headers like the request id. A concrete
* Service exception which derives from this exception uses its constructor to parse the Http
* raw response and assing the service specific values to the exception.
*
* @param message The error description.
* @param rawResponse The Http raw response from the service.
*/
explicit RequestFailedException(
const std::string& message,
std::unique_ptr<Azure::Core::Http::RawResponse> rawResponse);
};
}} // namespace Azure::Core

View File

@ -9,7 +9,10 @@
#pragma once
#include "azure/core/case_insensitive_containers.hpp"
#include "azure/core/dll_import_export.hpp"
#include "azure/core/exception.hpp"
#include "azure/core/http/http_status_code.hpp"
#include "azure/core/http/raw_response.hpp"
#include "azure/core/internal/contract.hpp"
#include "azure/core/io/body_stream.hpp"
#include "azure/core/nullable.hpp"
@ -58,86 +61,6 @@ namespace Azure { namespace Core { namespace Http {
}
};
/**
* @brief Defines the possible HTTP status codes.
*/
enum class HttpStatusCode
{
/// No HTTP status code.
None = 0,
// === 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, ///< 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, ///< 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, ///< 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, ///< 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.
};
/**
* @brief Defines a range of bytes within an HTTP resource, starting at an `Offset` and ending at
* `Offset + Length - 1` inclusively.
@ -340,134 +263,6 @@ namespace Azure { namespace Core { namespace Http {
Url const& GetUrl() const { return this->m_url; }
};
/**
* @brief Raw HTTP response.
*/
class RawResponse {
private:
int32_t m_majorVersion;
int32_t m_minorVersion;
HttpStatusCode m_statusCode;
std::string m_reasonPhrase;
CaseInsensitiveMap m_headers;
std::unique_ptr<Azure::Core::IO::BodyStream> m_bodyStream;
std::vector<uint8_t> m_body;
explicit RawResponse(
int32_t majorVersion,
int32_t minorVersion,
HttpStatusCode statusCode,
std::string const& reasonPhrase,
std::unique_ptr<Azure::Core::IO::BodyStream> BodyStream)
: m_majorVersion(majorVersion), m_minorVersion(minorVersion), m_statusCode(statusCode),
m_reasonPhrase(reasonPhrase), m_bodyStream(std::move(BodyStream))
{
}
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,
HttpStatusCode statusCode,
std::string const& reasonPhrase)
: RawResponse(majorVersion, minorVersion, statusCode, reasonPhrase, nullptr)
{
}
/**
* @brief Copy a raw response to construct a new one.
*
* @remark The body stream won't be copied.
*
* @param response A reference for copying the raw response.
*/
RawResponse(RawResponse const& response)
: RawResponse(
response.m_majorVersion,
response.m_minorVersion,
response.m_statusCode,
response.m_reasonPhrase)
{
// Copy body
m_body = response.GetBody();
}
// ===== Methods used to build HTTP response =====
/**
* @brief Set an HTTP header to the #RawResponse.
*
* @remark The \p name must contain valid header name characters (RFC 7230).
*
* @param name The name for the header to be set or added.
* @param value The value for the header to be set or added.
*
* @throw if \p name contains invalid characters.
*/
void SetHeader(std::string const& name, std::string const& value);
/**
* @brief Set #Azure::Core::IO::BodyStream for this HTTP response.
*
* @param stream #Azure::Core::IO::BodyStream.
*/
void SetBodyStream(std::unique_ptr<Azure::Core::IO::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 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.
*/
CaseInsensitiveMap const& GetHeaders() const;
/**
* @brief Get HTTP response body as #Azure::Core::IO::BodyStream.
*/
std::unique_ptr<Azure::Core::IO::BodyStream> ExtractBodyStream()
{
// If m_bodyStream was moved before. nullptr 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; }
};
namespace _detail {
struct RawResponseHelpers
{
@ -517,4 +312,28 @@ namespace Azure { namespace Core { namespace Http {
};
} // namespace _detail
namespace _internal {
struct HttpShared
{
AZ_CORE_DLLEXPORT static char const ContentType[];
AZ_CORE_DLLEXPORT static char const ApplicationJson[];
AZ_CORE_DLLEXPORT static char const Accept[];
AZ_CORE_DLLEXPORT static char const MsRequestId[];
AZ_CORE_DLLEXPORT static char const MsClientRequestId[];
static inline std::string GetHeaderOrEmptyString(
Azure::Core::CaseInsensitiveMap const& headers,
std::string const& headerName)
{
auto header = headers.find(headerName);
if (header != headers.end())
{
return header->second; // second is the header value.
}
return {}; // empty string
}
};
} // namespace _internal
}}} // namespace Azure::Core::Http

View File

@ -0,0 +1,94 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief HTTP status code definition.
*/
#pragma once
#include <string>
namespace Azure { namespace Core { namespace Http {
/**
* @brief Defines the possible HTTP status codes.
*/
enum class HttpStatusCode
{
/// No HTTP status code.
None = 0,
// === 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, ///< 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, ///< 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, ///< 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, ///< 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.
};
}}} // namespace Azure::Core::Http

View File

@ -0,0 +1,147 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
/**
* @file
* @brief Define the Http raw response.
*/
#pragma once
#include "azure/core/case_insensitive_containers.hpp"
#include "azure/core/http/http_status_code.hpp"
#include "azure/core/io/body_stream.hpp"
#include <memory>
#include <string>
#include <vector>
namespace Azure { namespace Core { namespace Http {
/**
* @brief Raw HTTP response.
*/
class RawResponse {
private:
int32_t m_majorVersion;
int32_t m_minorVersion;
HttpStatusCode m_statusCode;
std::string m_reasonPhrase;
CaseInsensitiveMap m_headers;
std::unique_ptr<Azure::Core::IO::BodyStream> m_bodyStream;
std::vector<uint8_t> m_body;
explicit RawResponse(
int32_t majorVersion,
int32_t minorVersion,
HttpStatusCode statusCode,
std::string const& reasonPhrase,
std::unique_ptr<Azure::Core::IO::BodyStream> BodyStream)
: m_majorVersion(majorVersion), m_minorVersion(minorVersion), m_statusCode(statusCode),
m_reasonPhrase(reasonPhrase), m_bodyStream(std::move(BodyStream))
{
}
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,
HttpStatusCode statusCode,
std::string const& reasonPhrase)
: RawResponse(majorVersion, minorVersion, statusCode, reasonPhrase, nullptr)
{
}
/**
* @brief Copy a raw response to construct a new one.
*
* @remark The body stream won't be copied.
*
* @param response A reference for copying the raw response.
*/
RawResponse(RawResponse const& response)
: RawResponse(
response.m_majorVersion,
response.m_minorVersion,
response.m_statusCode,
response.m_reasonPhrase)
{
// Copy body
m_body = response.GetBody();
}
// ===== Methods used to build HTTP response =====
/**
* @brief Set an HTTP header to the #RawResponse.
*
* @remark The \p name must contain valid header name characters (RFC 7230).
*
* @param name The name for the header to be set or added.
* @param value The value for the header to be set or added.
*
* @throw if \p name contains invalid characters.
*/
void SetHeader(std::string const& name, std::string const& value);
/**
* @brief Set #Azure::Core::IO::BodyStream for this HTTP response.
*
* @param stream #Azure::Core::IO::BodyStream.
*/
void SetBodyStream(std::unique_ptr<Azure::Core::IO::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 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.
*/
CaseInsensitiveMap const& GetHeaders() const;
/**
* @brief Get HTTP response body as #Azure::Core::IO::BodyStream.
*/
std::unique_ptr<Azure::Core::IO::BodyStream> ExtractBodyStream()
{
// If m_bodyStream was moved before. nullptr 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; }
};
}}} // namespace Azure::Core::Http

View File

@ -0,0 +1,31 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "azure/core/exception.hpp"
#include "azure/core/http/http.hpp"
#include <memory>
#include <stdexcept>
#include <string>
#include <vector>
using namespace Azure::Core::Http::_internal;
namespace Azure { namespace Core {
RequestFailedException::RequestFailedException(
const std::string& message,
std::unique_ptr<Azure::Core::Http::RawResponse> rawResponse)
: std::runtime_error(message), Message(message)
{
auto const& headers = rawResponse->GetHeaders();
StatusCode = rawResponse->GetStatusCode();
ReasonPhrase = rawResponse->GetReasonPhrase();
RequestId = HttpShared::GetHeaderOrEmptyString(headers, HttpShared::MsRequestId);
ClientRequestId = HttpShared::GetHeaderOrEmptyString(headers, HttpShared::MsClientRequestId);
Message = message;
RawResponse = std::move(rawResponse);
}
}} // namespace Azure::Core

View File

@ -12,6 +12,12 @@ using namespace Azure::Core;
using namespace Azure::Core::Http;
using namespace Azure::Core::IO::_internal;
char const Azure::Core::Http::_internal::HttpShared::ContentType[] = "content-type";
char const Azure::Core::Http::_internal::HttpShared::ApplicationJson[] = "application/json";
char const Azure::Core::Http::_internal::HttpShared::Accept[] = "accept";
char const Azure::Core::Http::_internal::HttpShared::MsRequestId[] = "x-ms-request-id";
char const Azure::Core::Http::_internal::HttpShared::MsClientRequestId[] = "x-ms-client-request-id";
void Azure::Core::Http::_detail::RawResponseHelpers::InsertHeaderWithValidation(
Azure::Core::CaseInsensitiveMap& headers,
std::string const& headerName,

View File

@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "azure/core/http/raw_response.hpp"
#include "azure/core/http/http.hpp"
#include "azure/core/internal/strings.hpp"

View File

@ -9,12 +9,6 @@
#pragma once
namespace Azure { namespace Security { namespace KeyVault { namespace _detail {
/***************** KeyVault headers *****************/
static constexpr char const ContentType[] = "content-type";
static constexpr char const ApplicationJson[] = "application/json";
static constexpr char const Accept[] = "accept";
static constexpr char const MsRequestId[] = "x-ms-request-id";
static constexpr char const MsClientRequestId[] = "x-ms-client-request-id";
/**************** KeyVault QueryParameters *********/
static constexpr char const ApiVersion[] = "api-version";

View File

@ -11,7 +11,6 @@
#include <azure/core/exception.hpp>
#include <azure/core/http/http.hpp>
#include <map>
#include <stdexcept>
#include <string>
@ -21,74 +20,26 @@ namespace Azure { namespace Security { namespace KeyVault {
* @brief The general exception thrown by the Key Vault SDK clients.
*
*/
struct KeyVaultException : public Azure::Core::RequestFailedException
{
class KeyVaultException : public Azure::Core::RequestFailedException {
public:
/**
* @brief Construct a new Key Vault Exception object.
* @brief Construct a new Key Vault Exception object without an Http raw response.
*
* @param message
* @remark A Key Vault Exception without an Http raw response represent an exception happend
* before sending the request to the server. There is no response yet.
*
* @param message An error message for the exception.
*/
explicit KeyVaultException(const std::string& message) : RequestFailedException(message) {}
/**
* @brief The Http response code.
* @brief Construct a new Key Vault Exception object with an Http raw response.
*
* @param message An error message for the exception.
* @param rawResponse The Http raw response from the service.
*/
Azure::Core::Http::HttpStatusCode StatusCode = Azure::Core::Http::HttpStatusCode::None;
/**
* @brief The Http reason phrase from the response.
*
*/
std::string ReasonPhrase;
/**
* @brief The client request header from the Http response.
*
*/
std::string ClientRequestId;
/**
* @brief The request id header from the Http response.
*
*/
std::string RequestId;
/**
* @brief The error code from the Key Vault service returned in the Http response.
*
*/
std::string ErrorCode;
/**
* @brief The error message from the Key Vault service returned in the Http response.
*
*/
std::string Message;
/**
* @brief The entire Http raw response.
*
*/
std::unique_ptr<Azure::Core::Http::RawResponse> RawResponse;
/**
* @brief Create a #Azure::Security::KeyVault::KeyVaultException by parsing the \p
* response.
*
* @param response The Http raw response from the network.
* @return KeyVaultException.
*/
static KeyVaultException CreateFromResponse(
std::unique_ptr<Azure::Core::Http::RawResponse> response);
/**
* @brief Create #Azure::Security::KeyVault::KeyVaultException by parsing the \p
* response reference.
*
* @param response The Http raw response from the network.
* @return KeyVaultException.
*/
static KeyVaultException CreateFromResponse(Azure::Core::Http::RawResponse const& response);
explicit KeyVaultException(
const std::string& message,
std::unique_ptr<Azure::Core::Http::RawResponse> rawResponse);
};
}}} // namespace Azure::Security::KeyVault

View File

@ -10,64 +10,26 @@
#include <type_traits>
using namespace Azure::Security::KeyVault;
using namespace Azure::Core::Http::_internal;
namespace {
inline std::string GetHeaderOrEmptyString(
Azure::Core::CaseInsensitiveMap const& headers,
std::string const& headerName)
KeyVaultException::KeyVaultException(
const std::string& message,
std::unique_ptr<Azure::Core::Http::RawResponse> rawResponse)
: RequestFailedException(message, std::move(rawResponse))
{
auto header = headers.find(headerName);
if (header != headers.end())
{
return header->second; // second is the header value.
}
return {}; // empty string
}
} // namespace
KeyVaultException KeyVaultException::CreateFromResponse(
std::unique_ptr<Azure::Core::Http::RawResponse> response)
{
return CreateFromResponse(*response);
}
KeyVaultException KeyVaultException::CreateFromResponse(
Azure::Core::Http::RawResponse const& response)
{
std::vector<uint8_t> bodyBuffer = std::move(response.GetBody());
auto httpStatusCode = response.GetStatusCode();
std::string reasonPhrase = response.GetReasonPhrase();
auto& headers = response.GetHeaders();
std::string requestId = GetHeaderOrEmptyString(headers, _detail::MsRequestId);
std::string clientRequestId = GetHeaderOrEmptyString(headers, _detail::MsClientRequestId);
std::string contentType = GetHeaderOrEmptyString(headers, _detail::ContentType);
std::string errorCode;
std::string message;
std::vector<uint8_t> bodyBuffer = std::move(RawResponse->GetBody());
auto& headers = RawResponse->GetHeaders();
std::string contentType = HttpShared::GetHeaderOrEmptyString(headers, HttpShared::ContentType);
if (contentType.find("json") != std::string::npos)
{
auto jsonParser = Azure::Core::Json::_internal::json::parse(bodyBuffer);
auto& error = jsonParser["error"];
errorCode = error["code"].get<std::string>();
message = error["message"].get<std::string>();
ErrorCode = error["code"].get<std::string>();
Message = error["message"].get<std::string>();
}
else
{
message = std::string(bodyBuffer.begin(), bodyBuffer.end());
Message = std::string(bodyBuffer.begin(), bodyBuffer.end());
}
KeyVaultException result = KeyVaultException(
std::to_string(static_cast<std::underlying_type<Azure::Core::Http::HttpStatusCode>::type>(
httpStatusCode))
+ " " + reasonPhrase + "\n" + message + "\nRequest ID: " + requestId);
result.StatusCode = httpStatusCode;
result.ReasonPhrase = std::move(reasonPhrase);
result.RequestId = std::move(requestId);
result.ErrorCode = std::move(errorCode);
result.Message = std::move(message);
result.RawResponse = std::make_unique<Azure::Core::Http::RawResponse>(response);
return result;
}

View File

@ -1,11 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "azure/core/http/http.hpp"
#include "azure/keyvault/common/internal/keyvault_pipeline.hpp"
#include "azure/keyvault/common/keyvault_constants.hpp"
#include "azure/keyvault/common/keyvault_exception.hpp"
using namespace Azure::Security::KeyVault;
using namespace Azure::Core::Http::_internal;
namespace {
inline Azure::Core::Http::Request InitRequest(
@ -29,8 +32,8 @@ Azure::Core::Http::Request _internal::KeyVaultPipeline::CreateRequest(
auto request = ::InitRequest(method, content, m_vaultUrl);
request.SetHeader(_detail::ContentType, _detail::ApplicationJson);
request.SetHeader(_detail::Accept, _detail::ApplicationJson);
request.SetHeader(HttpShared::ContentType, HttpShared::ApplicationJson);
request.SetHeader(HttpShared::Accept, HttpShared::ApplicationJson);
request.GetUrl().AppendQueryParameter(_detail::ApiVersion, m_apiVersion);
@ -59,6 +62,7 @@ std::unique_ptr<Azure::Core::Http::RawResponse> _internal::KeyVaultPipeline::Sen
auto responseCode = response->GetStatusCode();
switch (responseCode)
{
// 200, 2001, 202, 204 are accepted responses
case Azure::Core::Http::HttpStatusCode::Ok:
case Azure::Core::Http::HttpStatusCode::Created:
@ -66,7 +70,7 @@ std::unique_ptr<Azure::Core::Http::RawResponse> _internal::KeyVaultPipeline::Sen
case Azure::Core::Http::HttpStatusCode::NoContent:
break;
default:
throw KeyVaultException::CreateFromResponse(std::move(response));
throw KeyVaultException("Key Vault Keys error response received: ", std::move(response));
}
return response;
}

View File

@ -1,32 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "azure/keyvault/common/keyvault_exception.hpp"
#include "azure/keyvault/keys/delete_key_operation.hpp"
#include "azure/keyvault/keys/details/key_constants.hpp"
#include "azure/keyvault/keys/details/key_serializers.hpp"
using namespace Azure::Security::KeyVault::Keys;
namespace {
// For delete key, the LRO ends when we can retreive the Key from the deleted keys list from the
// server.
inline Azure::Core::OperationStatus CheckCompleted(Azure::Core::Http::RawResponse const& response)
{
auto code = response.GetStatusCode();
switch (code)
{
case Azure::Core::Http::HttpStatusCode::Ok:
case Azure::Core::Http::HttpStatusCode::Forbidden: // Access denied but proof the key was
// deleted.
return Azure::Core::OperationStatus::Succeeded;
case Azure::Core::Http::HttpStatusCode::NotFound:
return Azure::Core::OperationStatus::Running;
default:
throw Azure::Security::KeyVault::KeyVaultException::CreateFromResponse(response);
}
}
} // namespace
using namespace Azure::Security::KeyVault;
std::unique_ptr<Azure::Core::Http::RawResponse>
Azure::Security::KeyVault::Keys::DeleteKeyOperation::PollInternal(Azure::Core::Context& context)
@ -36,7 +18,25 @@ Azure::Security::KeyVault::Keys::DeleteKeyOperation::PollInternal(Azure::Core::C
{
rawResponse = m_pipeline->Send(
context, Azure::Core::Http::HttpMethod::Get, {_detail::DeletedKeysPath, m_value.Name()});
m_status = CheckCompleted(*rawResponse);
switch (rawResponse->GetStatusCode())
{
case Azure::Core::Http::HttpStatusCode::Ok:
case Azure::Core::Http::HttpStatusCode::Forbidden: // Access denied but proof the key was
// deleted.
{
m_status = Azure::Core::OperationStatus::Succeeded;
break;
}
case Azure::Core::Http::HttpStatusCode::NotFound: {
m_status = Azure::Core::OperationStatus::Running;
break;
}
default:
throw KeyVaultException(
"Unexpected operation status from Service response.", std::move(rawResponse));
}
if (m_status == Azure::Core::OperationStatus::Succeeded)
{
m_value = _detail::DeletedKeySerializer::DeletedKeyDeserialize(m_value.Name(), *rawResponse);

View File

@ -1,32 +1,14 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// SPDX-License-Identifier: MIT
#include "azure/keyvault/keys/recover_deleted_key_operation.hpp"
#include "azure/keyvault/common/keyvault_exception.hpp"
#include "azure/keyvault/keys/details/key_constants.hpp"
#include "azure/keyvault/keys/details/key_serializers.hpp"
#include "azure/keyvault/keys/recover_deleted_key_operation.hpp"
using namespace Azure::Security::KeyVault::Keys;
namespace {
// For delete key, the LRO ends when we can retreive the Key from the deleted keys list from the
// server.
inline Azure::Core::OperationStatus CheckCompleted(Azure::Core::Http::RawResponse const& response)
{
auto const code = response.GetStatusCode();
switch (code)
{
case Azure::Core::Http::HttpStatusCode::Ok:
// Access denied but proof the key was deleted.
case Azure::Core::Http::HttpStatusCode::Forbidden:
return Azure::Core::OperationStatus::Succeeded;
case Azure::Core::Http::HttpStatusCode::NotFound:
return Azure::Core::OperationStatus::Running;
default:
throw Azure::Security::KeyVault::KeyVaultException::CreateFromResponse(response);
}
}
} // namespace
using namespace Azure::Security::KeyVault;
std::unique_ptr<Azure::Core::Http::RawResponse>
Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation::PollInternal(
@ -39,7 +21,23 @@ Azure::Security::KeyVault::Keys::RecoverDeletedKeyOperation::PollInternal(
context,
Azure::Core::Http::HttpMethod::Get,
{_detail::KeysPath, m_value.Name(), m_value.Properties.Version});
m_status = CheckCompleted(*rawResponse);
switch (rawResponse->GetStatusCode())
{
case Azure::Core::Http::HttpStatusCode::Ok:
// Access denied but proof the key was deleted.
case Azure::Core::Http::HttpStatusCode::Forbidden: {
m_status = Azure::Core::OperationStatus::Succeeded;
break;
}
case Azure::Core::Http::HttpStatusCode::NotFound: {
m_status = Azure::Core::OperationStatus::Running;
break;
}
default:
throw KeyVaultException(
"Unexpected operation status from Service response.", std::move(rawResponse));
}
if (m_status == Azure::Core::OperationStatus::Succeeded)
{
m_value

View File

@ -17,14 +17,7 @@ namespace Azure { namespace Storage {
{
explicit StorageException(const std::string& message) : RequestFailedException(message) {}
Azure::Core::Http::HttpStatusCode StatusCode = Azure::Core::Http::HttpStatusCode::None;
std::string ReasonPhrase;
std::string ClientRequestId;
std::string RequestId;
std::string ErrorCode;
std::string Message;
std::map<std::string, std::string> AdditionalInformation;
std::unique_ptr<Azure::Core::Http::RawResponse> RawResponse;
static StorageException CreateFromResponse(
std::unique_ptr<Azure::Core::Http::RawResponse> response);