in aws-android-sdk-core/src/main/java/com/amazonaws/http/AmazonHttpClient.java [284:491]
<T> Response<T> executeHelper(Request<?> request,
HttpResponseHandler<AmazonWebServiceResponse<T>> responseHandler,
HttpResponseHandler<AmazonServiceException> errorResponseHandler,
ExecutionContext executionContext) {
/*
* Depending on which response handler we end up choosing to handle the
* HTTP response, it might require us to leave the underlying HTTP
* connection open, depending on whether or not it reads the complete
* HTTP response stream from the HTTP connection, or if delays reading
* any of the content until after a response is returned to the caller.
*/
boolean leaveHttpConnectionOpen = false;
final AWSRequestMetrics awsRequestMetrics = executionContext.getAwsRequestMetrics();
/*
* add the service endpoint to the logs. You can infer service name from
* service endpoint
*/
awsRequestMetrics.addProperty(Field.ServiceName, request.getServiceName());
awsRequestMetrics.addProperty(Field.ServiceEndpoint, request.getEndpoint());
// Apply whatever request options we know how to handle, such as
// user-agent.
setUserAgent(request);
request.addHeader(HEADER_SDK_TRANSACTION_ID, UUID.randomUUID().toString());
int requestCount = 0;
long lastBackoffDelay = 0;
URI redirectedURI = null;
AmazonClientException retriedException = null;
// Make a copy of the original request params and headers so that we can
// permute it in this loop and start over with the original every time.
final Map<String, String> originalParameters = new LinkedHashMap<String, String>(
request.getParameters());
final Map<String, String> originalHeaders = new HashMap<String, String>(request.getHeaders());
// mark input stream if supported
final InputStream originalContent = request.getContent();
if (originalContent != null && originalContent.markSupported()) {
originalContent.mark(-1);
}
final AWSCredentials credentials = executionContext.getCredentials();
Signer signer = null;
HttpResponse httpResponse = null;
HttpRequest httpRequest = null;
while (true) {
++requestCount;
awsRequestMetrics.setCounter(Field.RequestCount, requestCount);
if (requestCount > 1) { // retry
request.setParameters(originalParameters);
request.setHeaders(originalHeaders);
request.setContent(originalContent);
}
if (redirectedURI != null && request.getEndpoint() == null
&& request.getResourcePath() == null) {
request.setEndpoint(URI.create(
redirectedURI.getScheme() + "://" + redirectedURI.getAuthority()));
request.setResourcePath(redirectedURI.getPath());
}
try {
if (requestCount > 1) { // retry
awsRequestMetrics.startEvent(Field.RetryPauseTime);
try {
lastBackoffDelay = pauseBeforeNextRetry(request.getOriginalRequest(),
retriedException,
requestCount,
config.getRetryPolicy());
} finally {
awsRequestMetrics.endEvent(Field.RetryPauseTime);
}
final InputStream content = request.getContent();
if (content != null && content.markSupported()) {
content.reset();
}
}
request.addHeader(HEADER_SDK_RETRY_INFO,
(requestCount - 1) + "/" + lastBackoffDelay);
// Sign the request if a signer was provided
if (signer == null) {
signer = executionContext.getSignerByURI(request.getEndpoint());
}
if (signer != null && credentials != null) {
awsRequestMetrics.startEvent(Field.RequestSigningTime);
try {
signer.sign(request, credentials);
} finally {
awsRequestMetrics.endEvent(Field.RequestSigningTime);
}
}
if (REQUEST_LOG.isDebugEnabled()) {
REQUEST_LOG.debug("Sending Request: " + request.toString());
}
httpRequest = requestFactory.createHttpRequest(request, config,
executionContext);
retriedException = null;
awsRequestMetrics.startEvent(Field.HttpRequestTime);
try {
httpResponse = httpClient.execute(httpRequest);
} finally {
awsRequestMetrics.endEvent(Field.HttpRequestTime);
}
if (isRequestSuccessful(httpResponse)) {
awsRequestMetrics.addProperty(Field.StatusCode, httpResponse.getStatusCode());
/*
* If we get back any 2xx status code, then we know we
* should treat the service call as successful.
*/
leaveHttpConnectionOpen = responseHandler.needsConnectionLeftOpen();
final T response = handleResponse(request, responseHandler,
httpResponse,
executionContext);
return new Response<T>(response, httpResponse);
} else if (isTemporaryRedirect(httpResponse)) {
/*
* S3 sends 307 Temporary Redirects if you try to delete an
* EU bucket from the US endpoint. If we get a 307, we'll
* point the HTTP method to the redirected location, and let
* the next retry deliver the request to the right location.
*/
final String redirectedLocation = httpResponse.getHeaders().get("Location");
log.debug("Redirecting to: " + redirectedLocation);
// set redirect uri and retry
redirectedURI = URI.create(redirectedLocation);
request.setEndpoint(null);
request.setResourcePath(null);
awsRequestMetrics.addProperty(Field.StatusCode, httpResponse.getStatusCode());
awsRequestMetrics.addProperty(Field.RedirectLocation, redirectedLocation);
awsRequestMetrics.addProperty(Field.AWSRequestID, null);
} else {
leaveHttpConnectionOpen = errorResponseHandler.needsConnectionLeftOpen();
final AmazonServiceException ase = handleErrorResponse(request, errorResponseHandler,
httpResponse);
awsRequestMetrics.addProperty(Field.AWSRequestID, ase.getRequestId());
awsRequestMetrics.addProperty(Field.AWSErrorCode, ase.getErrorCode());
awsRequestMetrics.addProperty(Field.StatusCode, ase.getStatusCode());
if (!shouldRetry(request.getOriginalRequest(),
httpRequest.getContent(),
ase,
requestCount,
config.getRetryPolicy())) {
throw ase;
}
// Cache the retryable exception
retriedException = ase;
/*
* Checking for clock skew error again because we don't want
* to set the global time offset for every service
* exception.
*/
if (RetryUtils.isClockSkewError(ase)) {
final long timeOffset = parseClockSkewOffset(httpResponse, ase);
SDKGlobalConfiguration.setGlobalTimeOffset(timeOffset);
}
resetRequestAfterError(request, ase);
}
} catch (final IOException ioe) {
if (log.isDebugEnabled()) {
log.debug("Unable to execute HTTP request: " + ioe.getMessage(), ioe);
}
awsRequestMetrics.incrementCounter(Field.Exception);
awsRequestMetrics.addProperty(Field.Exception, ioe);
awsRequestMetrics.addProperty(Field.AWSRequestID, null);
final AmazonClientException ace = new AmazonClientException(
"Unable to execute HTTP request: " + ioe.getMessage(), ioe);
if (!shouldRetry(request.getOriginalRequest(),
httpRequest.getContent(),
ace,
requestCount,
config.getRetryPolicy())) {
throw ace;
}
// Cache the retryable exception
retriedException = ace;
resetRequestAfterError(request, ioe);
} catch (final RuntimeException e) {
throw handleUnexpectedFailure(e, awsRequestMetrics);
} catch (final Error e) {
throw handleUnexpectedFailure(e, awsRequestMetrics);
} finally {
/*
* Some response handlers need to manually manage the HTTP
* connection and will take care of releasing the connection on
* their own, but if this response handler doesn't need the
* connection left open, we go ahead and release the it to free
* up resources.
*/
if (!leaveHttpConnectionOpen && httpResponse != null) {
try {
if (httpResponse.getRawContent() != null) {
httpResponse.getRawContent().close();
}
} catch (final IOException e) {
log.warn("Cannot close the response content.", e);
}
}
}
} /* end while (true) */
}