/**
   * Handles a successful response from a service call by unmarshalling the results using the
   * specified response handler.
   *
   * @param <T> The type of object expected in the response.
   * @param request The original request that generated the response being handled.
   * @param responseHandler The response unmarshaller used to interpret the contents of the
   *     response.
   * @param method The HTTP method that was invoked, and contains the contents of the response.
   * @param executionContext Extra state information about the request currently being executed.
   * @return The contents of the response, unmarshalled using the specified response handler.
   * @throws IOException If any problems were encountered reading the response contents from the
   *     HTTP method object.
   */
  private <T> T handleResponse(
      Request<?> request,
      HttpResponseHandler<AmazonWebServiceResponse<T>> responseHandler,
      HttpRequestBase method,
      org.apache.http.HttpResponse apacheHttpResponse,
      ExecutionContext executionContext)
      throws IOException {

    HttpResponse httpResponse = createResponse(method, request, apacheHttpResponse);
    if (responseHandler.needsConnectionLeftOpen() && method instanceof HttpEntityEnclosingRequest) {
      HttpEntityEnclosingRequest httpEntityEnclosingRequest = (HttpEntityEnclosingRequest) method;
      httpResponse.setContent(new HttpMethodReleaseInputStream(httpEntityEnclosingRequest));
    }

    try {
      CountingInputStream countingInputStream = null;
      if (System.getProperty(PROFILING_SYSTEM_PROPERTY) != null) {
        countingInputStream = new CountingInputStream(httpResponse.getContent());
        httpResponse.setContent(countingInputStream);
      }

      long startTime = System.currentTimeMillis();
      AmazonWebServiceResponse<? extends T> awsResponse = responseHandler.handle(httpResponse);
      long endTime = System.currentTimeMillis();

      if (System.getProperty(PROFILING_SYSTEM_PROPERTY) != null) {
        if (executionContext.getTimingInfo() != null) {
          TimingInfo timingInfo = executionContext.getTimingInfo();
          TimingInfo responseProcessingTiming = new TimingInfo(startTime, endTime);
          timingInfo.addSubMeasurement(
              RESPONSE_PROCESSING_SUBMEASUREMENT, responseProcessingTiming);

          if (countingInputStream != null) {
            responseProcessingTiming.addCounter(
                BYTES_PROCESSED_COUNTER, countingInputStream.getByteCount());
          }
        }
      }

      if (awsResponse == null) throw new RuntimeException("Unable to unmarshall response metadata");

      responseMetadataCache.add(request.getOriginalRequest(), awsResponse.getResponseMetadata());

      if (requestLog.isDebugEnabled()) {
        requestLog.debug(
            "Received successful response: "
                + apacheHttpResponse.getStatusLine().getStatusCode()
                + ", AWS Request ID: "
                + awsResponse.getRequestId());
      }

      return awsResponse.getResult();
    } catch (Exception e) {
      String errorMessage = "Unable to unmarshall response (" + e.getMessage() + ")";
      log.warn(errorMessage, e);
      throw new AmazonClientException(errorMessage, e);
    }
  }
  /**
   * Handles a successful response from a service call by unmarshalling the results using the
   * specified response handler.
   *
   * @param <T> The type of object expected in the response.
   * @param request The original request that generated the response being handled.
   * @param responseHandler The response unmarshaller used to interpret the contents of the
   *     response.
   * @param method The HTTP method that was invoked, and contains the contents of the response.
   * @param executionContext Extra state information about the request currently being executed.
   * @return The contents of the response, unmarshalled using the specified response handler.
   * @throws IOException If any problems were encountered reading the response contents from the
   *     HTTP method object.
   */
  private <T> T handleResponse(
      Request<?> request,
      HttpResponseHandler<AmazonWebServiceResponse<T>> responseHandler,
      HttpRequestBase method,
      org.apache.http.HttpResponse apacheHttpResponse,
      ExecutionContext executionContext)
      throws IOException {

    HttpResponse httpResponse = createResponse(method, request, apacheHttpResponse);
    if (responseHandler.needsConnectionLeftOpen() && method instanceof HttpEntityEnclosingRequest) {
      HttpEntityEnclosingRequest httpEntityEnclosingRequest = (HttpEntityEnclosingRequest) method;
      httpResponse.setContent(new HttpMethodReleaseInputStream(httpEntityEnclosingRequest));
    }

    try {
      CountingInputStream countingInputStream = null;
      if (System.getProperty(PROFILING_SYSTEM_PROPERTY) != null) {
        countingInputStream = new CountingInputStream(httpResponse.getContent());
        httpResponse.setContent(countingInputStream);
      }

      AWSRequestMetrics awsRequestMetrics = executionContext.getAwsRequestMetrics();
      awsRequestMetrics.startEvent(Field.ResponseProcessingTime.name());
      AmazonWebServiceResponse<? extends T> awsResponse = responseHandler.handle(httpResponse);
      awsRequestMetrics.endEvent(Field.ResponseProcessingTime.name());
      if (countingInputStream != null) {
        awsRequestMetrics.setCounter(
            Field.BytesProcessed.name(), countingInputStream.getByteCount());
      }

      if (awsResponse == null) throw new RuntimeException("Unable to unmarshall response metadata");

      responseMetadataCache.add(request.getOriginalRequest(), awsResponse.getResponseMetadata());

      if (requestLog.isDebugEnabled()) {
        requestLog.debug(
            "Received successful response: "
                + apacheHttpResponse.getStatusLine().getStatusCode()
                + ", AWS Request ID: "
                + awsResponse.getRequestId());
      }
      awsRequestMetrics.addProperty(Field.AWSRequestID.name(), awsResponse.getRequestId());

      return awsResponse.getResult();
    } catch (CRC32MismatchException e) {
      throw e;
    } catch (Exception e) {
      String errorMessage = "Unable to unmarshall response (" + e.getMessage() + ")";
      throw new AmazonClientException(errorMessage, e);
    }
  }