| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471 |
- /*
- * 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
- *
- */
- #include <Framework/HttpRequestJob.h>
- // The AWS Native SDK AWSAllocator triggers a warning due to accessing members of std::allocator directly.
- // AWSAllocator.h(70): warning C4996: 'std::allocator<T>::pointer': warning STL4010: Various members of std::allocator are deprecated in C++17.
- // Use std::allocator_traits instead of accessing these members directly.
- // You can define _SILENCE_CXX17_OLD_ALLOCATOR_MEMBERS_DEPRECATION_WARNING or _SILENCE_ALL_CXX17_DEPRECATION_WARNINGS to acknowledge that you have received this warning.
- #include <AzCore/PlatformDef.h>
- AZ_PUSH_DISABLE_WARNING(4251 4996, "-Wunknown-warning-option")
- #include <aws/core/http/HttpRequest.h>
- #include <aws/core/http/HttpResponse.h>
- #include <aws/core/http/HttpClient.h>
- #include <aws/core/http/HttpClientFactory.h>
- #include <aws/core/utils/stream/ResponseStream.h>
- #include <aws/core/utils/memory/stl/AWSStringStream.h>
- #include <aws/core/auth/AWSAuthSigner.h>
- AZ_POP_DISABLE_WARNING
- #include <AzCore/Component/TickBus.h>
- #include <AzCore/std/smart_ptr/make_shared.h>
- namespace AWSCore
- {
- namespace
- {
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // Mappings from HttpRequestJob nested types to Aws types
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- struct HttpMethodInfo
- {
- Aws::Http::HttpMethod m_awsMethod;
- AZStd::string m_name;
- };
- // This will run the code fed to the macro, and then assign 0 to a static int (note the ,0 at the end)
- #define AWS_CORE_ONCE_PASTE(x) (x)
- #define AWS_CORE_ONCE(x) [[maybe_unused]] static int AZ_JOIN(init, __LINE__)((AWS_CORE_ONCE_PASTE(x), 0))
- #define AWS_CORE_HTTP_METHOD_ENTRY(x) { HttpRequestJob::HttpMethod::HTTP_##x, HttpMethodInfo{ Aws::Http::HttpMethod::HTTP_##x, #x } }
- using MethodLookup = AZStd::unordered_map<HttpRequestJob::HttpMethod, HttpMethodInfo>;
- AZ::EnvironmentVariable<MethodLookup> s_methodLookup = nullptr;
- const MethodLookup& GetMethodLookup()
- {
- AWS_CORE_ONCE(
- s_methodLookup = AZ::Environment::CreateVariable<MethodLookup>("methodlookup.httprequestjob.awscore", MethodLookup
- {
- AWS_CORE_HTTP_METHOD_ENTRY(GET),
- AWS_CORE_HTTP_METHOD_ENTRY(POST),
- AWS_CORE_HTTP_METHOD_ENTRY(DELETE),
- AWS_CORE_HTTP_METHOD_ENTRY(PUT),
- AWS_CORE_HTTP_METHOD_ENTRY(HEAD),
- AWS_CORE_HTTP_METHOD_ENTRY(PATCH),
- })
- );
- return *s_methodLookup;
- }
- #undef CLOUD_CANVAS_HTTP_METHOD_ENTRY
- #define AWS_CORE_HTTP_METHOD_AWS_ENTRY(x) { Aws::Http::HttpMethod::HTTP_##x, HttpRequestJob::HttpMethod::HTTP_##x }
- using MethodAwsReverseLookup = AZStd::unordered_map<Aws::Http::HttpMethod, HttpRequestJob::HttpMethod>;
- AZ::EnvironmentVariable<MethodAwsReverseLookup> s_methodAwsReverseLookup = nullptr;
- const MethodAwsReverseLookup& GetMethodAwsReverseLookup()
- {
- AWS_CORE_ONCE(s_methodAwsReverseLookup = AZ::Environment::CreateVariable<MethodAwsReverseLookup>("methodawsreverselookup.httprequestjob.awscore", MethodAwsReverseLookup
- {
- AWS_CORE_HTTP_METHOD_AWS_ENTRY(GET),
- AWS_CORE_HTTP_METHOD_AWS_ENTRY(POST),
- AWS_CORE_HTTP_METHOD_AWS_ENTRY(DELETE),
- AWS_CORE_HTTP_METHOD_AWS_ENTRY(PUT),
- AWS_CORE_HTTP_METHOD_AWS_ENTRY(HEAD),
- AWS_CORE_HTTP_METHOD_AWS_ENTRY(PATCH),
- })
- );
- return *s_methodAwsReverseLookup;
- }
- #undef AWS_CORE_HTTP_METHOD_AWS_ENTRY
- #define AWS_CORE_HTTP_METHOD_STRING_ENTRY(x) { #x, HttpRequestJob::HttpMethod::HTTP_##x }
- using MethodStringReverseLookup = AZStd::unordered_map<AZStd::string, HttpRequestJob::HttpMethod>;
- AZ::EnvironmentVariable<MethodStringReverseLookup> s_methodStringReverseLookup = nullptr;
- const MethodStringReverseLookup& GetMethodStringReverseLookup()
- {
- AWS_CORE_ONCE(s_methodStringReverseLookup = AZ::Environment::CreateVariable<MethodStringReverseLookup>("methodstringreverselookup.httprequestjob.awscore", MethodStringReverseLookup
- {
- AWS_CORE_HTTP_METHOD_STRING_ENTRY(GET),
- AWS_CORE_HTTP_METHOD_STRING_ENTRY(POST),
- AWS_CORE_HTTP_METHOD_STRING_ENTRY(DELETE),
- AWS_CORE_HTTP_METHOD_STRING_ENTRY(PUT),
- AWS_CORE_HTTP_METHOD_STRING_ENTRY(HEAD),
- AWS_CORE_HTTP_METHOD_STRING_ENTRY(PATCH),
- })
- );
- return *s_methodStringReverseLookup;
- }
- #undef AWS_CORE_HTTP_METHOD_STRING_ENTRY
- #define AWS_CORE_HEADER_FIELD_ENTRY(x) { HttpRequestJob::HeaderField::x, Aws::Http::x##_HEADER }
- using HeaderLookup = AZStd::unordered_map<HttpRequestJob::HeaderField, AZStd::string>;
- AZ::EnvironmentVariable<HeaderLookup> s_headerLookup = nullptr;
- const HeaderLookup& GetHeaderLookup()
- {
- AWS_CORE_ONCE(s_headerLookup = AZ::Environment::CreateVariable<HeaderLookup>("headerlookup.httprequestjob.awscore", HeaderLookup
- {
- AWS_CORE_HEADER_FIELD_ENTRY(DATE),
- AWS_CORE_HEADER_FIELD_ENTRY(AWS_DATE),
- { HttpRequestJob::HeaderField::AWS_SECURITY_TOKEN, Aws::Http::AWS_SECURITY_TOKEN },
- AWS_CORE_HEADER_FIELD_ENTRY(ACCEPT),
- AWS_CORE_HEADER_FIELD_ENTRY(ACCEPT_CHAR_SET),
- AWS_CORE_HEADER_FIELD_ENTRY(ACCEPT_ENCODING),
- AWS_CORE_HEADER_FIELD_ENTRY(AUTHORIZATION),
- AWS_CORE_HEADER_FIELD_ENTRY(AWS_AUTHORIZATION),
- AWS_CORE_HEADER_FIELD_ENTRY(COOKIE),
- AWS_CORE_HEADER_FIELD_ENTRY(CONTENT_LENGTH),
- AWS_CORE_HEADER_FIELD_ENTRY(CONTENT_TYPE),
- AWS_CORE_HEADER_FIELD_ENTRY(USER_AGENT),
- AWS_CORE_HEADER_FIELD_ENTRY(VIA),
- AWS_CORE_HEADER_FIELD_ENTRY(HOST),
- AWS_CORE_HEADER_FIELD_ENTRY(AMZ_TARGET),
- AWS_CORE_HEADER_FIELD_ENTRY(X_AMZ_EXPIRES),
- AWS_CORE_HEADER_FIELD_ENTRY(CONTENT_MD5),
- })
- );
- return *s_headerLookup;
- }
- #undef AWS_CORE_HEADER_FIELD_ENTRY
- #undef AWS_CORE_ONCE_PASTE
- #undef AWS_CORE_ONCE
- template<typename MapT>
- inline const typename MapT::mapped_type* FindInMap(const MapT& haystack, const typename MapT::key_type& needle)
- {
- const typename MapT::mapped_type* result = nullptr;
- auto itr = haystack.find(needle);
- if (itr != haystack.end())
- {
- result = &itr->second;
- }
- return result;
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // HttpRequestJob methods
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- void HttpRequestJob::StaticInit()
- {
- GetMethodLookup();
- GetMethodAwsReverseLookup();
- GetMethodStringReverseLookup();
- GetHeaderLookup();
- }
- void HttpRequestJob::StaticShutdown()
- {
- s_methodLookup.Reset();
- s_methodAwsReverseLookup.Reset();
- s_methodStringReverseLookup.Reset();
- s_headerLookup.Reset();
- }
- void HttpRequestJob::SetUrl(AZStd::string url)
- {
- m_url = url;
- }
- const AZStd::string& HttpRequestJob::GetUrl() const
- {
- return m_url;
- }
- void HttpRequestJob::SetMethod(HttpMethod method)
- {
- m_method = method;
- }
- bool HttpRequestJob::SetMethod(const AZStd::string& method)
- {
- bool result = false;
- if (AZStd::optional<HttpMethod> value = StringToHttpMethod(method))
- {
- SetMethod(*value);
- result = true;
- }
- return result;
- }
- HttpRequestJob::HttpMethod HttpRequestJob::GetMethod() const
- {
- return m_method;
- }
- void HttpRequestJob::SetRequestHeader(AZStd::string key, AZStd::string value)
- {
- m_requestHeaders.emplace(AZStd::move(key), AZStd::move(value));
- }
- bool HttpRequestJob::GetRequestHeader(const AZStd::string& key, AZStd::string* result)
- {
- bool found = false;
- auto itr = m_requestHeaders.find(key);
- if (itr != m_requestHeaders.end())
- {
- found = true;
- if (result)
- {
- *result = itr->second;
- }
- }
- return found;
- }
- void HttpRequestJob::SetRequestHeader(HeaderField field, AZStd::string value)
- {
- if (auto headerString = FindInMap(GetHeaderLookup(), field))
- {
- SetRequestHeader(*headerString, value);
- }
- }
- bool HttpRequestJob::GetRequestHeader(HeaderField field, AZStd::string* result)
- {
- bool found = false;
- if (auto headerString = FindInMap(GetHeaderLookup(), field))
- {
- found = GetRequestHeader(*headerString, result);
- }
- return found;
- }
- HttpRequestJob::StringMap& HttpRequestJob::GetRequestHeaders()
- {
- return m_requestHeaders;
- }
- const HttpRequestJob::StringMap& HttpRequestJob::GetRequestHeaders() const
- {
- return m_requestHeaders;
- }
- void HttpRequestJob::SetAccept(AZStd::string accept)
- {
- SetRequestHeader(HeaderField::ACCEPT, accept);
- }
- void HttpRequestJob::SetAcceptCharSet(AZStd::string acceptCharSet)
- {
- SetRequestHeader(HeaderField::ACCEPT_CHAR_SET, acceptCharSet);
- }
- void HttpRequestJob::SetContentLength(AZStd::string contentLength)
- {
- SetRequestHeader(HeaderField::CONTENT_LENGTH, contentLength);
- }
- void HttpRequestJob::SetContentType(AZStd::string contentType)
- {
- SetRequestHeader(HeaderField::CONTENT_TYPE, contentType);
- }
- void HttpRequestJob::SetAWSAuthSigner(const std::shared_ptr<Aws::Client::AWSAuthSigner>& authSigner)
- {
- m_awsAuthSigner = authSigner;
- }
- const std::shared_ptr<Aws::Client::AWSAuthSigner>& HttpRequestJob::GetAWSAuthSigner() const
- {
- return m_awsAuthSigner;
- }
- void HttpRequestJob::SetBody(AZStd::string body)
- {
- m_requestBody = std::move(body);
- }
- const AZStd::string& HttpRequestJob::GetBody() const
- {
- return m_requestBody;
- }
- AZStd::string& HttpRequestJob::GetBody()
- {
- return m_requestBody;
- }
- const char* HttpRequestJob::HttpMethodToString(HttpMethod method)
- {
- const char* result = nullptr;
- if (auto methodInfo = FindInMap(GetMethodLookup(), method))
- {
- result = methodInfo->m_name.c_str();
- }
- return result;
- }
- const char* HttpRequestJob::HttpMethodToString(Aws::Http::HttpMethod method)
- {
- const char* result = nullptr;
- if (auto convertedMethod = FindInMap(GetMethodAwsReverseLookup(), method))
- {
- result = HttpMethodToString(*convertedMethod);
- }
- return result;
- }
- AZStd::optional<HttpRequestJob::HttpMethod> HttpRequestJob::StringToHttpMethod(const AZStd::string& method)
- {
- AZStd::optional<HttpRequestJob::HttpMethod> result;
- const auto& haystack = GetMethodStringReverseLookup();
- auto itr = haystack.find(method);
- if (itr != haystack.end())
- {
- result = itr->second;
- }
- return result;
- }
- void HttpRequestJob::Process()
- {
- // Someday the AWS Http client may support real async I/O. The
- // GetRequest and OnResponse methods are designed with that in
- ///mind. When that feature is available, we can use the AZ::Job
- // defined IncrementDependentCount method, start the async i/o,
- // and call WaitForChildren. When the i/o completes, we would call
- // DecrementDependentCount, which would cause WaitForChildren to
- // return. We would then call OnResponse.
- // Create the request
- std::shared_ptr<Aws::Http::HttpRequest> request = InitializeRequest();
- std::shared_ptr<Aws::Http::HttpResponse> httpResponse;
- if (request)
- {
- // Populate headers
- for (const auto& header : m_requestHeaders)
- {
- request->SetHeaderValue(Util::ToAwsString(header.first), Util::ToAwsString(header.second));
- }
- // Populate the body
- if (!m_requestBody.empty())
- {
- auto body = std::make_shared<Aws::StringStream>();
- body->write(m_requestBody.c_str(), m_requestBody.length());
- request->AddContentBody(body);
- }
- // Allow descendant classes to modify the request if desired
- this->CustomizeRequest(request);
- // Sign the request
- if (m_awsAuthSigner)
- {
- m_awsAuthSigner->SignRequest(*request);
- }
- httpResponse = m_httpClient->MakeRequest(request, m_readRateLimiter.get(), m_writeRateLimiter.get());
- }
- // Allow descendant classes to process the response
- this->ProcessResponse(httpResponse);
- // Configure and deliver our response
- auto callbackResponse = AZStd::make_shared<Response>();
- callbackResponse->m_response = httpResponse;
- bool failure = true;
- if (httpResponse)
- {
- Aws::IOStream& responseBody = httpResponse->GetResponseBody();
- std::istreambuf_iterator<AZStd::string::value_type> eos;
- callbackResponse->m_responseBody = AZStd::string{ std::istreambuf_iterator<AZStd::string::value_type>(responseBody), eos };
- callbackResponse->m_responseCode = static_cast<int>(httpResponse->GetResponseCode());
- if (callbackResponse->m_responseCode >= 200 && callbackResponse->m_responseCode <= 299)
- {
- failure = false;
- if (m_successCallback)
- {
- auto callback = AZStd::make_shared<SuccessFn>(AZStd::move(m_successCallback));
- auto fn = AZStd::function<void()>([callbackResponse, callback]() { (*callback)(callbackResponse); });
- AZ::TickBus::QueueFunction(fn);
- }
- }
- }
- if (failure && m_failureCallback)
- {
- auto callback = AZStd::make_shared<SuccessFn>(AZStd::move(m_failureCallback));
- auto fn = AZStd::function<void()>([callbackResponse, callback]() { (*callback)(callbackResponse); });
- AZ::TickBus::QueueFunction(fn);
- }
- }
- std::shared_ptr<Aws::Http::HttpRequest> HttpRequestJob::InitializeRequest()
- {
- std::shared_ptr<Aws::Http::HttpRequest> result;
- auto methodInfo = FindInMap(GetMethodLookup(), m_method);
- if (!m_url.empty() && methodInfo)
- {
- result = Aws::Http::CreateHttpRequest(
- Util::ToAwsString(m_url),
- methodInfo->m_awsMethod,
- &Aws::Utils::Stream::DefaultResponseStreamFactoryMethod
- );
- }
- return result;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- // HttpRequestJob::Response methods
- ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- const AZStd::string& HttpRequestJob::Response::GetResponseBody() const
- {
- return m_responseBody;
- }
- int HttpRequestJob::Response::GetResponseCode() const
- {
- return m_responseCode;
- }
- const std::shared_ptr<Aws::Http::HttpResponse>& HttpRequestJob::Response::GetUnderlyingResponse() const
- {
- return m_response;
- }
- } // namespace AWSCore
|