/** @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;
  }
  /* (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;
  }
  @Override
  public AmazonServiceException handle(HttpResponse httpResponse) throws XMLStreamException {

    final InputStream is = httpResponse.getContent();
    String xmlContent = null;
    /*
     * We don't always get an error response body back from S3. When we send
     * a HEAD request, we don't receive a body, so we'll have to just return
     * what we can.
     */
    if (is == null || httpResponse.getRequest().getHttpMethod() == HttpMethodName.HEAD) {
      return createExceptionFromHeaders(httpResponse, null);
    }

    String content = null;
    try {
      content = IOUtils.toString(is);
    } catch (IOException ioe) {
      if (log.isDebugEnabled()) log.debug("Failed in parsing the error response : ", ioe);
      return createExceptionFromHeaders(httpResponse, null);
    }

    /*
     * XMLInputFactory is not thread safe and hence it is synchronized.
     * Reference :
     * http://itdoc.hitachi.co.jp/manuals/3020/30203Y2210e/EY220140.HTM
     */
    XMLStreamReader reader;
    synchronized (xmlInputFactory) {
      reader =
          xmlInputFactory.createXMLStreamReader(new ByteArrayInputStream(content.getBytes(UTF8)));
    }

    try {
      /*
       * target depth is to determine if the XML Error response from the
       * server has any element inside <Error> tag have child tags.
       * Parsing such tags is not supported now. target depth is
       * incremented for every start tag and decremented after every end
       * tag is encountered.
       */
      int targetDepth = 0;
      final AmazonS3ExceptionBuilder exceptionBuilder = new AmazonS3ExceptionBuilder();
      exceptionBuilder.setErrorResponseXml(content);
      exceptionBuilder.setStatusCode(httpResponse.getStatusCode());

      boolean hasErrorTagVisited = false;
      while (reader.hasNext()) {
        int event = reader.next();

        switch (event) {
          case XMLStreamConstants.START_ELEMENT:
            targetDepth++;
            String tagName = reader.getLocalName();
            if (targetDepth == 1 && !S3ErrorTags.Error.toString().equals(tagName))
              return createExceptionFromHeaders(
                  httpResponse,
                  "Unable to parse error response. Error XML Not in proper format." + content);
            if (S3ErrorTags.Error.toString().equals(tagName)) {
              hasErrorTagVisited = true;
            }
            continue;
          case XMLStreamConstants.CHARACTERS:
            xmlContent = reader.getText();
            if (xmlContent != null) xmlContent = xmlContent.trim();
            continue;
          case XMLStreamConstants.END_ELEMENT:
            tagName = reader.getLocalName();
            targetDepth--;
            if (!(hasErrorTagVisited) || targetDepth > 1) {
              return createExceptionFromHeaders(
                  httpResponse,
                  "Unable to parse error response. Error XML Not in proper format." + content);
            }
            if (S3ErrorTags.Message.toString().equals(tagName)) {
              exceptionBuilder.setErrorMessage(xmlContent);
            } else if (S3ErrorTags.Code.toString().equals(tagName)) {
              exceptionBuilder.setErrorCode(xmlContent);
            } else if (S3ErrorTags.RequestId.toString().equals(tagName)) {
              exceptionBuilder.setRequestId(xmlContent);
            } else if (S3ErrorTags.HostId.toString().equals(tagName)) {
              exceptionBuilder.setExtendedRequestId(xmlContent);
            } else {
              exceptionBuilder.addAdditionalDetail(tagName, xmlContent);
            }
            continue;
          case XMLStreamConstants.END_DOCUMENT:
            return exceptionBuilder.build();
        }
      }
    } catch (Exception e) {
      if (log.isDebugEnabled()) log.debug("Failed in parsing the error response : " + content, e);
    }
    return createExceptionFromHeaders(httpResponse, content);
  }