AWSClient.cpp 48 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072
  1. /**
  2. * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
  3. * SPDX-License-Identifier: Apache-2.0.
  4. */
  5. #include <aws/core/client/AWSClient.h>
  6. #include <aws/core/AmazonWebServiceRequest.h>
  7. #include <aws/core/auth/AWSAuthSigner.h>
  8. #include <aws/core/auth/AWSAuthSignerProvider.h>
  9. #include <aws/core/client/AWSUrlPresigner.h>
  10. #include <aws/core/client/AWSError.h>
  11. #include <aws/core/client/AWSErrorMarshaller.h>
  12. #include <aws/core/client/ClientConfiguration.h>
  13. #include <aws/core/client/CoreErrors.h>
  14. #include <aws/core/client/RetryStrategy.h>
  15. #include <aws/core/client/RequestCompression.h>
  16. #include <aws/core/http/HttpClient.h>
  17. #include <aws/core/http/HttpClientFactory.h>
  18. #include <aws/core/http/HttpResponse.h>
  19. #include <aws/core/http/standard/StandardHttpResponse.h>
  20. #include <aws/core/http/URI.h>
  21. #include <aws/core/utils/stream/ResponseStream.h>
  22. #include <aws/core/utils/json/JsonSerializer.h>
  23. #include <aws/core/utils/Outcome.h>
  24. #include <aws/core/utils/StringUtils.h>
  25. #include <aws/core/utils/xml/XmlSerializer.h>
  26. #include <aws/core/utils/memory/stl/AWSStringStream.h>
  27. #include <aws/core/utils/logging/LogMacros.h>
  28. #include <aws/core/Globals.h>
  29. #include <aws/core/utils/EnumParseOverflowContainer.h>
  30. #include <aws/core/utils/crypto/MD5.h>
  31. #include <aws/core/utils/crypto/CRC32.h>
  32. #include <aws/core/utils/crypto/Sha256.h>
  33. #include <aws/core/utils/crypto/Sha1.h>
  34. #include <aws/core/utils/HashingUtils.h>
  35. #include <aws/core/utils/crypto/Factories.h>
  36. #include <aws/core/utils/event/EventStream.h>
  37. #include <aws/core/utils/UUID.h>
  38. #include <aws/core/monitoring/MonitoringManager.h>
  39. #include <aws/core/Region.h>
  40. #include <aws/core/utils/DNS.h>
  41. #include <aws/core/Version.h>
  42. #include <aws/core/platform/Environment.h>
  43. #include <aws/core/platform/OSVersionInfo.h>
  44. #include <cstring>
  45. #include <cassert>
  46. #include <iomanip>
  47. using namespace Aws;
  48. using namespace Aws::Client;
  49. using namespace Aws::Http;
  50. using namespace Aws::Utils;
  51. using namespace Aws::Utils::Json;
  52. using namespace Aws::Utils::Xml;
  53. static const int SUCCESS_RESPONSE_MIN = 200;
  54. static const int SUCCESS_RESPONSE_MAX = 299;
  55. static const char AWS_CLIENT_LOG_TAG[] = "AWSClient";
  56. static const char AWS_LAMBDA_FUNCTION_NAME[] = "AWS_LAMBDA_FUNCTION_NAME";
  57. static const char X_AMZN_TRACE_ID[] = "_X_AMZN_TRACE_ID";
  58. //4 Minutes
  59. static const std::chrono::milliseconds TIME_DIFF_MAX = std::chrono::minutes(4);
  60. //-4 Minutes
  61. static const std::chrono::milliseconds TIME_DIFF_MIN = std::chrono::minutes(-4);
  62. CoreErrors AWSClient::GuessBodylessErrorType(Aws::Http::HttpResponseCode responseCode)
  63. {
  64. switch (responseCode)
  65. {
  66. case HttpResponseCode::FORBIDDEN:
  67. case HttpResponseCode::UNAUTHORIZED:
  68. return CoreErrors::ACCESS_DENIED;
  69. case HttpResponseCode::NOT_FOUND:
  70. return CoreErrors::RESOURCE_NOT_FOUND;
  71. default:
  72. return CoreErrors::UNKNOWN;
  73. }
  74. }
  75. bool AWSClient::DoesResponseGenerateError(const std::shared_ptr<HttpResponse>& response)
  76. {
  77. if (response->HasClientError()) return true;
  78. int responseCode = static_cast<int>(response->GetResponseCode());
  79. return responseCode < SUCCESS_RESPONSE_MIN || responseCode > SUCCESS_RESPONSE_MAX;
  80. }
  81. struct RequestInfo
  82. {
  83. Aws::Utils::DateTime ttl;
  84. long attempt;
  85. long maxAttempts;
  86. operator String()
  87. {
  88. Aws::StringStream ss;
  89. if (ttl.WasParseSuccessful() && ttl != DateTime())
  90. {
  91. assert(attempt > 1);
  92. ss << "ttl=" << ttl.ToGmtString(DateFormat::ISO_8601_BASIC) << "; ";
  93. }
  94. ss << "attempt=" << attempt;
  95. if (maxAttempts > 0)
  96. {
  97. ss << "; max=" << maxAttempts;
  98. }
  99. return ss.str();
  100. }
  101. };
  102. AWSClient::AWSClient(const Aws::Client::ClientConfiguration& configuration,
  103. const std::shared_ptr<Aws::Client::AWSAuthSigner>& signer,
  104. const std::shared_ptr<AWSErrorMarshaller>& errorMarshaller) :
  105. m_region(configuration.region),
  106. m_httpClient(CreateHttpClient(configuration)),
  107. m_signerProvider(Aws::MakeUnique<Aws::Auth::DefaultAuthSignerProvider>(AWS_CLIENT_LOG_TAG, signer)),
  108. m_errorMarshaller(errorMarshaller),
  109. m_retryStrategy(configuration.retryStrategy),
  110. m_writeRateLimiter(configuration.writeRateLimiter),
  111. m_readRateLimiter(configuration.readRateLimiter),
  112. m_userAgent(configuration.userAgent),
  113. m_customizedUserAgent(!m_userAgent.empty()),
  114. m_hash(Aws::Utils::Crypto::CreateMD5Implementation()),
  115. m_requestTimeoutMs(configuration.requestTimeoutMs),
  116. m_enableClockSkewAdjustment(configuration.enableClockSkewAdjustment),
  117. m_requestCompressionConfig(configuration.requestCompressionConfig)
  118. {
  119. AWSClient::SetServiceClientName("AWSBaseClient");
  120. }
  121. AWSClient::AWSClient(const Aws::Client::ClientConfiguration& configuration,
  122. const std::shared_ptr<Aws::Auth::AWSAuthSignerProvider>& signerProvider,
  123. const std::shared_ptr<AWSErrorMarshaller>& errorMarshaller) :
  124. m_region(configuration.region),
  125. m_httpClient(CreateHttpClient(configuration)),
  126. m_signerProvider(signerProvider),
  127. m_errorMarshaller(errorMarshaller),
  128. m_retryStrategy(configuration.retryStrategy),
  129. m_writeRateLimiter(configuration.writeRateLimiter),
  130. m_readRateLimiter(configuration.readRateLimiter),
  131. m_userAgent(configuration.userAgent),
  132. m_customizedUserAgent(!m_userAgent.empty()),
  133. m_hash(Aws::Utils::Crypto::CreateMD5Implementation()),
  134. m_requestTimeoutMs(configuration.requestTimeoutMs),
  135. m_enableClockSkewAdjustment(configuration.enableClockSkewAdjustment),
  136. m_requestCompressionConfig(configuration.requestCompressionConfig)
  137. {
  138. AWSClient::SetServiceClientName("AWSBaseClient");
  139. }
  140. void AWSClient::SetServiceClientName(const Aws::String& name)
  141. {
  142. m_serviceName = name;
  143. if (!m_customizedUserAgent)
  144. {
  145. m_userAgent = Aws::Client::ComputeUserAgentString();
  146. }
  147. }
  148. void AWSClient::DisableRequestProcessing()
  149. {
  150. m_httpClient->DisableRequestProcessing();
  151. }
  152. void AWSClient::EnableRequestProcessing()
  153. {
  154. m_httpClient->EnableRequestProcessing();
  155. }
  156. Aws::Client::AWSAuthSigner* AWSClient::GetSignerByName(const char* name) const
  157. {
  158. const auto& signer = m_signerProvider->GetSigner(name);
  159. return signer ? signer.get() : nullptr;
  160. }
  161. static DateTime GetServerTimeFromError(const AWSError<CoreErrors> error)
  162. {
  163. const Http::HeaderValueCollection& headers = error.GetResponseHeaders();
  164. auto awsDateHeaderIter = headers.find(StringUtils::ToLower(Http::AWS_DATE_HEADER));
  165. auto dateHeaderIter = headers.find(StringUtils::ToLower(Http::DATE_HEADER));
  166. if (awsDateHeaderIter != headers.end())
  167. {
  168. return DateTime(awsDateHeaderIter->second.c_str(), DateFormat::AutoDetect);
  169. }
  170. else if (dateHeaderIter != headers.end())
  171. {
  172. return DateTime(dateHeaderIter->second.c_str(), DateFormat::AutoDetect);
  173. }
  174. else
  175. {
  176. return DateTime();
  177. }
  178. }
  179. bool AWSClient::AdjustClockSkew(HttpResponseOutcome& outcome, const char* signerName) const
  180. {
  181. if (m_enableClockSkewAdjustment)
  182. {
  183. auto signer = GetSignerByName(signerName);
  184. //detect clock skew and try to correct.
  185. AWS_LOGSTREAM_WARN(AWS_CLIENT_LOG_TAG, "If the signature check failed. This could be because of a time skew. Attempting to adjust the signer.");
  186. DateTime serverTime = GetServerTimeFromError(outcome.GetError());
  187. const auto signingTimestamp = signer->GetSigningTimestamp();
  188. if (!serverTime.WasParseSuccessful() || serverTime == DateTime())
  189. {
  190. AWS_LOGSTREAM_DEBUG(AWS_CLIENT_LOG_TAG, "Date header was not found in the response, can't attempt to detect clock skew");
  191. return false;
  192. }
  193. AWS_LOGSTREAM_DEBUG(AWS_CLIENT_LOG_TAG, "Server time is " << serverTime.ToGmtString(DateFormat::RFC822) << ", while client time is " << DateTime::Now().ToGmtString(DateFormat::RFC822));
  194. auto diff = DateTime::Diff(serverTime, signingTimestamp);
  195. //only try again if clock skew was the cause of the error.
  196. if (diff >= TIME_DIFF_MAX || diff <= TIME_DIFF_MIN)
  197. {
  198. diff = DateTime::Diff(serverTime, DateTime::Now());
  199. AWS_LOGSTREAM_INFO(AWS_CLIENT_LOG_TAG, "Computed time difference as " << diff.count() << " milliseconds. Adjusting signer with the skew.");
  200. signer->SetClockSkew(diff);
  201. AWSError<CoreErrors> newError(
  202. outcome.GetError().GetErrorType(), outcome.GetError().GetExceptionName(), outcome.GetError().GetMessage(), true);
  203. newError.SetResponseHeaders(outcome.GetError().GetResponseHeaders());
  204. newError.SetResponseCode(outcome.GetError().GetResponseCode());
  205. outcome = std::move(newError);
  206. return true;
  207. }
  208. }
  209. return false;
  210. }
  211. HttpResponseOutcome AWSClient::AttemptExhaustively(const Aws::Http::URI& uri,
  212. const Aws::AmazonWebServiceRequest& request,
  213. HttpMethod method,
  214. const char* signerName,
  215. const char* signerRegionOverride,
  216. const char* signerServiceNameOverride) const
  217. {
  218. if (!Aws::Utils::IsValidHost(uri.GetAuthority()))
  219. {
  220. return HttpResponseOutcome(AWSError<CoreErrors>(CoreErrors::VALIDATION, "", "Invalid DNS Label found in URI host", false/*retryable*/));
  221. }
  222. std::shared_ptr<HttpRequest> httpRequest(CreateHttpRequest(uri, method, request.GetResponseStreamFactory()));
  223. HttpResponseOutcome outcome;
  224. AWSError<CoreErrors> lastError;
  225. Aws::Monitoring::CoreMetricsCollection coreMetrics;
  226. auto contexts = Aws::Monitoring::OnRequestStarted(this->GetServiceClientName(), request.GetServiceRequestName(), httpRequest);
  227. const char* signerRegion = signerRegionOverride;
  228. Aws::String regionFromResponse;
  229. Aws::String invocationId = Aws::Utils::UUID::RandomUUID();
  230. RequestInfo requestInfo;
  231. requestInfo.attempt = 1;
  232. requestInfo.maxAttempts = 0;
  233. httpRequest->SetHeaderValue(Http::SDK_INVOCATION_ID_HEADER, invocationId);
  234. httpRequest->SetHeaderValue(Http::SDK_REQUEST_HEADER, requestInfo);
  235. AppendRecursionDetectionHeader(httpRequest);
  236. for (long retries = 0;; retries++)
  237. {
  238. if(!m_retryStrategy->HasSendToken())
  239. {
  240. return HttpResponseOutcome(AWSError<CoreErrors>(CoreErrors::SLOW_DOWN,
  241. "",
  242. "Unable to acquire enough send tokens to execute request.",
  243. false/*retryable*/));
  244. };
  245. httpRequest->SetEventStreamRequest(request.IsEventStreamRequest());
  246. outcome = AttemptOneRequest(httpRequest, request, signerName, signerRegion, signerServiceNameOverride);
  247. if (retries == 0)
  248. {
  249. m_retryStrategy->RequestBookkeeping(outcome);
  250. }
  251. else
  252. {
  253. m_retryStrategy->RequestBookkeeping(outcome, lastError);
  254. }
  255. coreMetrics.httpClientMetrics = httpRequest->GetRequestMetrics();
  256. if (outcome.IsSuccess())
  257. {
  258. Aws::Monitoring::OnRequestSucceeded(this->GetServiceClientName(), request.GetServiceRequestName(), httpRequest, outcome, coreMetrics, contexts);
  259. AWS_LOGSTREAM_TRACE(AWS_CLIENT_LOG_TAG, "Request successful returning.");
  260. break;
  261. }
  262. lastError = outcome.GetError();
  263. DateTime serverTime = GetServerTimeFromError(outcome.GetError());
  264. auto clockSkew = DateTime::Diff(serverTime, DateTime::Now());
  265. Aws::Monitoring::OnRequestFailed(this->GetServiceClientName(), request.GetServiceRequestName(), httpRequest, outcome, coreMetrics, contexts);
  266. if (!m_httpClient->IsRequestProcessingEnabled())
  267. {
  268. AWS_LOGSTREAM_TRACE(AWS_CLIENT_LOG_TAG, "Request was cancelled externally.");
  269. break;
  270. }
  271. // Adjust region
  272. bool retryWithCorrectRegion = false;
  273. HttpResponseCode httpResponseCode = outcome.GetError().GetResponseCode();
  274. if (httpResponseCode == HttpResponseCode::MOVED_PERMANENTLY || // 301
  275. httpResponseCode == HttpResponseCode::TEMPORARY_REDIRECT || // 307
  276. httpResponseCode == HttpResponseCode::BAD_REQUEST || // 400
  277. httpResponseCode == HttpResponseCode::FORBIDDEN) // 403
  278. {
  279. regionFromResponse = GetErrorMarshaller()->ExtractRegion(outcome.GetError());
  280. if (m_region == Aws::Region::AWS_GLOBAL && !regionFromResponse.empty() && regionFromResponse != signerRegion)
  281. {
  282. signerRegion = regionFromResponse.c_str();
  283. retryWithCorrectRegion = true;
  284. }
  285. }
  286. long sleepMillis = m_retryStrategy->CalculateDelayBeforeNextRetry(outcome.GetError(), retries);
  287. //AdjustClockSkew returns true means clock skew was the problem and skew was adjusted, false otherwise.
  288. //sleep if clock skew and region was NOT the problem. AdjustClockSkew may update error inside outcome.
  289. bool shouldSleep = !AdjustClockSkew(outcome, signerName) && !retryWithCorrectRegion;
  290. if (!retryWithCorrectRegion && !m_retryStrategy->ShouldRetry(outcome.GetError(), retries))
  291. {
  292. break;
  293. }
  294. AWS_LOGSTREAM_WARN(AWS_CLIENT_LOG_TAG, "Request failed, now waiting " << sleepMillis << " ms before attempting again.");
  295. if(request.GetBody())
  296. {
  297. request.GetBody()->clear();
  298. request.GetBody()->seekg(0);
  299. }
  300. if (request.GetRequestRetryHandler())
  301. {
  302. request.GetRequestRetryHandler()(request);
  303. }
  304. if (shouldSleep)
  305. {
  306. m_httpClient->RetryRequestSleep(std::chrono::milliseconds(sleepMillis));
  307. }
  308. Aws::Http::URI newUri = uri;
  309. Aws::String newEndpoint = GetErrorMarshaller()->ExtractEndpoint(outcome.GetError());
  310. if (!newEndpoint.empty())
  311. {
  312. newUri.SetAuthority(newEndpoint);
  313. }
  314. httpRequest = CreateHttpRequest(newUri, method, request.GetResponseStreamFactory());
  315. httpRequest->SetHeaderValue(Http::SDK_INVOCATION_ID_HEADER, invocationId);
  316. if (serverTime.WasParseSuccessful() && serverTime != DateTime())
  317. {
  318. requestInfo.ttl = DateTime::Now() + clockSkew + std::chrono::milliseconds(m_requestTimeoutMs);
  319. }
  320. requestInfo.attempt ++;
  321. requestInfo.maxAttempts = m_retryStrategy->GetMaxAttempts();
  322. httpRequest->SetHeaderValue(Http::SDK_REQUEST_HEADER, requestInfo);
  323. Aws::Monitoring::OnRequestRetry(this->GetServiceClientName(), request.GetServiceRequestName(), httpRequest, contexts);
  324. }
  325. Aws::Monitoring::OnFinish(this->GetServiceClientName(), request.GetServiceRequestName(), httpRequest, contexts);
  326. return outcome;
  327. }
  328. HttpResponseOutcome AWSClient::AttemptExhaustively(const Aws::Http::URI& uri,
  329. HttpMethod method,
  330. const char* signerName,
  331. const char* requestName,
  332. const char* signerRegionOverride,
  333. const char* signerServiceNameOverride) const
  334. {
  335. if (!Aws::Utils::IsValidHost(uri.GetAuthority()))
  336. {
  337. return HttpResponseOutcome(AWSError<CoreErrors>(CoreErrors::VALIDATION, "", "Invalid DNS Label found in URI host", false/*retryable*/));
  338. }
  339. std::shared_ptr<HttpRequest> httpRequest(CreateHttpRequest(uri, method, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod));
  340. HttpResponseOutcome outcome;
  341. AWSError<CoreErrors> lastError;
  342. Aws::Monitoring::CoreMetricsCollection coreMetrics;
  343. auto contexts = Aws::Monitoring::OnRequestStarted(this->GetServiceClientName(), requestName, httpRequest);
  344. const char* signerRegion = signerRegionOverride;
  345. Aws::String regionFromResponse;
  346. Aws::String invocationId = Aws::Utils::UUID::RandomUUID();
  347. RequestInfo requestInfo;
  348. requestInfo.attempt = 1;
  349. requestInfo.maxAttempts = 0;
  350. httpRequest->SetHeaderValue(Http::SDK_INVOCATION_ID_HEADER, invocationId);
  351. httpRequest->SetHeaderValue(Http::SDK_REQUEST_HEADER, requestInfo);
  352. AppendRecursionDetectionHeader(httpRequest);
  353. for (long retries = 0;; retries++)
  354. {
  355. if(!m_retryStrategy->HasSendToken())
  356. {
  357. return HttpResponseOutcome(AWSError<CoreErrors>(CoreErrors::SLOW_DOWN,
  358. "",
  359. "Unable to acquire enough send tokens to execute request.",
  360. false/*retryable*/));
  361. };
  362. outcome = AttemptOneRequest(httpRequest, signerName, requestName, signerRegion, signerServiceNameOverride);
  363. if (retries == 0)
  364. {
  365. m_retryStrategy->RequestBookkeeping(outcome);
  366. }
  367. else
  368. {
  369. m_retryStrategy->RequestBookkeeping(outcome, lastError);
  370. }
  371. coreMetrics.httpClientMetrics = httpRequest->GetRequestMetrics();
  372. if (outcome.IsSuccess())
  373. {
  374. Aws::Monitoring::OnRequestSucceeded(this->GetServiceClientName(), requestName, httpRequest, outcome, coreMetrics, contexts);
  375. AWS_LOGSTREAM_TRACE(AWS_CLIENT_LOG_TAG, "Request successful returning.");
  376. break;
  377. }
  378. lastError = outcome.GetError();
  379. DateTime serverTime = GetServerTimeFromError(outcome.GetError());
  380. auto clockSkew = DateTime::Diff(serverTime, DateTime::Now());
  381. Aws::Monitoring::OnRequestFailed(this->GetServiceClientName(), requestName, httpRequest, outcome, coreMetrics, contexts);
  382. if (!m_httpClient->IsRequestProcessingEnabled())
  383. {
  384. AWS_LOGSTREAM_TRACE(AWS_CLIENT_LOG_TAG, "Request was cancelled externally.");
  385. break;
  386. }
  387. // Adjust region
  388. bool retryWithCorrectRegion = false;
  389. HttpResponseCode httpResponseCode = outcome.GetError().GetResponseCode();
  390. if (httpResponseCode == HttpResponseCode::MOVED_PERMANENTLY || // 301
  391. httpResponseCode == HttpResponseCode::TEMPORARY_REDIRECT || // 307
  392. httpResponseCode == HttpResponseCode::BAD_REQUEST || // 400
  393. httpResponseCode == HttpResponseCode::FORBIDDEN) // 403
  394. {
  395. regionFromResponse = GetErrorMarshaller()->ExtractRegion(outcome.GetError());
  396. if (m_region == Aws::Region::AWS_GLOBAL && !regionFromResponse.empty() && regionFromResponse != signerRegion)
  397. {
  398. signerRegion = regionFromResponse.c_str();
  399. retryWithCorrectRegion = true;
  400. }
  401. }
  402. long sleepMillis = m_retryStrategy->CalculateDelayBeforeNextRetry(outcome.GetError(), retries);
  403. //AdjustClockSkew returns true means clock skew was the problem and skew was adjusted, false otherwise.
  404. //sleep if clock skew and region was NOT the problem. AdjustClockSkew may update error inside outcome.
  405. bool shouldSleep = !AdjustClockSkew(outcome, signerName) && !retryWithCorrectRegion;
  406. if (!retryWithCorrectRegion && !m_retryStrategy->ShouldRetry(outcome.GetError(), retries))
  407. {
  408. break;
  409. }
  410. AWS_LOGSTREAM_WARN(AWS_CLIENT_LOG_TAG, "Request failed, now waiting " << sleepMillis << " ms before attempting again.");
  411. if (shouldSleep)
  412. {
  413. m_httpClient->RetryRequestSleep(std::chrono::milliseconds(sleepMillis));
  414. }
  415. Aws::Http::URI newUri = uri;
  416. Aws::String newEndpoint = GetErrorMarshaller()->ExtractEndpoint(outcome.GetError());
  417. if (!newEndpoint.empty())
  418. {
  419. newUri.SetAuthority(newEndpoint);
  420. }
  421. httpRequest = CreateHttpRequest(newUri, method, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod);
  422. httpRequest->SetHeaderValue(Http::SDK_INVOCATION_ID_HEADER, invocationId);
  423. if (serverTime.WasParseSuccessful() && serverTime != DateTime())
  424. {
  425. requestInfo.ttl = DateTime::Now() + clockSkew + std::chrono::milliseconds(m_requestTimeoutMs);
  426. }
  427. requestInfo.attempt ++;
  428. requestInfo.maxAttempts = m_retryStrategy->GetMaxAttempts();
  429. httpRequest->SetHeaderValue(Http::SDK_REQUEST_HEADER, requestInfo);
  430. Aws::Monitoring::OnRequestRetry(this->GetServiceClientName(), requestName, httpRequest, contexts);
  431. }
  432. Aws::Monitoring::OnFinish(this->GetServiceClientName(), requestName, httpRequest, contexts);
  433. return outcome;
  434. }
  435. HttpResponseOutcome AWSClient::AttemptOneRequest(const std::shared_ptr<HttpRequest>& httpRequest, const Aws::AmazonWebServiceRequest& request,
  436. const char* signerName, const char* signerRegionOverride, const char* signerServiceNameOverride) const
  437. {
  438. BuildHttpRequest(request, httpRequest);
  439. auto signer = GetSignerByName(signerName);
  440. if (!signer->SignRequest(*httpRequest, signerRegionOverride, signerServiceNameOverride, request.SignBody()))
  441. {
  442. AWS_LOGSTREAM_ERROR(AWS_CLIENT_LOG_TAG, "Request signing failed. Returning error.");
  443. return HttpResponseOutcome(AWSError<CoreErrors>(CoreErrors::CLIENT_SIGNING_FAILURE, "", "SDK failed to sign the request", false/*retryable*/));
  444. }
  445. if (request.GetRequestSignedHandler())
  446. {
  447. request.GetRequestSignedHandler()(*httpRequest);
  448. }
  449. AWS_LOGSTREAM_DEBUG(AWS_CLIENT_LOG_TAG, "Request Successfully signed");
  450. std::shared_ptr<HttpResponse> httpResponse(
  451. m_httpClient->MakeRequest(httpRequest, m_readRateLimiter.get(), m_writeRateLimiter.get()));
  452. if (request.ShouldValidateResponseChecksum())
  453. {
  454. for (const auto& hashIterator : httpRequest->GetResponseValidationHashes())
  455. {
  456. Aws::String checksumHeaderKey = Aws::String("x-amz-checksum-") + hashIterator.first;
  457. // TODO: If checksum ends with -#, then skip
  458. if (httpResponse->HasHeader(checksumHeaderKey.c_str()))
  459. {
  460. Aws::String checksumHeaderValue = httpResponse->GetHeader(checksumHeaderKey.c_str());
  461. if (HashingUtils::Base64Encode(hashIterator.second->GetHash().GetResult()) != checksumHeaderValue)
  462. {
  463. AWSError<CoreErrors> error(CoreErrors::VALIDATION, "", "Response checksums mismatch", false/*retryable*/);
  464. error.SetResponseHeaders(httpResponse->GetHeaders());
  465. error.SetResponseCode(httpResponse->GetResponseCode());
  466. error.SetRemoteHostIpAddress(httpResponse->GetOriginatingRequest().GetResolvedRemoteHost());
  467. AWS_LOGSTREAM_ERROR(AWS_CLIENT_LOG_TAG, error);
  468. return HttpResponseOutcome(error);
  469. }
  470. // Validate only a single checksum returned in an HTTP response
  471. break;
  472. }
  473. }
  474. }
  475. if (DoesResponseGenerateError(httpResponse) || request.HasEmbeddedError(httpResponse->GetResponseBody(), httpResponse->GetHeaders()))
  476. {
  477. AWS_LOGSTREAM_DEBUG(AWS_CLIENT_LOG_TAG, "Request returned error. Attempting to generate appropriate error codes from response");
  478. auto error = BuildAWSError(httpResponse);
  479. return HttpResponseOutcome(std::move(error));
  480. }
  481. AWS_LOGSTREAM_DEBUG(AWS_CLIENT_LOG_TAG, "Request returned successful response.");
  482. return HttpResponseOutcome(std::move(httpResponse));
  483. }
  484. HttpResponseOutcome AWSClient::AttemptOneRequest(const std::shared_ptr<HttpRequest>& httpRequest,
  485. const char* signerName, const char* requestName, const char* signerRegionOverride, const char* signerServiceNameOverride) const
  486. {
  487. AWS_UNREFERENCED_PARAM(requestName);
  488. auto signer = GetSignerByName(signerName);
  489. if (!signer->SignRequest(*httpRequest, signerRegionOverride, signerServiceNameOverride, true))
  490. {
  491. AWS_LOGSTREAM_ERROR(AWS_CLIENT_LOG_TAG, "Request signing failed. Returning error.");
  492. return HttpResponseOutcome(AWSError<CoreErrors>(CoreErrors::CLIENT_SIGNING_FAILURE, "", "SDK failed to sign the request", false/*retryable*/));
  493. }
  494. //user agent and headers like that shouldn't be signed for the sake of compatibility with proxies which MAY mutate that header.
  495. AddCommonHeaders(*httpRequest);
  496. AWS_LOGSTREAM_DEBUG(AWS_CLIENT_LOG_TAG, "Request Successfully signed");
  497. std::shared_ptr<HttpResponse> httpResponse(
  498. m_httpClient->MakeRequest(httpRequest, m_readRateLimiter.get(), m_writeRateLimiter.get()));
  499. if (DoesResponseGenerateError(httpResponse))
  500. {
  501. AWS_LOGSTREAM_DEBUG(AWS_CLIENT_LOG_TAG, "Request returned error. Attempting to generate appropriate error codes from response");
  502. auto error = BuildAWSError(httpResponse);
  503. return HttpResponseOutcome(std::move(error));
  504. }
  505. AWS_LOGSTREAM_DEBUG(AWS_CLIENT_LOG_TAG, "Request returned successful response.");
  506. return HttpResponseOutcome(std::move(httpResponse));
  507. }
  508. StreamOutcome AWSClient::MakeRequestWithUnparsedResponse(const Aws::Http::URI& uri,
  509. const Aws::AmazonWebServiceRequest& request,
  510. Http::HttpMethod method,
  511. const char* signerName,
  512. const char* signerRegionOverride,
  513. const char* signerServiceNameOverride) const
  514. {
  515. HttpResponseOutcome httpResponseOutcome = AttemptExhaustively(uri, request, method, signerName, signerRegionOverride, signerServiceNameOverride);
  516. if (httpResponseOutcome.IsSuccess())
  517. {
  518. return StreamOutcome(AmazonWebServiceResult<Stream::ResponseStream>(
  519. httpResponseOutcome.GetResult()->SwapResponseStreamOwnership(),
  520. httpResponseOutcome.GetResult()->GetHeaders(), httpResponseOutcome.GetResult()->GetResponseCode()));
  521. }
  522. return StreamOutcome(std::move(httpResponseOutcome));
  523. }
  524. StreamOutcome AWSClient::MakeRequestWithUnparsedResponse(const Aws::Http::URI& uri,
  525. Http::HttpMethod method,
  526. const char* signerName,
  527. const char* requestName,
  528. const char* signerRegionOverride,
  529. const char* signerServiceNameOverride) const
  530. {
  531. HttpResponseOutcome httpResponseOutcome = AttemptExhaustively(uri, method, signerName, requestName, signerRegionOverride, signerServiceNameOverride);
  532. if (httpResponseOutcome.IsSuccess())
  533. {
  534. return StreamOutcome(AmazonWebServiceResult<Stream::ResponseStream>(
  535. httpResponseOutcome.GetResult()->SwapResponseStreamOwnership(),
  536. httpResponseOutcome.GetResult()->GetHeaders(), httpResponseOutcome.GetResult()->GetResponseCode()));
  537. }
  538. return StreamOutcome(std::move(httpResponseOutcome));
  539. }
  540. StreamOutcome AWSClient::MakeRequestWithUnparsedResponse(const Aws::AmazonWebServiceRequest& request,
  541. const Aws::Endpoint::AWSEndpoint& endpoint,
  542. Http::HttpMethod method,
  543. const char* signerName,
  544. const char* signerRegionOverride,
  545. const char* signerServiceNameOverride) const
  546. {
  547. const Aws::Http::URI& uri = endpoint.GetURI();
  548. if (endpoint.GetAttributes()) {
  549. signerName = endpoint.GetAttributes()->authScheme.GetName().c_str();
  550. if (endpoint.GetAttributes()->authScheme.GetSigningRegion()) {
  551. signerRegionOverride = endpoint.GetAttributes()->authScheme.GetSigningRegion()->c_str();
  552. }
  553. if (endpoint.GetAttributes()->authScheme.GetSigningRegionSet()) {
  554. signerRegionOverride = endpoint.GetAttributes()->authScheme.GetSigningRegionSet()->c_str();
  555. }
  556. if (endpoint.GetAttributes()->authScheme.GetSigningName()) {
  557. signerServiceNameOverride = endpoint.GetAttributes()->authScheme.GetSigningName()->c_str();
  558. }
  559. }
  560. return MakeRequestWithUnparsedResponse(uri, request, method, signerName, signerRegionOverride, signerServiceNameOverride);
  561. }
  562. XmlOutcome AWSXMLClient::MakeRequestWithEventStream(const Aws::AmazonWebServiceRequest& request,
  563. const Aws::Endpoint::AWSEndpoint& endpoint,
  564. Http::HttpMethod method,
  565. const char* signerName,
  566. const char* signerRegionOverride,
  567. const char* signerServiceNameOverride) const
  568. {
  569. const Aws::Http::URI& uri = endpoint.GetURI();
  570. if (endpoint.GetAttributes()) {
  571. signerName = endpoint.GetAttributes()->authScheme.GetName().c_str();
  572. if (endpoint.GetAttributes()->authScheme.GetSigningRegion()) {
  573. signerRegionOverride = endpoint.GetAttributes()->authScheme.GetSigningRegion()->c_str();
  574. }
  575. if (endpoint.GetAttributes()->authScheme.GetSigningRegionSet()) {
  576. signerRegionOverride = endpoint.GetAttributes()->authScheme.GetSigningRegionSet()->c_str();
  577. }
  578. if (endpoint.GetAttributes()->authScheme.GetSigningName()) {
  579. signerServiceNameOverride = endpoint.GetAttributes()->authScheme.GetSigningName()->c_str();
  580. }
  581. }
  582. return MakeRequestWithEventStream(uri, request, method, signerName, signerRegionOverride, signerServiceNameOverride);
  583. }
  584. XmlOutcome AWSXMLClient::MakeRequestWithEventStream(const Aws::Http::URI& uri,
  585. const Aws::AmazonWebServiceRequest& request,
  586. Http::HttpMethod method,
  587. const char* signerName,
  588. const char* signerRegionOverride,
  589. const char* signerServiceNameOverride) const
  590. {
  591. HttpResponseOutcome httpOutcome = AttemptExhaustively(uri, request, method, signerName, signerRegionOverride, signerServiceNameOverride);
  592. if (httpOutcome.IsSuccess())
  593. {
  594. return XmlOutcome(AmazonWebServiceResult<XmlDocument>(XmlDocument(), httpOutcome.GetResult()->GetHeaders()));
  595. }
  596. return XmlOutcome(std::move(httpOutcome));
  597. }
  598. XmlOutcome AWSXMLClient::MakeRequestWithEventStream(const Aws::Http::URI& uri,
  599. Http::HttpMethod method,
  600. const char* signerName,
  601. const char* requestName,
  602. const char* signerRegionOverride,
  603. const char* signerServiceNameOverride) const
  604. {
  605. HttpResponseOutcome httpOutcome = AttemptExhaustively(uri, method, signerName, requestName, signerRegionOverride, signerServiceNameOverride);
  606. if (httpOutcome.IsSuccess())
  607. {
  608. return XmlOutcome(AmazonWebServiceResult<XmlDocument>(XmlDocument(), httpOutcome.GetResult()->GetHeaders()));
  609. }
  610. return XmlOutcome(std::move(httpOutcome));
  611. }
  612. void AWSClient::AddHeadersToRequest(const std::shared_ptr<Aws::Http::HttpRequest>& httpRequest,
  613. const Http::HeaderValueCollection& headerValues) const
  614. {
  615. for (auto const& headerValue : headerValues)
  616. {
  617. httpRequest->SetHeaderValue(headerValue.first, headerValue.second);
  618. }
  619. AddCommonHeaders(*httpRequest);
  620. }
  621. void AWSClient::AppendHeaderValueToRequest(const std::shared_ptr<HttpRequest> &httpRequest, const String header, const String value) const
  622. {
  623. if (!httpRequest->HasHeader(header.c_str()))
  624. {
  625. httpRequest->SetHeaderValue(header, value);
  626. }
  627. else
  628. {
  629. Aws::String contentEncoding = httpRequest->GetHeaderValue(header.c_str());
  630. contentEncoding.append(",").append(value);
  631. httpRequest->SetHeaderValue(header, contentEncoding);
  632. }
  633. }
  634. void AWSClient::AddChecksumToRequest(const std::shared_ptr<Aws::Http::HttpRequest>& httpRequest,
  635. const Aws::AmazonWebServiceRequest& request) const
  636. {
  637. Aws::String checksumAlgorithmName = Aws::Utils::StringUtils::ToLower(request.GetChecksumAlgorithmName().c_str());
  638. // Request checksums
  639. if (!checksumAlgorithmName.empty())
  640. {
  641. // For non-streaming payload, the resolved checksum location is always header.
  642. // For streaming payload, the resolved checksum location depends on whether it is an unsigned payload, we let AwsAuthSigner decide it.
  643. if (checksumAlgorithmName == "crc32")
  644. {
  645. if (request.IsStreaming())
  646. {
  647. httpRequest->SetRequestHash("crc32", Aws::MakeShared<Crypto::CRC32>(AWS_CLIENT_LOG_TAG));
  648. }
  649. else
  650. {
  651. httpRequest->SetHeaderValue("x-amz-checksum-crc32", HashingUtils::Base64Encode(HashingUtils::CalculateCRC32(*(GetBodyStream(request)))));
  652. }
  653. }
  654. else if (checksumAlgorithmName == "crc32c")
  655. {
  656. if (request.IsStreaming())
  657. {
  658. httpRequest->SetRequestHash("crc32c", Aws::MakeShared<Crypto::CRC32C>(AWS_CLIENT_LOG_TAG));
  659. }
  660. else
  661. {
  662. httpRequest->SetHeaderValue("x-amz-checksum-crc32c", HashingUtils::Base64Encode(HashingUtils::CalculateCRC32C(*(GetBodyStream(request)))));
  663. }
  664. }
  665. else if (checksumAlgorithmName == "sha256")
  666. {
  667. if (request.IsStreaming())
  668. {
  669. httpRequest->SetRequestHash("sha256", Aws::MakeShared<Crypto::Sha256>(AWS_CLIENT_LOG_TAG));
  670. }
  671. else
  672. {
  673. httpRequest->SetHeaderValue("x-amz-checksum-sha256", HashingUtils::Base64Encode(HashingUtils::CalculateSHA256(*(GetBodyStream(request)))));
  674. }
  675. }
  676. else if (checksumAlgorithmName == "sha1")
  677. {
  678. if (request.IsStreaming())
  679. {
  680. httpRequest->SetRequestHash("sha1", Aws::MakeShared<Crypto::Sha1>(AWS_CLIENT_LOG_TAG));
  681. }
  682. else
  683. {
  684. httpRequest->SetHeaderValue("x-amz-checksum-sha1", HashingUtils::Base64Encode(HashingUtils::CalculateSHA1(*(GetBodyStream(request)))));
  685. }
  686. }
  687. else if (checksumAlgorithmName == "md5")
  688. {
  689. httpRequest->SetHeaderValue(Http::CONTENT_MD5_HEADER, HashingUtils::Base64Encode(HashingUtils::CalculateMD5(*(GetBodyStream(request)))));
  690. }
  691. else
  692. {
  693. AWS_LOGSTREAM_WARN(AWS_CLIENT_LOG_TAG, "Checksum algorithm: " << checksumAlgorithmName << "is not supported by SDK.");
  694. }
  695. }
  696. // Response checksums
  697. if (request.ShouldValidateResponseChecksum())
  698. {
  699. for (const Aws::String& responseChecksumAlgorithmName : request.GetResponseChecksumAlgorithmNames())
  700. {
  701. checksumAlgorithmName = Aws::Utils::StringUtils::ToLower(responseChecksumAlgorithmName.c_str());
  702. if (checksumAlgorithmName == "crc32c")
  703. {
  704. std::shared_ptr<Aws::Utils::Crypto::CRC32C> crc32c = Aws::MakeShared<Aws::Utils::Crypto::CRC32C>(AWS_CLIENT_LOG_TAG);
  705. httpRequest->AddResponseValidationHash("crc32c", crc32c);
  706. }
  707. else if (checksumAlgorithmName == "crc32")
  708. {
  709. std::shared_ptr<Aws::Utils::Crypto::CRC32> crc32 = Aws::MakeShared<Aws::Utils::Crypto::CRC32>(AWS_CLIENT_LOG_TAG);
  710. httpRequest->AddResponseValidationHash("crc", crc32);
  711. }
  712. else if (checksumAlgorithmName == "sha1")
  713. {
  714. std::shared_ptr<Aws::Utils::Crypto::Sha1> sha1 = Aws::MakeShared<Aws::Utils::Crypto::Sha1>(AWS_CLIENT_LOG_TAG);
  715. httpRequest->AddResponseValidationHash("sha1", sha1);
  716. }
  717. else if (checksumAlgorithmName == "sha256")
  718. {
  719. std::shared_ptr<Aws::Utils::Crypto::Sha256> sha256 = Aws::MakeShared<Aws::Utils::Crypto::Sha256>(AWS_CLIENT_LOG_TAG);
  720. httpRequest->AddResponseValidationHash("sha256", sha256);
  721. }
  722. else
  723. {
  724. AWS_LOGSTREAM_WARN(AWS_CLIENT_LOG_TAG, "Checksum algorithm: " << checksumAlgorithmName << " is not supported in validating response body yet.");
  725. }
  726. }
  727. }
  728. }
  729. void AWSClient::AddContentBodyToRequest(const std::shared_ptr<Aws::Http::HttpRequest>& httpRequest,
  730. const std::shared_ptr<Aws::IOStream>& body, bool needsContentMd5, bool isChunked) const
  731. {
  732. httpRequest->AddContentBody(body);
  733. //If there is no body, we have a content length of 0
  734. //note: we also used to remove content-type, but S3 actually needs content-type on InitiateMultipartUpload and it isn't
  735. //forbidden by the spec. If we start getting weird errors related to this, make sure it isn't caused by this removal.
  736. if (!body)
  737. {
  738. AWS_LOGSTREAM_TRACE(AWS_CLIENT_LOG_TAG, "No content body, content-length headers");
  739. if(httpRequest->GetMethod() == HttpMethod::HTTP_POST || httpRequest->GetMethod() == HttpMethod::HTTP_PUT)
  740. {
  741. httpRequest->SetHeaderValue(Http::CONTENT_LENGTH_HEADER, "0");
  742. }
  743. else
  744. {
  745. httpRequest->DeleteHeader(Http::CONTENT_LENGTH_HEADER);
  746. }
  747. }
  748. //Add transfer-encoding:chunked to header
  749. if (body && isChunked && !httpRequest->HasHeader(Http::CONTENT_LENGTH_HEADER))
  750. {
  751. httpRequest->SetTransferEncoding(CHUNKED_VALUE);
  752. }
  753. //in the scenario where we are adding a content body as a stream, the request object likely already
  754. //has a content-length header set and we don't want to seek the stream just to find this information.
  755. else if (body && !httpRequest->HasHeader(Http::CONTENT_LENGTH_HEADER))
  756. {
  757. if (!m_httpClient->SupportsChunkedTransferEncoding())
  758. {
  759. AWS_LOGSTREAM_WARN(AWS_CLIENT_LOG_TAG, "This http client doesn't support transfer-encoding:chunked. " <<
  760. "The request may fail if it's not a seekable stream.");
  761. }
  762. AWS_LOGSTREAM_TRACE(AWS_CLIENT_LOG_TAG, "Found body, but content-length has not been set, attempting to compute content-length");
  763. body->seekg(0, body->end);
  764. auto streamSize = body->tellg();
  765. body->seekg(0, body->beg);
  766. Aws::StringStream ss;
  767. ss << streamSize;
  768. httpRequest->SetContentLength(ss.str());
  769. }
  770. if (needsContentMd5 && body && !httpRequest->HasHeader(Http::CONTENT_MD5_HEADER))
  771. {
  772. AWS_LOGSTREAM_TRACE(AWS_CLIENT_LOG_TAG, "Found body, and content-md5 needs to be set" <<
  773. ", attempting to compute content-md5");
  774. //changing the internal state of the hash computation is not a logical state
  775. //change as far as constness goes for this class. Due to the platform specificness
  776. //of hash computations, we can't control the fact that computing a hash mutates
  777. //state on some platforms such as windows (but that isn't a concern of this class.
  778. auto md5HashResult = const_cast<AWSClient*>(this)->m_hash->Calculate(*body);
  779. body->clear();
  780. if (md5HashResult.IsSuccess())
  781. {
  782. httpRequest->SetHeaderValue(Http::CONTENT_MD5_HEADER, HashingUtils::Base64Encode(md5HashResult.GetResult()));
  783. }
  784. }
  785. }
  786. Aws::String Aws::Client::GetAuthorizationHeader(const Aws::Http::HttpRequest& httpRequest)
  787. {
  788. // Extract the hex-encoded signature from the authorization header rather than recalculating it.
  789. assert(httpRequest.HasAwsAuthorization());
  790. const auto& authHeader = httpRequest.GetAwsAuthorization();
  791. auto signaturePosition = authHeader.rfind(Aws::Auth::SIGNATURE);
  792. // The auth header should end with 'Signature=<64 chars>'
  793. // Make sure we found the word 'Signature' in the header and make sure it's the last item followed by its 64 hex chars
  794. if (signaturePosition == Aws::String::npos || (signaturePosition + strlen(Aws::Auth::SIGNATURE) + 1/*'=' character*/ + 64/*hex chars*/) != authHeader.length())
  795. {
  796. AWS_LOGSTREAM_ERROR(AWS_CLIENT_LOG_TAG, "Failed to extract signature from authorization header.");
  797. return {};
  798. }
  799. return authHeader.substr(signaturePosition + strlen(Aws::Auth::SIGNATURE) + 1);
  800. }
  801. void AWSClient::BuildHttpRequest(const Aws::AmazonWebServiceRequest& request, const std::shared_ptr<HttpRequest>& httpRequest) const
  802. {
  803. //do headers first since the request likely will set content-length as its own header.
  804. AddHeadersToRequest(httpRequest, request.GetHeaders());
  805. AddHeadersToRequest(httpRequest, request.GetAdditionalCustomHeaders());
  806. if (request.IsEventStreamRequest())
  807. {
  808. httpRequest->AddContentBody(request.GetBody());
  809. }
  810. else
  811. {
  812. //Check if compression is required
  813. CompressionAlgorithm selectedCompressionAlgorithm =
  814. request.GetSelectedCompressionAlgorithm(m_requestCompressionConfig);
  815. if (Aws::Client::CompressionAlgorithm::NONE != selectedCompressionAlgorithm) {
  816. Aws::Client::RequestCompression rc;
  817. auto compressOutcome = rc.compress(request.GetBody(), selectedCompressionAlgorithm);
  818. if (compressOutcome.IsSuccess()) {
  819. Aws::String compressionAlgorithmId = Aws::Client::GetCompressionAlgorithmId(selectedCompressionAlgorithm);
  820. AppendHeaderValueToRequest(httpRequest, CONTENT_ENCODING_HEADER, compressionAlgorithmId);
  821. AddContentBodyToRequest(
  822. httpRequest, compressOutcome.GetResult(),
  823. request.ShouldComputeContentMd5(),
  824. request.IsStreaming() && request.IsChunked() &&
  825. m_httpClient->SupportsChunkedTransferEncoding());
  826. } else {
  827. AWS_LOGSTREAM_ERROR(AWS_CLIENT_LOG_TAG, "Failed to compress request, submitting uncompressed");
  828. AddContentBodyToRequest(httpRequest, request.GetBody(), request.ShouldComputeContentMd5(), request.IsStreaming() && request.IsChunked() && m_httpClient->SupportsChunkedTransferEncoding());
  829. }
  830. } else {
  831. AddContentBodyToRequest(httpRequest, request.GetBody(), request.ShouldComputeContentMd5(), request.IsStreaming() && request.IsChunked() && m_httpClient->SupportsChunkedTransferEncoding());
  832. }
  833. }
  834. AddChecksumToRequest(httpRequest, request);
  835. // Pass along handlers for processing data sent/received in bytes
  836. httpRequest->SetDataReceivedEventHandler(request.GetDataReceivedEventHandler());
  837. httpRequest->SetDataSentEventHandler(request.GetDataSentEventHandler());
  838. httpRequest->SetContinueRequestHandle(request.GetContinueRequestHandler());
  839. request.AddQueryStringParameters(httpRequest->GetUri());
  840. }
  841. void AWSClient::AddCommonHeaders(HttpRequest& httpRequest) const
  842. {
  843. httpRequest.SetUserAgent(m_userAgent);
  844. }
  845. Aws::String AWSClient::GeneratePresignedUrl(const URI& uri, HttpMethod method, long long expirationInSeconds)
  846. {
  847. return AWSUrlPresigner(*this).GeneratePresignedUrl(uri, method, expirationInSeconds);
  848. }
  849. Aws::String AWSClient::GeneratePresignedUrl(const URI& uri, HttpMethod method, const Aws::Http::HeaderValueCollection& customizedHeaders, long long expirationInSeconds)
  850. {
  851. return AWSUrlPresigner(*this).GeneratePresignedUrl(uri, method, customizedHeaders, expirationInSeconds);
  852. }
  853. Aws::String AWSClient::GeneratePresignedUrl(const URI& uri, HttpMethod method, const char* region, long long expirationInSeconds) const
  854. {
  855. return AWSUrlPresigner(*this).GeneratePresignedUrl(uri, method, region, expirationInSeconds);
  856. }
  857. Aws::String AWSClient::GeneratePresignedUrl(const URI& uri, HttpMethod method, const char* region, const Aws::Http::HeaderValueCollection& customizedHeaders, long long expirationInSeconds)
  858. {
  859. return AWSUrlPresigner(*this).GeneratePresignedUrl(uri, method, region, customizedHeaders, expirationInSeconds);
  860. }
  861. Aws::String AWSClient::GeneratePresignedUrl(const Aws::Http::URI& uri, Aws::Http::HttpMethod method, const char* region, const char* serviceName, long long expirationInSeconds) const
  862. {
  863. return AWSUrlPresigner(*this).GeneratePresignedUrl(uri, method, region, serviceName, expirationInSeconds);
  864. }
  865. Aws::String AWSClient::GeneratePresignedUrl(const Aws::Http::URI& uri, Aws::Http::HttpMethod method, const char* region, const char* serviceName, const Aws::Http::HeaderValueCollection& customizedHeaders, long long expirationInSeconds)
  866. {
  867. return AWSUrlPresigner(*this).GeneratePresignedUrl(uri, method, region, serviceName, customizedHeaders, expirationInSeconds);
  868. }
  869. Aws::String AWSClient::GeneratePresignedUrl(const Aws::Http::URI& uri, Aws::Http::HttpMethod method, const char* region, const char* serviceName, const char* signerName, long long expirationInSeconds) const
  870. {
  871. return AWSUrlPresigner(*this).GeneratePresignedUrl(uri, method, region, serviceName, signerName, expirationInSeconds);
  872. }
  873. Aws::String AWSClient::GeneratePresignedUrl(const Aws::Http::URI& uri, Aws::Http::HttpMethod method, const char* region, const char* serviceName, const char* signerName, const Aws::Http::HeaderValueCollection& customizedHeaders, long long expirationInSeconds)
  874. {
  875. return AWSUrlPresigner(*this).GeneratePresignedUrl(uri, method, region, serviceName, signerName, customizedHeaders, expirationInSeconds);
  876. }
  877. Aws::String AWSClient::GeneratePresignedUrl(const Aws::Endpoint::AWSEndpoint& endpoint,
  878. Aws::Http::HttpMethod method /* = Http::HttpMethod::HTTP_POST */,
  879. const Aws::Http::HeaderValueCollection& customizedHeaders /* = {} */,
  880. uint64_t expirationInSeconds /* = 0 */,
  881. const char* signerName /* = Aws::Auth::SIGV4_SIGNER */,
  882. const char* signerRegionOverride /* = nullptr */,
  883. const char* signerServiceNameOverride /* = nullptr */)
  884. {
  885. return AWSUrlPresigner(*this).GeneratePresignedUrl(endpoint, method, customizedHeaders, expirationInSeconds, signerName, signerRegionOverride, signerServiceNameOverride);
  886. }
  887. Aws::String AWSClient::GeneratePresignedUrl(const Aws::AmazonWebServiceRequest& request, const Aws::Http::URI& uri, Aws::Http::HttpMethod method, const char* region,
  888. const Aws::Http::QueryStringParameterCollection& extraParams, long long expirationInSeconds) const
  889. {
  890. return AWSUrlPresigner(*this).GeneratePresignedUrl(request, uri, method, region, extraParams, expirationInSeconds);
  891. }
  892. Aws::String AWSClient::GeneratePresignedUrl(const Aws::AmazonWebServiceRequest& request, const Aws::Http::URI& uri, Aws::Http::HttpMethod method, const char* region, const char* serviceName,
  893. const Aws::Http::QueryStringParameterCollection& extraParams, long long expirationInSeconds) const
  894. {
  895. return AWSUrlPresigner(*this).GeneratePresignedUrl(request, uri, method, region, serviceName, extraParams, expirationInSeconds);
  896. }
  897. Aws::String AWSClient::GeneratePresignedUrl(const Aws::AmazonWebServiceRequest& request,
  898. const Aws::Http::URI& uri,
  899. Aws::Http::HttpMethod method,
  900. const char* region,
  901. const char* serviceName,
  902. const char* signerName,
  903. const Aws::Http::QueryStringParameterCollection& extraParams,
  904. long long expirationInSeconds) const
  905. {
  906. return AWSUrlPresigner(*this).GeneratePresignedUrl(request, uri, method, region, serviceName, signerName, extraParams, expirationInSeconds);
  907. }
  908. Aws::String AWSClient::GeneratePresignedUrl(const Aws::AmazonWebServiceRequest& request, const Aws::Http::URI& uri, Aws::Http::HttpMethod method,
  909. const Aws::Http::QueryStringParameterCollection& extraParams, long long expirationInSeconds) const
  910. {
  911. return AWSUrlPresigner(*this).GeneratePresignedUrl(request, uri, method, extraParams, expirationInSeconds);
  912. }
  913. std::shared_ptr<Aws::IOStream> AWSClient::GetBodyStream(const Aws::AmazonWebServiceRequest& request) const {
  914. if (request.GetBody() != nullptr) {
  915. return request.GetBody();
  916. }
  917. // Return an empty string stream for no body
  918. return Aws::MakeShared<Aws::StringStream>(AWS_CLIENT_LOG_TAG, "");
  919. }
  920. std::shared_ptr<Aws::Http::HttpResponse> AWSClient::MakeHttpRequest(std::shared_ptr<Aws::Http::HttpRequest>& request) const
  921. {
  922. return m_httpClient->MakeRequest(request, m_readRateLimiter.get(), m_writeRateLimiter.get());
  923. }
  924. void AWSClient::AppendRecursionDetectionHeader(std::shared_ptr<Aws::Http::HttpRequest> ioRequest)
  925. {
  926. if(!ioRequest || ioRequest->HasHeader(Aws::Http::X_AMZN_TRACE_ID_HEADER)) {
  927. return;
  928. }
  929. Aws::String awsLambdaFunctionName = Aws::Environment::GetEnv(AWS_LAMBDA_FUNCTION_NAME);
  930. if(awsLambdaFunctionName.empty()) {
  931. return;
  932. }
  933. Aws::String xAmznTraceIdVal = Aws::Environment::GetEnv(X_AMZN_TRACE_ID);
  934. if(xAmznTraceIdVal.empty()) {
  935. return;
  936. }
  937. // Escape all non-printable ASCII characters by percent encoding
  938. Aws::OStringStream xAmznTraceIdValEncodedStr;
  939. for(const char ch : xAmznTraceIdVal)
  940. {
  941. if (ch >= 0x20 && ch <= 0x7e) // ascii chars [32-126] or [' ' to '~'] are not escaped
  942. {
  943. xAmznTraceIdValEncodedStr << ch;
  944. }
  945. else
  946. {
  947. // A percent-encoded octet is encoded as a character triplet
  948. xAmznTraceIdValEncodedStr << '%' // consisting of the percent character "%"
  949. << std::hex << std::setfill('0') << std::setw(2) << std::uppercase
  950. << (size_t) ch //followed by the two hexadecimal digits representing that octet's numeric value
  951. << std::dec << std::setfill(' ') << std::setw(0) << std::nouppercase;
  952. }
  953. }
  954. xAmznTraceIdVal = xAmznTraceIdValEncodedStr.str();
  955. ioRequest->SetHeaderValue(Aws::Http::X_AMZN_TRACE_ID_HEADER, xAmznTraceIdVal);
  956. }