/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #pragma once #include #include #include namespace Aws { namespace Client { class AWSAuthSigner; } namespace Http { class HttpClient; class HttpRequest; class HttpResponse; } } namespace AWSCore { ///////////////////////////////////////////// // HTTP Request run on a background thread ///////////////////////////////////////////// class HttpRequestJob : public AwsApiJob { public: enum class HttpMethod { HTTP_GET, HTTP_POST, HTTP_DELETE, HTTP_PUT, HTTP_HEAD, HTTP_PATCH }; enum class HeaderField { DATE, AWS_DATE, AWS_SECURITY_TOKEN, ACCEPT, ACCEPT_CHAR_SET, ACCEPT_ENCODING, AUTHORIZATION, AWS_AUTHORIZATION, COOKIE, CONTENT_LENGTH, CONTENT_TYPE, USER_AGENT, VIA, HOST, AMZ_TARGET, X_AMZ_EXPIRES, CONTENT_MD5, }; class Response { public: const AZStd::string& GetResponseBody() const; int GetResponseCode() const; const std::shared_ptr& GetUnderlyingResponse() const; private: std::shared_ptr m_response; AZStd::string m_responseBody; int m_responseCode = 0; friend class HttpRequestJob; }; using SuccessFn = AZStd::function& response)>; using FailureFn = AZStd::function& response)>; using StringMap = AZStd::unordered_map; public: // To use a different allocator, extend this class and use this macro. AZ_CLASS_ALLOCATOR(HttpRequestJob, AZ::SystemAllocator); using IConfig = IHttpRequestJobConfig; using Config = HttpRequestJobConfig; static Config* GetDefaultConfig() { static AwsApiJobConfigHolder s_configHolder{}; return s_configHolder.GetConfig(AwsApiJob::GetDefaultConfig()); } static void StaticInit(); static void StaticShutdown(); public: HttpRequestJob(bool isAutoDelete, IConfig* config) : AwsApiJob(isAutoDelete, config) , m_readRateLimiter(config->GetReadRateLimiter()) , m_writeRateLimiter(config->GetWriteRateLimiter()) , m_httpClient(config->GetHttpClient()) { SetRequestHeader(HeaderField::USER_AGENT, Util::ToAZString(config->GetUserAgent())); } /// Get and set the URL for this request void SetUrl(AZStd::string url); const AZStd::string& GetUrl() const; /// Get and set the HTTP method for this request void SetMethod(HttpMethod method); bool SetMethod(const AZStd::string& method); HttpMethod GetMethod() const; /// Get and set headers for the HTTP request void SetRequestHeader(AZStd::string key, AZStd::string value); bool GetRequestHeader(const AZStd::string& key, AZStd::string* result = nullptr); /// Get and set pre-defined header fields. Equivalent to manually setting the corresponding header field, e.g. /// HeaderField::CONTENT_LENGTH maps to "Content-Length". void SetRequestHeader(HeaderField field, AZStd::string value); bool GetRequestHeader(HeaderField field, AZStd::string* result = nullptr); /// Get the collection of all request headers. StringMap& GetRequestHeaders(); const StringMap& GetRequestHeaders() const; /// Syntactic sugar to set popular headers. Equivalent to manually setting the corresponding header field. void SetAccept(AZStd::string accept); void SetAcceptCharSet(AZStd::string accept); void SetContentLength(AZStd::string contentLength); void SetContentType(AZStd::string contentType); /// Get and set AWS authorization signer for the request void SetAWSAuthSigner(const std::shared_ptr& authSigner); const std::shared_ptr& GetAWSAuthSigner() const; /// Get and set the body for the HTTP request. (You are responsible for setting the Content-Length header.) void SetBody(AZStd::string body); const AZStd::string& GetBody() const; AZStd::string& GetBody(); /// Set callback functions for success and failure. These will be executed on the main thread. template void SetCallbacks(SuccessCallbackT&& successCB, FailureCallbackT&& failureCB); /// Converts an HttpMethod to a string. Used for debug output. static const char* HttpMethodToString(HttpMethod); static const char* HttpMethodToString(Aws::Http::HttpMethod); /// Converts a string to an HttpMethod. static AZStd::optional StringToHttpMethod(const AZStd::string& method); protected: /// Override to provide a custom instantiation of the HttpRequest. /// The default implementation uses the URL and method specified in this class. /// This is required mainly because Aws::Http::HttpRequest demands that these parameters be configured at the time of construction. /// You can also use this opportunity to set properties of the HttpRequestJob you want reflected in the request. /// WARNING: This gets called on the job's thread, so observe thread safety precautions. virtual std::shared_ptr InitializeRequest(); /// Override to customize HTTP request right before it is sent. /// Configuration specified in this class will already have been configured in the request. /// WARNING: This gets called on the job's thread, so observe thread safety precautions. virtual void CustomizeRequest(const std::shared_ptr& request) { AZ_UNUSED(request); }; /// Override to process the response to the HTTP request before callbacks are fired. /// WARNING: This gets called on the job's thread, so observe thread safety precautions. virtual void ProcessResponse(const std::shared_ptr& response) { AZ_UNUSED(response); }; private: /// Runs the HTTP request on the Job's thread. void Process() override; private: std::shared_ptr m_readRateLimiter; std::shared_ptr m_writeRateLimiter; std::shared_ptr m_httpClient; std::shared_ptr m_awsAuthSigner; SuccessFn m_successCallback; FailureFn m_failureCallback; StringMap m_requestHeaders; AZStd::string m_url; AZStd::string m_requestBody; HttpMethod m_method = HttpMethod::HTTP_GET; }; template inline void HttpRequestJob::SetCallbacks(SuccessCallbackT&& successCB, FailureCallbackT&& failureCB) { m_successCallback = AZStd::forward(successCB); m_failureCallback = AZStd::forward(failureCB); } } // namespace AWSCore