S3Response invoke(
      @Nullable String bucket, @Nullable String object, @Nullable String temporaryEndpoint)
      throws S3Exception, CloudException, InternalException {
    if (wire.isDebugEnabled()) {
      wire.debug("");
      wire.debug(
          "----------------------------------------------------------------------------------");
    }
    try {
      StringBuilder url = new StringBuilder();
      boolean leaveOpen = false;
      HttpRequestBase method;
      HttpClient client;
      int status;

      if (provider.getEC2Provider().isAWS()) {
        url.append("https://");
        if (temporaryEndpoint == null) {
          boolean validDomainName = isValidDomainName(bucket);
          if (bucket != null && validDomainName) {
            url.append(bucket);
            url.append(".");
          }
          url.append("s3.amazonaws.com/");
          if (bucket != null && !validDomainName) {
            url.append(bucket);
            url.append("/");
          }
        } else {
          url.append(temporaryEndpoint);
          url.append("/");
        }
      } else if (provider.getEC2Provider().isStorage()
          && "google".equalsIgnoreCase(provider.getProviderName())) {
        url.append("https://");
        if (temporaryEndpoint == null) {
          if (bucket != null) {
            url.append(bucket);
            url.append(".");
          }
          url.append("commondatastorage.googleapis.com/");
        } else {
          url.append(temporaryEndpoint);
          url.append("/");
        }
      } else {
        int idx = 0;

        if (!provider.getContext().getEndpoint().startsWith("http")) {
          url.append("https://");
        } else {
          idx = provider.getContext().getEndpoint().indexOf("https://");
          if (idx == -1) {
            idx = "http://".length();
            url.append("http://");
          } else {
            idx = "https://".length();
            url.append("https://");
          }
        }
        String service = "";
        if (provider.getEC2Provider().isEucalyptus()) {
          service = "Walrus/";
        }

        if (temporaryEndpoint == null) {
          url.append(provider.getContext().getEndpoint().substring(idx));
          if (!provider.getContext().getEndpoint().endsWith("/")) {
            url.append("/").append(service);
          } else {
            url.append(service);
          }
        } else {
          url.append(temporaryEndpoint);
          url.append("/");
          url.append(service);
        }
        if (bucket != null) {
          url.append(bucket);
          url.append("/");
        }
      }
      if (object != null) {
        url.append(object);
      } else if (parameters != null) {
        boolean first = true;

        if (object != null && object.indexOf('?') != -1) {
          first = false;
        }
        for (Map.Entry<String, String> entry : parameters.entrySet()) {
          String key = entry.getKey();
          String val = entry.getValue();

          if (first) {
            url.append("?");
            first = false;
          } else {
            url.append("&");
          }
          if (val != null) {
            url.append(AWSCloud.encode(key, false));
            url.append("=");
            url.append(AWSCloud.encode(val, false));
          } else {
            url.append(AWSCloud.encode(key, false));
          }
        }
      }

      if (provider.getEC2Provider().isStorage()
          && provider.getProviderName().equalsIgnoreCase("Google")) {
        headers.put(AWSCloud.P_GOOG_DATE, getDate());
      } else {
        headers.put(AWSCloud.P_AWS_DATE, getDate());
      }
      method = action.getMethod(url.toString());
      if (headers != null) {
        for (Map.Entry<String, String> entry : headers.entrySet()) {
          method.addHeader(entry.getKey(), entry.getValue());
        }
      }
      if (contentType == null && body != null) {
        contentType = "application/xml";
        method.addHeader("Content-Type", contentType);
      }
      try {
        String hash = null;
        String signature;

        signature =
            provider.signS3(
                new String(provider.getContext().getAccessPublic(), "utf-8"),
                provider.getContext().getAccessPrivate(),
                method.getMethod(),
                hash,
                contentType,
                headers,
                bucket,
                object);
        method.addHeader(AWSCloud.P_CFAUTH, signature);
      } catch (UnsupportedEncodingException e) {
        logger.error(e);
        e.printStackTrace();
        throw new InternalException(e);
      }
      if (body != null) {
        try {
          ((HttpEntityEnclosingRequestBase) method)
              .setEntity(new StringEntity(body, "application/xml", "utf-8"));
        } catch (UnsupportedEncodingException e) {
          throw new InternalException(e);
        }
      } else if (uploadFile != null) {
        ((HttpEntityEnclosingRequestBase) method)
            .setEntity(new FileEntity(uploadFile, contentType));
      }
      attempts++;
      client = getClient(url.toString(), body == null && uploadFile == null);

      if (wire.isDebugEnabled()) {
        wire.debug("[" + url.toString() + "]");
        wire.debug(method.getRequestLine().toString());
        for (Header header : method.getAllHeaders()) {
          wire.debug(header.getName() + ": " + header.getValue());
        }
        wire.debug("");
        if (body != null) {
          try {
            wire.debug(EntityUtils.toString(((HttpEntityEnclosingRequestBase) method).getEntity()));
          } catch (IOException ignore) {
          }

          wire.debug("");
        } else if (uploadFile != null) {
          wire.debug("-- file upload --");
          wire.debug("");
        }
      }
      S3Response response = new S3Response();
      HttpResponse httpResponse;

      try {
        httpResponse = client.execute(method);
        if (wire.isDebugEnabled()) {
          wire.debug(httpResponse.getStatusLine().toString());
          for (Header header : httpResponse.getAllHeaders()) {
            wire.debug(header.getName() + ": " + header.getValue());
          }
          wire.debug("");
        }
        status = httpResponse.getStatusLine().getStatusCode();
      } catch (IOException e) {
        logger.error(url + ": " + e.getMessage());
        e.printStackTrace();
        throw new InternalException(e);
      }
      response.headers = httpResponse.getAllHeaders();

      HttpEntity entity = httpResponse.getEntity();
      InputStream input = null;

      if (entity != null) {
        try {
          input = entity.getContent();
        } catch (IOException e) {
          throw new CloudException(e);
        }
      }
      try {
        if (status == HttpServletResponse.SC_OK
            || status == HttpServletResponse.SC_CREATED
            || status == HttpServletResponse.SC_ACCEPTED) {
          Header clen = httpResponse.getFirstHeader("Content-Length");
          long len = -1L;

          if (clen != null) {
            len = Long.parseLong(clen.getValue());
          }
          if (len != 0L) {
            try {
              Header ct = httpResponse.getFirstHeader("Content-Type");

              if (ct != null
                  && (ct.getValue().startsWith("application/xml")
                      || ct.getValue().startsWith("text/xml"))) {
                try {
                  response.document = parseResponse(input);
                  return response;
                } finally {
                  input.close();
                }
              } else if (ct != null
                  && ct.getValue().startsWith("application/octet-stream")
                  && len < 1) {
                return null;
              } else {
                response.contentLength = len;
                if (ct != null) {
                  response.contentType = ct.getValue();
                }
                response.input = input;
                response.method = method;
                leaveOpen = true;
                return response;
              }
            } catch (IOException e) {
              logger.error(e);
              e.printStackTrace();
              throw new CloudException(e);
            }
          } else {
            return response;
          }
        } else if (status == HttpServletResponse.SC_NO_CONTENT) {
          return response;
        } else if (status == HttpServletResponse.SC_NOT_FOUND) {
          throw new S3Exception(status, null, null, "Object not found.");
        } else {
          if (status == HttpServletResponse.SC_SERVICE_UNAVAILABLE
              || status == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
            if (attempts >= 5) {
              String msg;

              if (status == HttpServletResponse.SC_SERVICE_UNAVAILABLE) {
                msg = "Cloud service is currently unavailable.";
              } else {
                msg = "The cloud service encountered a server error while processing your request.";
              }
              logger.error(msg);
              throw new CloudException(msg);
            } else {
              leaveOpen = true;
              if (input != null) {
                try {
                  input.close();
                } catch (IOException ignore) {
                }
              }
              try {
                Thread.sleep(5000L);
              } catch (InterruptedException ignore) {
              }
              return invoke(bucket, object);
            }
          }
          try {
            Document doc;

            try {
              logger.warn("Received error code: " + status);
              doc = parseResponse(input);
            } finally {
              input.close();
            }
            if (doc != null) {
              String endpoint = null, code = null, message = null, requestId = null;
              NodeList blocks = doc.getElementsByTagName("Error");

              if (blocks.getLength() > 0) {
                Node error = blocks.item(0);
                NodeList attrs;

                attrs = error.getChildNodes();
                for (int i = 0; i < attrs.getLength(); i++) {
                  Node attr = attrs.item(i);

                  if (attr.getNodeName().equals("Code") && attr.hasChildNodes()) {
                    code = attr.getFirstChild().getNodeValue().trim();
                  } else if (attr.getNodeName().equals("Message") && attr.hasChildNodes()) {
                    message = attr.getFirstChild().getNodeValue().trim();
                  } else if (attr.getNodeName().equals("RequestId") && attr.hasChildNodes()) {
                    requestId = attr.getFirstChild().getNodeValue().trim();
                  } else if (attr.getNodeName().equals("Endpoint") && attr.hasChildNodes()) {
                    endpoint = attr.getFirstChild().getNodeValue().trim();
                  }
                }
              }
              if (endpoint != null && code.equals("TemporaryRedirect")) {
                if (temporaryEndpoint != null) {
                  throw new CloudException("Too deep redirect to " + endpoint);
                } else {
                  return invoke(bucket, object, endpoint);
                }
              } else {
                if (message == null) {
                  throw new CloudException(
                      "Unable to identify error condition: "
                          + status
                          + "/"
                          + requestId
                          + "/"
                          + code);
                }
                throw new S3Exception(status, requestId, code, message);
              }
            } else {
              throw new CloudException("Unable to parse error.");
            }
          } catch (IOException e) {
            if (status == HttpServletResponse.SC_FORBIDDEN) {
              throw new S3Exception(
                  status, "", "AccessForbidden", "Access was denied without explanation.");
            }
            throw new CloudException(e);
          } catch (RuntimeException e) {
            throw new CloudException(e);
          } catch (Error e) {
            throw new CloudException(e);
          }
        }
      } finally {
        if (!leaveOpen) {
          if (input != null) {
            try {
              input.close();
            } catch (IOException ignore) {
            }
          }
        }
      }
    } finally {
      if (wire.isDebugEnabled()) {
        wire.debug(
            "----------------------------------------------------------------------------------");
        wire.debug("");
      }
    }
  }
  public void tempRedirectInvoke(
      @Nonnull String tempEndpoint,
      @Nonnull String method,
      @Nonnull String account,
      @Nonnull String resource,
      @Nonnull String body)
      throws CloudException, InternalException {
    if (logger.isTraceEnabled()) {
      logger.trace(
          "enter - " + AzureMethod.class.getName() + ".post(" + account + "," + resource + ")");
    }
    if (wire.isDebugEnabled()) {
      wire.debug(
          "POST --------------------------------------------------------> "
              + endpoint
              + account
              + resource);
      wire.debug("");
    }
    try {
      HttpClient client = getClient();
      String url = tempEndpoint + account + resource;

      HttpRequestBase httpMethod = getMethod(method, url);

      // If it is networking configuration services
      if (httpMethod instanceof HttpPut) {
        if (url.endsWith("/services/networking/media")) {
          httpMethod.addHeader("Content-Type", "text/plain");
        } else {
          httpMethod.addHeader("Content-Type", "application/xml;charset=UTF-8");
        }
      } else {
        httpMethod.addHeader("Content-Type", "application/xml;charset=UTF-8");
      }

      // dmayne version is older for anything to do with images and for disk deletion
      if (url.indexOf("/services/images") > -1
          || (httpMethod instanceof HttpDelete && url.indexOf("/services/disks") > -1)) {
        httpMethod.addHeader("x-ms-version", "2012-08-01");
      } else {
        httpMethod.addHeader("x-ms-version", "2012-03-01");
      }

      if (strategy != null && strategy.getSendAsHeader()) {
        httpMethod.addHeader(strategy.getHeaderName(), strategy.getRequestId());
      }

      if (wire.isDebugEnabled()) {
        wire.debug(httpMethod.getRequestLine().toString());
        for (Header header : httpMethod.getAllHeaders()) {
          wire.debug(header.getName() + ": " + header.getValue());
        }
        wire.debug("");
        if (body != null) {
          wire.debug(body);
          wire.debug("");
        }
      }

      if (httpMethod instanceof HttpEntityEnclosingRequestBase) {

        HttpEntityEnclosingRequestBase entityEnclosingMethod =
            (HttpEntityEnclosingRequestBase) httpMethod;

        if (body != null) {
          try {
            entityEnclosingMethod.setEntity(new StringEntity(body, "application/xml", "utf-8"));
          } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
        }
      }

      HttpResponse response;
      StatusLine status;

      try {
        response = client.execute(httpMethod);
        status = response.getStatusLine();
      } catch (IOException e) {
        logger.error(
            "post(): Failed to execute HTTP request due to a cloud I/O error: " + e.getMessage());
        if (logger.isTraceEnabled()) {
          e.printStackTrace();
        }
        throw new CloudException(e);
      }
      if (logger.isDebugEnabled()) {
        logger.debug("post(): HTTP Status " + status);
      }
      Header[] headers = response.getAllHeaders();

      if (wire.isDebugEnabled()) {
        wire.debug(status.toString());
        for (Header h : headers) {
          if (h.getValue() != null) {
            wire.debug(h.getName() + ": " + h.getValue().trim());
          } else {
            wire.debug(h.getName() + ":");
          }
        }
        wire.debug("");
      }
      if (status.getStatusCode() != HttpServletResponse.SC_OK
          && status.getStatusCode() != HttpServletResponse.SC_CREATED
          && status.getStatusCode() != HttpServletResponse.SC_ACCEPTED) {
        logger.error("post(): Expected OK for GET request, got " + status.getStatusCode());

        HttpEntity entity = response.getEntity();

        if (entity == null) {
          throw new AzureException(
              CloudErrorType.GENERAL,
              status.getStatusCode(),
              status.getReasonPhrase(),
              "An error was returned without explanation");
        }
        try {
          body = EntityUtils.toString(entity);
        } catch (IOException e) {
          throw new AzureException(
              CloudErrorType.GENERAL,
              status.getStatusCode(),
              status.getReasonPhrase(),
              e.getMessage());
        }
        if (wire.isDebugEnabled()) {
          wire.debug(body);
        }
        wire.debug("");
        AzureException.ExceptionItems items =
            AzureException.parseException(status.getStatusCode(), body);

        if (items == null) {
          throw new CloudException(
              CloudErrorType.GENERAL, status.getStatusCode(), "Unknown", "Unknown");
        }
        logger.error(
            "post(): [" + status.getStatusCode() + " : " + items.message + "] " + items.details);
        throw new AzureException(items);
      }
    } finally {
      if (logger.isTraceEnabled()) {
        logger.trace("exit - " + AzureMethod.class.getName() + ".post()");
      }
      if (wire.isDebugEnabled()) {
        wire.debug("");
        wire.debug(
            "POST --------------------------------------------------------> "
                + endpoint
                + account
                + resource);
      }
    }
  }