/** @see com.amazonaws.http.HttpResponseHandler#handle(com.amazonaws.http.HttpResponse) */
  public AmazonWebServiceResponse<S3Object> handle(HttpResponse response) throws Exception {
    /*
     * TODO: It'd be nice to set the bucket name and key here, but the
     *       information isn't easy to pull out of the response/request
     *       currently.
     */
    S3Object object = new S3Object();
    AmazonWebServiceResponse<S3Object> awsResponse = parseResponseMetadata(response);
    if (response.getHeaders().get(Headers.REDIRECT_LOCATION) != null) {
      object.setRedirectLocation(response.getHeaders().get(Headers.REDIRECT_LOCATION));
    }
    ObjectMetadata metadata = object.getObjectMetadata();
    populateObjectMetadata(response, metadata);
    boolean hasServerSideCalculatedChecksum =
        !ServiceUtils.isMultipartUploadETag(metadata.getETag());
    boolean responseContainsEntireObject = response.getHeaders().get("Content-Range") == null;

    if (hasServerSideCalculatedChecksum && responseContainsEntireObject) {
      byte[] expectedChecksum = BinaryUtils.fromHex(metadata.getETag());
      object.setObjectContent(
          new S3ObjectInputStream(
              new ChecksumValidatingInputStream(
                  response.getContent(),
                  expectedChecksum,
                  object.getBucketName() + "/" + object.getKey()),
              response.getHttpRequest()));
    } else {
      object.setObjectContent(
          new S3ObjectInputStream(response.getContent(), response.getHttpRequest()));
    }

    awsResponse.setResult(object);
    return awsResponse;
  }
  /** @see com.amazonaws.http.HttpResponseHandler#handle(com.amazonaws.http.HttpResponse) */
  public AmazonWebServiceResponse<T> handle(HttpResponse response) throws Exception {
    log.trace("Parsing service response JSON");
    JsonParser jsonParser = jsonFactory.createJsonParser(response.getContent());
    try {
      AmazonWebServiceResponse<T> awsResponse = new AmazonWebServiceResponse<T>();
      JsonUnmarshallerContext unmarshallerContext = new JsonUnmarshallerContext(jsonParser);

      // TODO: Anything else to do for JSON metadata?
      unmarshallerContext.registerMetadataExpression(
          "ResponseMetadata/RequestId", 2, ResponseMetadata.AWS_REQUEST_ID);
      unmarshallerContext.registerMetadataExpression(
          "requestId", 2, ResponseMetadata.AWS_REQUEST_ID);
      registerAdditionalMetadataExpressions(unmarshallerContext);

      T result = responseUnmarshaller.unmarshall(unmarshallerContext);
      awsResponse.setResult(result);

      Map<String, String> metadata = unmarshallerContext.getMetadata();
      awsResponse.setResponseMetadata(new ResponseMetadata(metadata));

      log.trace("Done parsing service response");
      return awsResponse;
    } finally {
      try {
        jsonParser.close();
      } catch (Exception e) {
      }
    }
  }
  /** @see com.amazonaws.http.HttpResponseHandler#handle(com.amazonaws.http.HttpResponse) */
  public AmazonWebServiceResponse<ObjectMetadata> handle(HttpResponse response) throws Exception {
    ObjectMetadata metadata = new ObjectMetadata();
    populateObjectMetadata(response, metadata);

    AmazonWebServiceResponse<ObjectMetadata> awsResponse = parseResponseMetadata(response);
    awsResponse.setResult(metadata);
    return awsResponse;
  }
  /**
   * 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);
    }
  }
  /** @see com.amazonaws.http.HttpResponseHandler#handle(com.amazonaws.http.HttpResponse) */
  public AmazonWebServiceResponse<T> handle(HttpResponse response) throws Exception {
    log.trace("Parsing service response JSON");

    String CRC32Checksum = response.getHeaders().get("x-amz-crc32");
    CRC32ChecksumCalculatingInputStream crc32ChecksumInputStream = null;

    JsonParser jsonParser = null;

    if (!needsConnectionLeftOpen) {
      if (CRC32Checksum != null) {
        crc32ChecksumInputStream = new CRC32ChecksumCalculatingInputStream(response.getContent());
        jsonParser = jsonFactory.createParser(crc32ChecksumInputStream);
      } else {
        jsonParser = jsonFactory.createParser(response.getContent());
      }
    }

    try {
      AmazonWebServiceResponse<T> awsResponse = new AmazonWebServiceResponse<T>();
      JsonUnmarshallerContext unmarshallerContext =
          new JsonUnmarshallerContext(jsonParser, response);
      registerAdditionalMetadataExpressions(unmarshallerContext);

      T result = responseUnmarshaller.unmarshall(unmarshallerContext);

      if (CRC32Checksum != null) {
        long serverSideCRC = Long.parseLong(CRC32Checksum);
        long clientSideCRC = crc32ChecksumInputStream.getCRC32Checksum();
        if (clientSideCRC != serverSideCRC) {
          throw new CRC32MismatchException(
              "Client calculated crc32 checksum didn't match that calculated by server side");
        }
      }

      awsResponse.setResult(result);

      Map<String, String> metadata = unmarshallerContext.getMetadata();
      metadata.put(ResponseMetadata.AWS_REQUEST_ID, response.getHeaders().get("x-amzn-RequestId"));
      awsResponse.setResponseMetadata(new ResponseMetadata(metadata));

      log.trace("Done parsing service response");
      return awsResponse;
    } finally {
      if (!needsConnectionLeftOpen) {
        try {
          jsonParser.close();
        } catch (IOException e) {
          log.warn("Error closing json parser", 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);
    }
  }
  /* (non-Javadoc)
   * @see com.amazonaws.http.HttpResponseHandler#handle(com.amazonaws.http.HttpResponse)
   */
  public AmazonWebServiceResponse<String> handle(HttpResponse response) throws Exception {
    AmazonWebServiceResponse<String> awsResponse = parseResponseMetadata(response);

    int bytesRead;
    byte[] buffer = new byte[1024];
    StringBuilder builder = new StringBuilder();
    InputStream content = response.getContent();
    while ((bytesRead = content.read(buffer)) > 0) {
      builder.append(new String(buffer, 0, bytesRead));
    }
    awsResponse.setResult(builder.toString());

    return awsResponse;
  }