in src/aws-cpp-sdk-core/source/client/AWSClient.cpp [255:409]
HttpResponseOutcome AWSClient::AttemptExhaustively(const Aws::Http::URI& uri,
const Aws::AmazonWebServiceRequest& request,
Aws::Http::HttpMethod method,
const char* signerName,
const char* signerRegionOverride,
const char* signerServiceNameOverride) const
{
if (!Aws::Utils::IsValidHost(uri.GetHost()))
{
return HttpResponseOutcome(AWSError<CoreErrors>(CoreErrors::VALIDATION, "", "Invalid DNS Label found in URI host", false/*retryable*/));
}
std::shared_ptr<HttpRequest> httpRequest(CreateHttpRequest(uri, method, request.GetResponseStreamFactory()));
HttpResponseOutcome outcome;
AWSError<CoreErrors> lastError;
Aws::Monitoring::CoreMetricsCollection coreMetrics;
auto contexts = Aws::Monitoring::OnRequestStarted(this->GetServiceClientName(), request.GetServiceRequestName(), httpRequest);
const char* signerRegion = signerRegionOverride;
Aws::String regionFromResponse;
Aws::String invocationId = Aws::Utils::UUID::PseudoRandomUUID();
RequestInfo requestInfo;
requestInfo.attempt = 1;
requestInfo.maxAttempts = 0;
httpRequest->SetHeaderValue(Http::SDK_INVOCATION_ID_HEADER, invocationId);
httpRequest->SetHeaderValue(Http::SDK_REQUEST_HEADER, requestInfo);
AppendRecursionDetectionHeader(httpRequest);
for (long retries = 0;; retries++)
{
if(!m_retryStrategy->HasSendToken())
{
return HttpResponseOutcome(AWSError<CoreErrors>(CoreErrors::SLOW_DOWN,
"",
"Unable to acquire enough send tokens to execute request.",
false/*retryable*/));
};
httpRequest->SetEventStreamRequest(request.IsEventStreamRequest());
httpRequest->SetHasEventStreamResponse(request.HasEventStreamResponse());
outcome = AttemptOneRequest(httpRequest, request, signerName, signerRegion, signerServiceNameOverride);
outcome.SetRetryCount(retries);
if (retries == 0)
{
m_retryStrategy->RequestBookkeeping(outcome);
}
else
{
m_retryStrategy->RequestBookkeeping(outcome, lastError);
}
coreMetrics.httpClientMetrics = httpRequest->GetRequestMetrics();
TracingUtils::EmitCoreHttpMetrics(httpRequest->GetRequestMetrics(),
*m_telemetryProvider->getMeter(this->GetServiceClientName(), {}),
{{TracingUtils::SMITHY_METHOD_DIMENSION, request.GetServiceRequestName()},
{TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}});
if (outcome.IsSuccess())
{
Aws::Monitoring::OnRequestSucceeded(this->GetServiceClientName(), request.GetServiceRequestName(), httpRequest, outcome, coreMetrics, contexts);
AWS_LOGSTREAM_TRACE(AWS_CLIENT_LOG_TAG, "Request successful returning.");
break;
}
lastError = outcome.GetError();
DateTime serverTime = GetServerTimeFromError(outcome.GetError());
auto clockSkew = DateTime::Diff(serverTime, DateTime::Now());
Aws::Monitoring::OnRequestFailed(this->GetServiceClientName(), request.GetServiceRequestName(), httpRequest, outcome, coreMetrics, contexts);
if (!m_httpClient->IsRequestProcessingEnabled())
{
AWS_LOGSTREAM_TRACE(AWS_CLIENT_LOG_TAG, "Request was cancelled externally.");
break;
}
// Adjust region
bool retryWithCorrectRegion = false;
HttpResponseCode httpResponseCode = outcome.GetError().GetResponseCode();
if (httpResponseCode == HttpResponseCode::MOVED_PERMANENTLY || // 301
httpResponseCode == HttpResponseCode::TEMPORARY_REDIRECT || // 307
httpResponseCode == HttpResponseCode::BAD_REQUEST || // 400
httpResponseCode == HttpResponseCode::FORBIDDEN) // 403
{
regionFromResponse = GetErrorMarshaller()->ExtractRegion(outcome.GetError());
if (m_region == Aws::Region::AWS_GLOBAL && !regionFromResponse.empty() && regionFromResponse != signerRegion)
{
signerRegion = regionFromResponse.c_str();
retryWithCorrectRegion = true;
}
}
long sleepMillis = TracingUtils::MakeCallWithTiming<long>(
[&]() -> long {
return m_retryStrategy->CalculateDelayBeforeNextRetry(outcome.GetError(), retries);
},
TracingUtils::SMITHY_CLIENT_SERVICE_BACKOFF_DELAY_METRIC,
*m_telemetryProvider->getMeter(this->GetServiceClientName(), {}),
{{TracingUtils::SMITHY_METHOD_DIMENSION, request.GetServiceRequestName()},{TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}});
//AdjustClockSkew returns true means clock skew was the problem and skew was adjusted, false otherwise.
//sleep if clock skew and region was NOT the problem. AdjustClockSkew may update error inside outcome.
bool shouldSleep = !AdjustClockSkew(outcome, signerName) && !retryWithCorrectRegion;
if (!retryWithCorrectRegion && !m_retryStrategy->ShouldRetry(outcome.GetError(), retries))
{
break;
}
if (request.IsEventStreamRequest() &&
(request.GetBody()->eof() ||
(outcome.GetError().GetResponseCode() != Http::HttpResponseCode::REQUEST_NOT_MADE &&
outcome.GetError().GetResponseCode() != Http::HttpResponseCode::NETWORK_CONNECT_TIMEOUT &&
outcome.GetError().GetResponseCode() != Http::HttpResponseCode::SERVICE_UNAVAILABLE))) {
AWS_LOGSTREAM_ERROR(AWS_CLIENT_LOG_TAG, "SDK is not able to retry EventStream request after the connection was established");
break;
}
AWS_LOGSTREAM_WARN(AWS_CLIENT_LOG_TAG, "Request failed, now waiting " << sleepMillis << " ms before attempting again.");
if(request.GetBody())
{
request.GetBody()->clear();
request.GetBody()->seekg(0);
}
if (request.GetRequestRetryHandler())
{
request.GetRequestRetryHandler()(request);
}
if (shouldSleep)
{
m_httpClient->RetryRequestSleep(std::chrono::milliseconds(sleepMillis));
}
Aws::Http::URI newUri = uri;
Aws::String newEndpoint = GetErrorMarshaller()->ExtractEndpoint(outcome.GetError());
if (!newEndpoint.empty())
{
newUri.SetAuthority(newEndpoint);
}
httpRequest = CreateHttpRequest(newUri, method, request.GetResponseStreamFactory());
httpRequest->SetHeaderValue(Http::SDK_INVOCATION_ID_HEADER, invocationId);
if (serverTime.WasParseSuccessful() && serverTime != DateTime())
{
requestInfo.ttl = DateTime::Now() + clockSkew + std::chrono::milliseconds(m_requestTimeoutMs);
}
requestInfo.attempt ++;
requestInfo.maxAttempts = m_retryStrategy->GetMaxAttempts();
httpRequest->SetHeaderValue(Http::SDK_REQUEST_HEADER, requestInfo);
Aws::Monitoring::OnRequestRetry(this->GetServiceClientName(), request.GetServiceRequestName(), httpRequest, contexts);
}
auto meter = m_telemetryProvider->getMeter(this->GetServiceClientName(), {});
auto counter = meter->CreateCounter(TracingUtils::SMITHY_CLIENT_SERVICE_ATTEMPTS_METRIC, TracingUtils::COUNT_METRIC_TYPE, "");
counter->add(requestInfo.attempt, {{TracingUtils::SMITHY_METHOD_DIMENSION, request.GetServiceRequestName()},{TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}});
Aws::Monitoring::OnFinish(this->GetServiceClientName(), request.GetServiceRequestName(), httpRequest, contexts);
return outcome;
}