From dfdaf25223ccd9ca50e7aea4e86d684cc09e30e2 Mon Sep 17 00:00:00 2001 From: Victor Vazquez Date: Mon, 5 Apr 2021 12:11:31 -0700 Subject: [PATCH] [Core] Refactor RequestFailedException with Http information (#2026) * KeyVault Exception --- sdk/core/azure-core/CMakeLists.txt | 3 + .../azure-core/inc/azure/core/exception.hpp | 71 +++++- .../azure-core/inc/azure/core/http/http.hpp | 235 ++---------------- .../inc/azure/core/http/http_status_code.hpp | 94 +++++++ .../inc/azure/core/http/raw_response.hpp | 147 +++++++++++ sdk/core/azure-core/src/exception.cpp | 31 +++ sdk/core/azure-core/src/http/http.cpp | 6 + sdk/core/azure-core/src/http/raw_response.cpp | 1 + .../keyvault/common/keyvault_constants.hpp | 6 - .../keyvault/common/keyvault_exception.hpp | 75 +----- .../src/keyvault_exception.cpp | 60 +---- .../src/keyvault_pipeline.cpp | 10 +- .../src/delete_key_operation.cpp | 44 ++-- .../src/recover_deleted_key_operation.cpp | 44 ++-- .../storage/common/storage_exception.hpp | 7 - 15 files changed, 453 insertions(+), 381 deletions(-) create mode 100644 sdk/core/azure-core/inc/azure/core/http/http_status_code.hpp create mode 100644 sdk/core/azure-core/inc/azure/core/http/raw_response.hpp create mode 100644 sdk/core/azure-core/src/exception.cpp diff --git a/sdk/core/azure-core/CMakeLists.txt b/sdk/core/azure-core/CMakeLists.txt index 5d68fe840..5146f00c6 100644 --- a/sdk/core/azure-core/CMakeLists.txt +++ b/sdk/core/azure-core/CMakeLists.txt @@ -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 diff --git a/sdk/core/azure-core/inc/azure/core/exception.hpp b/sdk/core/azure-core/inc/azure/core/exception.hpp index 2a1ef9fa1..6a14ac48a 100644 --- a/sdk/core/azure-core/inc/azure/core/exception.hpp +++ b/sdk/core/azure-core/inc/azure/core/exception.hpp @@ -8,7 +8,13 @@ #pragma once +#include "azure/core/http/http_status_code.hpp" +#include "azure/core/http/raw_response.hpp" + +#include #include +#include +#include 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 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 rawResponse); }; }} // namespace Azure::Core diff --git a/sdk/core/azure-core/inc/azure/core/http/http.hpp b/sdk/core/azure-core/inc/azure/core/http/http.hpp index c58f83114..4ae32163a 100644 --- a/sdk/core/azure-core/inc/azure/core/http/http.hpp +++ b/sdk/core/azure-core/inc/azure/core/http/http.hpp @@ -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 m_bodyStream; - std::vector m_body; - - explicit RawResponse( - int32_t majorVersion, - int32_t minorVersion, - HttpStatusCode statusCode, - std::string const& reasonPhrase, - std::unique_ptr 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 stream); - - /** - * @brief Set HTTP response body for this HTTP response. - * - * @param body HTTP response body bytes. - */ - void SetBody(std::vector 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 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& GetBody() { return this->m_body; } - - /** - * @brief Get HTTP response body as vector of bytes. - */ - std::vector 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 diff --git a/sdk/core/azure-core/inc/azure/core/http/http_status_code.hpp b/sdk/core/azure-core/inc/azure/core/http/http_status_code.hpp new file mode 100644 index 000000000..97f2a73bd --- /dev/null +++ b/sdk/core/azure-core/inc/azure/core/http/http_status_code.hpp @@ -0,0 +1,94 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// SPDX-License-Identifier: MIT + +/** + * @file + * @brief HTTP status code definition. + */ + +#pragma once + +#include + +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 diff --git a/sdk/core/azure-core/inc/azure/core/http/raw_response.hpp b/sdk/core/azure-core/inc/azure/core/http/raw_response.hpp new file mode 100644 index 000000000..b2d766932 --- /dev/null +++ b/sdk/core/azure-core/inc/azure/core/http/raw_response.hpp @@ -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 +#include +#include + +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 m_bodyStream; + std::vector m_body; + + explicit RawResponse( + int32_t majorVersion, + int32_t minorVersion, + HttpStatusCode statusCode, + std::string const& reasonPhrase, + std::unique_ptr 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 stream); + + /** + * @brief Set HTTP response body for this HTTP response. + * + * @param body HTTP response body bytes. + */ + void SetBody(std::vector 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 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& GetBody() { return this->m_body; } + + /** + * @brief Get HTTP response body as vector of bytes. + */ + std::vector const& GetBody() const { return this->m_body; } + }; +}}} // namespace Azure::Core::Http diff --git a/sdk/core/azure-core/src/exception.cpp b/sdk/core/azure-core/src/exception.cpp new file mode 100644 index 000000000..801406b95 --- /dev/null +++ b/sdk/core/azure-core/src/exception.cpp @@ -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 +#include +#include +#include + +using namespace Azure::Core::Http::_internal; + +namespace Azure { namespace Core { + + RequestFailedException::RequestFailedException( + const std::string& message, + std::unique_ptr 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 diff --git a/sdk/core/azure-core/src/http/http.cpp b/sdk/core/azure-core/src/http/http.cpp index 19c31b9a9..4e8b3a177 100644 --- a/sdk/core/azure-core/src/http/http.cpp +++ b/sdk/core/azure-core/src/http/http.cpp @@ -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, diff --git a/sdk/core/azure-core/src/http/raw_response.cpp b/sdk/core/azure-core/src/http/raw_response.cpp index 6299706bf..213630cff 100644 --- a/sdk/core/azure-core/src/http/raw_response.cpp +++ b/sdk/core/azure-core/src/http/raw_response.cpp @@ -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" diff --git a/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/keyvault_constants.hpp b/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/keyvault_constants.hpp index eb2ebab5a..40a56d17f 100644 --- a/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/keyvault_constants.hpp +++ b/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/keyvault_constants.hpp @@ -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"; diff --git a/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/keyvault_exception.hpp b/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/keyvault_exception.hpp index 18146d422..71174e833 100644 --- a/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/keyvault_exception.hpp +++ b/sdk/keyvault/azure-security-keyvault-common/inc/azure/keyvault/common/keyvault_exception.hpp @@ -11,7 +11,6 @@ #include #include -#include #include #include @@ -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 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 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 rawResponse); }; }}} // namespace Azure::Security::KeyVault diff --git a/sdk/keyvault/azure-security-keyvault-common/src/keyvault_exception.cpp b/sdk/keyvault/azure-security-keyvault-common/src/keyvault_exception.cpp index 39652a895..3f32fccdf 100644 --- a/sdk/keyvault/azure-security-keyvault-common/src/keyvault_exception.cpp +++ b/sdk/keyvault/azure-security-keyvault-common/src/keyvault_exception.cpp @@ -10,64 +10,26 @@ #include 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 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 response) -{ - return CreateFromResponse(*response); -} - -KeyVaultException KeyVaultException::CreateFromResponse( - Azure::Core::Http::RawResponse const& response) -{ - std::vector 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 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(); - message = error["message"].get(); + ErrorCode = error["code"].get(); + Message = error["message"].get(); } else { - message = std::string(bodyBuffer.begin(), bodyBuffer.end()); + Message = std::string(bodyBuffer.begin(), bodyBuffer.end()); } - - KeyVaultException result = KeyVaultException( - std::to_string(static_cast::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(response); - return result; } diff --git a/sdk/keyvault/azure-security-keyvault-common/src/keyvault_pipeline.cpp b/sdk/keyvault/azure-security-keyvault-common/src/keyvault_pipeline.cpp index dc6ddf743..f1a006d9b 100644 --- a/sdk/keyvault/azure-security-keyvault-common/src/keyvault_pipeline.cpp +++ b/sdk/keyvault/azure-security-keyvault-common/src/keyvault_pipeline.cpp @@ -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 _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 _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; } diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/delete_key_operation.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/delete_key_operation.cpp index 047b48cb2..e206a120a 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/delete_key_operation.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/delete_key_operation.cpp @@ -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::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); diff --git a/sdk/keyvault/azure-security-keyvault-keys/src/recover_deleted_key_operation.cpp b/sdk/keyvault/azure-security-keyvault-keys/src/recover_deleted_key_operation.cpp index 8c9d72866..7c89b6ddd 100644 --- a/sdk/keyvault/azure-security-keyvault-keys/src/recover_deleted_key_operation.cpp +++ b/sdk/keyvault/azure-security-keyvault-keys/src/recover_deleted_key_operation.cpp @@ -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::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 diff --git a/sdk/storage/azure-storage-common/inc/azure/storage/common/storage_exception.hpp b/sdk/storage/azure-storage-common/inc/azure/storage/common/storage_exception.hpp index 4f2a4d31c..104c837cd 100644 --- a/sdk/storage/azure-storage-common/inc/azure/storage/common/storage_exception.hpp +++ b/sdk/storage/azure-storage-common/inc/azure/storage/common/storage_exception.hpp @@ -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 AdditionalInformation; - std::unique_ptr RawResponse; static StorageException CreateFromResponse( std::unique_ptr response);