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