Response executeHelper()

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) */
    }