Repository: jclouds
Updated Branches:
  refs/heads/master c20fcb8cd -> 8bddbb496


http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignature.java
----------------------------------------------------------------------
diff --git 
a/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignature.java 
b/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignature.java
index e8009e4..75dd648 100644
--- 
a/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignature.java
+++ 
b/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignature.java
@@ -16,243 +16,11 @@
  */
 package org.jclouds.s3.filters;
 
-import static com.google.common.base.Charsets.UTF_8;
-import static com.google.common.collect.Iterables.get;
-import static com.google.common.io.BaseEncoding.base64;
-import static com.google.common.io.ByteStreams.readBytes;
-import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AUTH_TAG;
-import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
-import static org.jclouds.crypto.Macs.asByteProcessor;
-import static org.jclouds.http.utils.Queries.queryParser;
-import static org.jclouds.s3.reference.S3Constants.PROPERTY_S3_SERVICE_PATH;
-import static 
org.jclouds.s3.reference.S3Constants.PROPERTY_S3_VIRTUAL_HOST_BUCKETS;
-import static org.jclouds.util.Strings2.toInputStream;
-
-import java.util.Collection;
-import java.util.Locale;
-import java.util.Map.Entry;
-import java.util.Set;
-
-import javax.annotation.Resource;
-import javax.inject.Inject;
-import javax.inject.Named;
-import javax.inject.Provider;
-import javax.inject.Singleton;
-
-import org.jclouds.Constants;
-import org.jclouds.aws.domain.SessionCredentials;
-import org.jclouds.crypto.Crypto;
-import org.jclouds.date.TimeStamp;
-import org.jclouds.domain.Credentials;
-import org.jclouds.http.HttpException;
-import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpRequestFilter;
-import org.jclouds.http.HttpUtils;
-import org.jclouds.http.internal.SignatureWire;
-import org.jclouds.logging.Logger;
-import org.jclouds.rest.RequestSigner;
-import org.jclouds.s3.util.S3Utils;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.base.Strings;
-import com.google.common.base.Supplier;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.SortedSetMultimap;
-import com.google.common.collect.TreeMultimap;
-import com.google.common.io.ByteProcessor;
-import com.google.common.net.HttpHeaders;
 
 /**
  * Signs the S3 request.
  */
-@Singleton
-public class RequestAuthorizeSignature implements HttpRequestFilter, 
RequestSigner {
-
-   private static final Collection<String> FIRST_HEADERS_TO_SIGN = 
ImmutableList.of(HttpHeaders.DATE);
-
-   private static final Set<String> SIGNED_PARAMETERS = ImmutableSet.of("acl", 
"torrent", "logging", "location", "policy",
-            "requestPayment", "versioning", "versions", "versionId", 
"notification", "uploadId", "uploads",
-            "partNumber", "website", "response-content-type", 
"response-content-language", "response-expires",
-            "response-cache-control", "response-content-disposition", 
"response-content-encoding", "delete");
-
-   private final SignatureWire signatureWire;
-   private final Supplier<Credentials> creds;
-   private final Provider<String> timeStampProvider;
-   private final Crypto crypto;
-   private final HttpUtils utils;
-
-   @Resource
-   @Named(Constants.LOGGER_SIGNATURE)
-   Logger signatureLog = Logger.NULL;
-
-   private final String authTag;
-   private final String headerTag;
-   private final String servicePath;
-   private final boolean isVhostStyle;
-
-   @Inject
-   public RequestAuthorizeSignature(SignatureWire signatureWire, 
@Named(PROPERTY_AUTH_TAG) String authTag,
-            @Named(PROPERTY_S3_VIRTUAL_HOST_BUCKETS) boolean isVhostStyle,
-            @Named(PROPERTY_S3_SERVICE_PATH) String servicePath, 
@Named(PROPERTY_HEADER_TAG) String headerTag,
-            @org.jclouds.location.Provider Supplier<Credentials> creds,
-            @TimeStamp Provider<String> timeStampProvider, Crypto crypto, 
HttpUtils utils) {
-      this.isVhostStyle = isVhostStyle;
-      this.servicePath = servicePath;
-      this.headerTag = headerTag;
-      this.authTag = authTag;
-      this.signatureWire = signatureWire;
-      this.creds = creds;
-      this.timeStampProvider = timeStampProvider;
-      this.crypto = crypto;
-      this.utils = utils;
-   }
-
-   public HttpRequest filter(HttpRequest request) throws HttpException {
-      request = replaceDateHeader(request);
-      Credentials current = creds.get();
-      if (current instanceof SessionCredentials) {
-         request = replaceSecurityTokenHeader(request, 
SessionCredentials.class.cast(current));
-      }
-      String signature = calculateSignature(createStringToSign(request));
-      request = replaceAuthorizationHeader(request, signature);
-      utils.logRequest(signatureLog, request, "<<");
-      return request;
-   }
-
-   HttpRequest replaceSecurityTokenHeader(HttpRequest request, 
SessionCredentials current) {
-      return request.toBuilder().replaceHeader("x-amz-security-token", 
current.getSessionToken()).build();
-   }
-
-   protected HttpRequest replaceAuthorizationHeader(HttpRequest request, 
String signature) {
-      request = request.toBuilder()
-            .replaceHeader(HttpHeaders.AUTHORIZATION, authTag + " " + 
creds.get().identity + ":" + signature).build();
-      return request;
-   }
-
-   HttpRequest replaceDateHeader(HttpRequest request) {
-      request = request.toBuilder().replaceHeader(HttpHeaders.DATE, 
timeStampProvider.get()).build();
-      return request;
-   }
-
-   public String createStringToSign(HttpRequest request) {
-      utils.logRequest(signatureLog, request, ">>");
-      SortedSetMultimap<String, String> canonicalizedHeaders = 
TreeMultimap.create();
-      StringBuilder buffer = new StringBuilder();
-      // re-sign the request
-      appendMethod(request, buffer);
-      appendPayloadMetadata(request, buffer);
-      appendHttpHeaders(request, canonicalizedHeaders);
-
-      // Remove default date timestamp if "x-amz-date" is set.
-      if (canonicalizedHeaders.containsKey("x-" + headerTag + "-date")) {
-         canonicalizedHeaders.removeAll("date");
-      }
-
-      appendAmzHeaders(canonicalizedHeaders, buffer);
-      appendBucketName(request, buffer);
-      appendUriPath(request, buffer);
-      if (signatureWire.enabled())
-         signatureWire.output(buffer.toString());
-      return buffer.toString();
-   }
-
-   String calculateSignature(String toSign) throws HttpException {
-      String signature = sign(toSign);
-      if (signatureWire.enabled())
-         signatureWire.input(toInputStream(signature));
-      return signature;
-   }
-
-   public String sign(String toSign) {
-      try {
-         ByteProcessor<byte[]> hmacSHA1 = 
asByteProcessor(crypto.hmacSHA1(creds.get().credential.getBytes(UTF_8)));
-         return base64().encode(readBytes(toInputStream(toSign), hmacSHA1));
-      } catch (Exception e) {
-         throw new HttpException("error signing request", e);
-      }
-   }
-
-   void appendMethod(HttpRequest request, StringBuilder toSign) {
-      toSign.append(request.getMethod()).append("\n");
-   }
-
-   @VisibleForTesting
-   void appendAmzHeaders(SortedSetMultimap<String, String> 
canonicalizedHeaders, StringBuilder toSign) {
-      for (Entry<String, String> header : canonicalizedHeaders.entries()) {
-         String key = header.getKey();
-         if (key.startsWith("x-" + headerTag + "-")) {
-            toSign.append(String.format("%s:%s\n", key.toLowerCase(), 
header.getValue()));
-         }
-      }
-   }
-
-   void appendPayloadMetadata(HttpRequest request, StringBuilder buffer) {
-      // note that we fall back to headers, and some requests such as ?uploads 
do not have a
-      // payload, yet specify payload related parameters
-      buffer.append(
-               request.getPayload() == null ? 
Strings.nullToEmpty(request.getFirstHeaderOrNull("Content-MD5")) :
-                        HttpUtils.nullToEmpty(request.getPayload() == null ? 
null : request.getPayload().getContentMetadata()
-                                 .getContentMD5())).append("\n");
-      buffer.append(
-               Strings.nullToEmpty(request.getPayload() == null ? 
request.getFirstHeaderOrNull(HttpHeaders.CONTENT_TYPE)
-                        : 
request.getPayload().getContentMetadata().getContentType())).append("\n");
-      for (String header : FIRST_HEADERS_TO_SIGN)
-         
buffer.append(HttpUtils.nullToEmpty(request.getHeaders().get(header))).append("\n");
-   }
-
-   @VisibleForTesting
-   void appendHttpHeaders(HttpRequest request, SortedSetMultimap<String, 
String> canonicalizedHeaders) {
-      Multimap<String, String> headers = request.getHeaders();
-      for (Entry<String, String> header : headers.entries()) {
-         if (header.getKey() == null)
-            continue;
-         String key = 
header.getKey().toString().toLowerCase(Locale.getDefault());
-         // Ignore any headers that are not particularly interesting.
-         if (key.equalsIgnoreCase(HttpHeaders.CONTENT_TYPE) || 
key.equalsIgnoreCase("Content-MD5")
-                  || key.equalsIgnoreCase(HttpHeaders.DATE) || 
key.startsWith("x-" + headerTag + "-")) {
-            canonicalizedHeaders.put(key, header.getValue());
-         }
-      }
-   }
-
-   @VisibleForTesting
-   void appendBucketName(HttpRequest req, StringBuilder toSign) {
-      String bucketName = S3Utils.getBucketName(req);
-
-      // If we have a payload/bucket/container that is not all lowercase, 
vhost-style URLs are not an option and must be
-      // automatically converted to their path-based equivalent.  This should 
only be possible for AWS-S3 since it is
-      // the only S3 implementation configured to allow uppercase 
payload/bucket/container names.
-      //
-      // http://code.google.com/p/jclouds/issues/detail?id=992
-      if (isVhostStyle && bucketName != null && 
bucketName.equals(bucketName.toLowerCase()))
-         toSign.append(servicePath).append(bucketName);
-   }
-
-   @VisibleForTesting
-   void appendUriPath(HttpRequest request, StringBuilder toSign) {
-
-      toSign.append(request.getEndpoint().getRawPath());
-
-      // ...however, there are a few exceptions that must be included in the
-      // signed URI.
-      if (request.getEndpoint().getQuery() != null) {
-         Multimap<String, String> params = 
queryParser().apply(request.getEndpoint().getQuery());
-         char separator = '?';
-         for (String paramName : 
Ordering.natural().sortedCopy(params.keySet())) {
-            // Skip any parameters that aren't part of the canonical signed 
string
-            if (!SIGNED_PARAMETERS.contains(paramName))
-               continue;
-            toSign.append(separator).append(paramName);
-            String paramValue = get(params.get(paramName), 0);
-            if (paramValue != null) {
-               toSign.append("=").append(paramValue);
-            }
-            separator = '&';
-         }
-      }
-   }
 
+public interface RequestAuthorizeSignature extends HttpRequestFilter {
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV2.java
----------------------------------------------------------------------
diff --git 
a/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV2.java 
b/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV2.java
new file mode 100644
index 0000000..dab003c
--- /dev/null
+++ 
b/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV2.java
@@ -0,0 +1,264 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.s3.filters;
+
+import static com.google.common.base.Charsets.UTF_8;
+import static com.google.common.collect.Iterables.get;
+import static com.google.common.io.BaseEncoding.base64;
+import static com.google.common.io.ByteStreams.readBytes;
+import static org.jclouds.aws.reference.AWSConstants.PROPERTY_AUTH_TAG;
+import static org.jclouds.aws.reference.AWSConstants.PROPERTY_HEADER_TAG;
+import static org.jclouds.crypto.Macs.asByteProcessor;
+import static org.jclouds.http.utils.Queries.queryParser;
+import static org.jclouds.s3.reference.S3Constants.PROPERTY_S3_SERVICE_PATH;
+import static 
org.jclouds.s3.reference.S3Constants.PROPERTY_S3_VIRTUAL_HOST_BUCKETS;
+import static org.jclouds.util.Strings2.toInputStream;
+
+import java.util.Collection;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Resource;
+import javax.inject.Inject;
+import javax.inject.Named;
+import javax.inject.Provider;
+import javax.inject.Singleton;
+
+import org.jclouds.Constants;
+import org.jclouds.aws.domain.SessionCredentials;
+import org.jclouds.crypto.Crypto;
+import org.jclouds.date.TimeStamp;
+import org.jclouds.domain.Credentials;
+import org.jclouds.http.HttpException;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.HttpUtils;
+import org.jclouds.http.internal.SignatureWire;
+import org.jclouds.logging.Logger;
+import org.jclouds.rest.RequestSigner;
+import org.jclouds.s3.util.S3Utils;
+
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Strings;
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Ordering;
+import com.google.common.collect.SortedSetMultimap;
+import com.google.common.collect.TreeMultimap;
+import com.google.common.io.ByteProcessor;
+import com.google.common.net.HttpHeaders;
+
+/**
+ * AWS Sign V2
+ */
+@Singleton
+public class RequestAuthorizeSignatureV2 implements RequestAuthorizeSignature, 
RequestSigner {
+   private static final Collection<String> FIRST_HEADERS_TO_SIGN = 
ImmutableList.of(HttpHeaders.DATE);
+
+   private static final Set<String> SIGNED_PARAMETERS = ImmutableSet.of("acl", 
"torrent", "logging", "location",
+         "policy", "requestPayment", "versioning", "versions", "versionId", 
"notification", "uploadId", "uploads",
+         "partNumber", "website", "response-content-type", 
"response-content-language", "response-expires",
+         "response-cache-control", "response-content-disposition", 
"response-content-encoding", "delete");
+
+   private final SignatureWire signatureWire;
+   private final Supplier<Credentials> creds;
+   private final Provider<String> timeStampProvider;
+   private final Crypto crypto;
+   private final HttpUtils utils;
+
+   @Resource
+   @Named(Constants.LOGGER_SIGNATURE)
+   Logger signatureLog = Logger.NULL;
+
+   private final String authTag;
+   private final String headerTag;
+   private final String servicePath;
+   private final boolean isVhostStyle;
+
+   @Inject
+   public RequestAuthorizeSignatureV2(SignatureWire signatureWire, 
@Named(PROPERTY_AUTH_TAG) String authTag,
+         @Named(PROPERTY_S3_VIRTUAL_HOST_BUCKETS) boolean isVhostStyle,
+         @Named(PROPERTY_S3_SERVICE_PATH) String servicePath, 
@Named(PROPERTY_HEADER_TAG) String headerTag,
+         @org.jclouds.location.Provider Supplier<Credentials> creds,
+         @TimeStamp Provider<String> timeStampProvider, Crypto crypto, 
HttpUtils utils) {
+      this.isVhostStyle = isVhostStyle;
+      this.servicePath = servicePath;
+      this.headerTag = headerTag;
+      this.authTag = authTag;
+      this.signatureWire = signatureWire;
+      this.creds = creds;
+      this.timeStampProvider = timeStampProvider;
+      this.crypto = crypto;
+      this.utils = utils;
+   }
+
+   public HttpRequest filter(HttpRequest request) throws HttpException {
+      request = replaceDateHeader(request);
+      Credentials current = creds.get();
+      if (current instanceof SessionCredentials) {
+         request = replaceSecurityTokenHeader(request, 
SessionCredentials.class.cast(current));
+      }
+      String signature = calculateSignature(createStringToSign(request));
+      request = replaceAuthorizationHeader(request, signature);
+      utils.logRequest(signatureLog, request, "<<");
+      return request;
+   }
+
+   HttpRequest replaceSecurityTokenHeader(HttpRequest request, 
SessionCredentials current) {
+      return request.toBuilder().replaceHeader("x-amz-security-token", 
current.getSessionToken()).build();
+   }
+
+   protected HttpRequest replaceAuthorizationHeader(HttpRequest request, 
String signature) {
+      request = request.toBuilder()
+            .replaceHeader(HttpHeaders.AUTHORIZATION,
+                  authTag + " " + creds.get().identity + ":" + 
signature).build();
+      return request;
+   }
+
+   HttpRequest replaceDateHeader(HttpRequest request) {
+      request = request.toBuilder().replaceHeader(HttpHeaders.DATE, 
timeStampProvider.get()).build();
+      return request;
+   }
+
+   public String createStringToSign(HttpRequest request) {
+      utils.logRequest(signatureLog, request, ">>");
+      SortedSetMultimap<String, String> canonicalizedHeaders = 
TreeMultimap.create();
+      StringBuilder buffer = new StringBuilder();
+      // re-sign the request
+      appendMethod(request, buffer);
+      appendPayloadMetadata(request, buffer);
+      appendHttpHeaders(request, canonicalizedHeaders);
+
+      // Remove default date timestamp if "x-amz-date" is set.
+      if (canonicalizedHeaders.containsKey("x-" + headerTag + "-date")) {
+         canonicalizedHeaders.removeAll("date");
+      }
+
+      appendAmzHeaders(canonicalizedHeaders, buffer);
+      appendBucketName(request, buffer);
+      appendUriPath(request, buffer);
+      if (signatureWire.enabled()) {
+         signatureWire.output(buffer.toString());
+      }
+      return buffer.toString();
+   }
+
+   String calculateSignature(String toSign) throws HttpException {
+      String signature = sign(toSign);
+      if (signatureWire.enabled()) {
+         signatureWire.input(toInputStream(signature));
+      }
+      return signature;
+   }
+
+   public String sign(String toSign) {
+      try {
+         ByteProcessor<byte[]> hmacSHA1 = asByteProcessor(
+               crypto.hmacSHA1(creds.get().credential.getBytes(UTF_8)));
+         return base64().encode(readBytes(toInputStream(toSign), hmacSHA1));
+      } catch (Exception e) {
+         throw new HttpException("error signing request", e);
+      }
+   }
+
+   void appendMethod(HttpRequest request, StringBuilder toSign) {
+      toSign.append(request.getMethod()).append("\n");
+   }
+
+   @VisibleForTesting
+   void appendAmzHeaders(SortedSetMultimap<String, String> 
canonicalizedHeaders, StringBuilder toSign) {
+      for (Map.Entry<String, String> header : canonicalizedHeaders.entries()) {
+         String key = header.getKey();
+         if (key.startsWith("x-" + headerTag + "-")) {
+            toSign.append(String.format("%s:%s\n", key.toLowerCase(), 
header.getValue()));
+         }
+      }
+   }
+
+   void appendPayloadMetadata(HttpRequest request, StringBuilder buffer) {
+      // note that we fall back to headers, and some requests such as ?uploads 
do not have a
+      // payload, yet specify payload related parameters
+      buffer.append(
+            request.getPayload() == null ? 
Strings.nullToEmpty(request.getFirstHeaderOrNull("Content-MD5")) :
+                  HttpUtils.nullToEmpty(
+                        request.getPayload() == null ? null : 
request.getPayload().getContentMetadata()
+                              .getContentMD5())).append("\n");
+      buffer.append(
+            Strings.nullToEmpty(
+                  request.getPayload() == null ? 
request.getFirstHeaderOrNull(HttpHeaders.CONTENT_TYPE)
+                        : 
request.getPayload().getContentMetadata().getContentType())).append("\n");
+      for (String header : FIRST_HEADERS_TO_SIGN)
+         
buffer.append(HttpUtils.nullToEmpty(request.getHeaders().get(header))).append("\n");
+   }
+
+   @VisibleForTesting
+   void appendHttpHeaders(HttpRequest request, SortedSetMultimap<String, 
String> canonicalizedHeaders) {
+      Multimap<String, String> headers = request.getHeaders();
+      for (Map.Entry<String, String> header : headers.entries()) {
+         if (header.getKey() == null) {
+            continue;
+         }
+         String key = 
header.getKey().toString().toLowerCase(Locale.getDefault());
+         // Ignore any headers that are not particularly interesting.
+         if (key.equalsIgnoreCase(HttpHeaders.CONTENT_TYPE) || 
key.equalsIgnoreCase("Content-MD5")
+               || key.equalsIgnoreCase(HttpHeaders.DATE) || 
key.startsWith("x-" + headerTag + "-")) {
+            canonicalizedHeaders.put(key, header.getValue());
+         }
+      }
+   }
+
+   @VisibleForTesting
+   void appendBucketName(HttpRequest req, StringBuilder toSign) {
+      String bucketName = S3Utils.getBucketName(req);
+
+      // If we have a payload/bucket/container that is not all lowercase, 
vhost-style URLs are not an option and must be
+      // automatically converted to their path-based equivalent.  This should 
only be possible for AWS-S3 since it is
+      // the only S3 implementation configured to allow uppercase 
payload/bucket/container names.
+      //
+      // http://code.google.com/p/jclouds/issues/detail?id=992
+      if (isVhostStyle && bucketName != null && 
bucketName.equals(bucketName.toLowerCase())) {
+         toSign.append(servicePath).append(bucketName);
+      }
+   }
+
+   @VisibleForTesting
+   void appendUriPath(HttpRequest request, StringBuilder toSign) {
+
+      toSign.append(request.getEndpoint().getRawPath());
+
+      // ...however, there are a few exceptions that must be included in the
+      // signed URI.
+      if (request.getEndpoint().getQuery() != null) {
+         Multimap<String, String> params = 
queryParser().apply(request.getEndpoint().getQuery());
+         char separator = '?';
+         for (String paramName : 
Ordering.natural().sortedCopy(params.keySet())) {
+            // Skip any parameters that aren't part of the canonical signed 
string
+            if (!SIGNED_PARAMETERS.contains(paramName)) {
+               continue;
+            }
+            toSign.append(separator).append(paramName);
+            String paramValue = get(params.get(paramName), 0);
+            if (paramValue != null) {
+               toSign.append("=").append(paramValue);
+            }
+            separator = '&';
+         }
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV4.java
----------------------------------------------------------------------
diff --git 
a/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV4.java 
b/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV4.java
new file mode 100644
index 0000000..4e4edbd
--- /dev/null
+++ 
b/apis/s3/src/main/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV4.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.s3.filters;
+
+import com.google.common.reflect.TypeToken;
+import com.google.inject.Singleton;
+import org.jclouds.http.HttpException;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.io.Payload;
+import org.jclouds.rest.internal.GeneratedHttpRequest;
+import org.jclouds.s3.S3Client;
+
+import javax.inject.Inject;
+
+@Singleton
+public class RequestAuthorizeSignatureV4 implements RequestAuthorizeSignature {
+
+   private static final String PUT_OBJECT_METHOD = "putObject";
+   private static final TypeToken<S3Client> S3_CLIENT_TYPE = new 
TypeToken<S3Client>() {
+   };
+
+   private final Aws4SignerForAuthorizationHeader signerForAuthorizationHeader;
+   private final Aws4SignerForChunkedUpload signerForChunkedUpload;
+   private final Aws4SignerForQueryString signerForQueryString;
+
+   @Inject
+   public RequestAuthorizeSignatureV4(Aws4SignerForAuthorizationHeader 
signerForAuthorizationHeader,
+         Aws4SignerForChunkedUpload signerForChunkedUpload,
+         Aws4SignerForQueryString signerForQueryString) {
+      this.signerForAuthorizationHeader = signerForAuthorizationHeader;
+      this.signerForChunkedUpload = signerForChunkedUpload;
+      this.signerForQueryString = signerForQueryString;
+   }
+
+   @Override
+   public HttpRequest filter(HttpRequest request) throws HttpException {
+      // request use chunked upload
+      if (useChunkedUpload(request)) {
+         return signForChunkedUpload(request);
+      }
+      return signForAuthorizationHeader(request);
+   }
+
+   /**
+    * returns true, if use AWS S3 chunked upload.
+    */
+   protected boolean useChunkedUpload(HttpRequest request) {
+      // only S3Client putObject method, payload not null, content-length > 0 
and cannot repeatable
+      if (!GeneratedHttpRequest.class.isAssignableFrom(request.getClass())) {
+         return false;
+      }
+      GeneratedHttpRequest req = GeneratedHttpRequest.class.cast(request);
+
+      // s3 client type and method name is putObject
+      if 
(S3_CLIENT_TYPE.equals(req.getInvocation().getInvokable().getOwnerType()) &&
+            
!PUT_OBJECT_METHOD.equals(req.getInvocation().getInvokable().getName())) {
+         return false;
+      }
+
+      Payload payload = req.getPayload();
+
+      // check payload null or payload.contentMetadata null
+      if (payload == null || payload.getContentMetadata() == null) {
+         return false;
+      }
+
+      Long contentLength = payload.getContentMetadata().getContentLength();
+
+      if (contentLength == null) {
+         return false;
+      }
+
+      return contentLength > 0l && !payload.isRepeatable();
+   }
+
+   protected HttpRequest signForAuthorizationHeader(HttpRequest request) {
+      return signerForAuthorizationHeader.sign(request);
+   }
+
+   protected HttpRequest signForChunkedUpload(HttpRequest request) {
+      return signerForChunkedUpload.sign(request);
+   }
+
+   // Authenticating Requests by Using Query Parameters (AWS Signature Version 
4)
+
+   /**
+    * Using query parameters to authenticate requests is useful when you want 
to express a request entirely in a URL.
+    * This method is also referred as presigning a URL. Presigned URLs enable 
you to grant temporary access to your
+    * Amazon S3 resources. The end user can then enter the presigned URL in 
his or her browser to access the specific
+    * Amazon S3 resource. You can also use presigned URLs to embed clickable 
links in HTML.
+    * <p/>
+    * For example, you might store videos in an Amazon S3 bucket and make them 
available on your website by using presigned URLs.
+    * Identifies the version of AWS Signature and the algorithm that you used 
to calculate the signature.
+    */
+   public HttpRequest signForTemporaryAccess(HttpRequest request, long 
timeInSeconds) {
+      return signerForQueryString.sign(request, timeInSeconds);
+   }
+
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/apis/s3/src/main/java/org/jclouds/s3/reference/S3Constants.java
----------------------------------------------------------------------
diff --git a/apis/s3/src/main/java/org/jclouds/s3/reference/S3Constants.java 
b/apis/s3/src/main/java/org/jclouds/s3/reference/S3Constants.java
index a376ae3..7569573 100644
--- a/apis/s3/src/main/java/org/jclouds/s3/reference/S3Constants.java
+++ b/apis/s3/src/main/java/org/jclouds/s3/reference/S3Constants.java
@@ -31,6 +31,7 @@ public final class S3Constants {
    public static final String DELIMITER = "delimiter";
    public static final String PROPERTY_S3_SERVICE_PATH = 
"jclouds.s3.service-path";
    public static final String PROPERTY_S3_VIRTUAL_HOST_BUCKETS = 
"jclouds.s3.virtual-host-buckets";
+   public static final String PROPERTY_JCLOUDS_S3_CHUNKED_SIZE = 
"jclouds.s3.chunked.size";
 
    private S3Constants() {
       throw new AssertionError("intentionally unimplemented");

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/apis/s3/src/test/java/org/jclouds/s3/filters/AwsHostNameUtilsTest.java
----------------------------------------------------------------------
diff --git 
a/apis/s3/src/test/java/org/jclouds/s3/filters/AwsHostNameUtilsTest.java 
b/apis/s3/src/test/java/org/jclouds/s3/filters/AwsHostNameUtilsTest.java
new file mode 100644
index 0000000..6e160cd
--- /dev/null
+++ b/apis/s3/src/test/java/org/jclouds/s3/filters/AwsHostNameUtilsTest.java
@@ -0,0 +1,61 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.s3.filters;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import java.net.URI;
+
+/**
+ * Tests parser region and service
+ */
+public class AwsHostNameUtilsTest {
+   @Test
+   public void testParseRegion() {
+      Assert.assertEquals(
+         
AwsHostNameUtils.parseRegionName("test.s3.cn-north-1.amazonaws.com.cn", "s3"),
+         "cn-north-1"
+      );
+
+
+   }
+
+   @Test
+   // default region
+   public void testParseDefaultRegion() {
+      Assert.assertEquals(
+         AwsHostNameUtils.parseRegionName("s3.amazonaws.com", "s3"),
+         "us-east-1"
+      );
+   }
+
+   @Test
+   // test s3 service
+   public void testParseService() {
+      Assert.assertEquals(
+         
AwsHostNameUtils.parseServiceName(URI.create("https://s3.amazonaws.com";)),
+         "s3"
+      );
+
+
+      Assert.assertEquals(
+         
AwsHostNameUtils.parseServiceName(URI.create("https://test-bucket.s3.cn-north-1.amazonaws.com.cn";)),
+         "s3"
+      );
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureTest.java
----------------------------------------------------------------------
diff --git 
a/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureTest.java
 
b/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureTest.java
deleted file mode 100644
index 794701d..0000000
--- 
a/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureTest.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.jclouds.s3.filters;
-
-import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
-import static org.jclouds.reflect.Reflection2.method;
-import static org.testng.Assert.assertEquals;
-
-import java.net.URI;
-import java.util.Properties;
-
-import org.jclouds.blobstore.binders.BindBlobToMultipartFormTest;
-import org.jclouds.http.HttpRequest;
-import org.jclouds.rest.internal.GeneratedHttpRequest;
-import org.jclouds.s3.S3Client;
-import org.jclouds.s3.domain.AccessControlList;
-import org.jclouds.s3.domain.CannedAccessPolicy;
-import org.jclouds.s3.domain.S3Object;
-import org.jclouds.s3.internal.BaseS3ClientTest;
-import org.jclouds.s3.options.PutObjectOptions;
-import org.jclouds.s3.reference.S3Headers;
-import org.testng.annotations.DataProvider;
-import org.testng.annotations.Test;
-
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.SortedSetMultimap;
-import com.google.common.collect.TreeMultimap;
-import com.google.common.net.HttpHeaders;
-/**
- * Tests behavior of {@code RequestAuthorizeSignature}
- */
-// NOTE:without testName, this will not call @Before* and fail w/NPE during 
surefire
-@Test(groups = "unit", testName = "RequestAuthorizeSignatureTest")
-public class RequestAuthorizeSignatureTest extends BaseS3ClientTest<S3Client> {
-   String bucketName = "bucket";
-
-   @DataProvider(parallel = true)
-   public Object[][] dataProvider() throws NoSuchMethodException {
-      return new Object[][] { { listOwnedBuckets() }, { putObject() }, { 
putBucketAcl() }
-
-      };
-   }
-
-   /**
-    * NOTE this test is dependent on how frequently the timestamp updates. At 
the time of writing,
-    * this was once per second. If this timestamp update interval is 
increased, it could make this
-    * test appear to hang for a long time.
-    */
-   @Test(threadPoolSize = 3, dataProvider = "dataProvider", timeOut = 10000)
-   void testIdempotent(HttpRequest request) {
-      request = filter.filter(request);
-      String signature = 
request.getFirstHeaderOrNull(HttpHeaders.AUTHORIZATION);
-      String date = request.getFirstHeaderOrNull(HttpHeaders.DATE);
-      int iterations = 1;
-      while (request.getFirstHeaderOrNull(HttpHeaders.DATE).equals(date)) {
-         date = request.getFirstHeaderOrNull(HttpHeaders.DATE);
-         request = filter.filter(request);
-         if (request.getFirstHeaderOrNull(HttpHeaders.DATE).equals(date))
-            assert 
signature.equals(request.getFirstHeaderOrNull(HttpHeaders.AUTHORIZATION)) : 
String.format(
-                     "sig: %s != %s on attempt %s", signature, 
request.getFirstHeaderOrNull(HttpHeaders.AUTHORIZATION),
-                     iterations);
-         else
-            iterations++;
-
-      }
-      System.out.printf("%s: %d iterations before the timestamp updated %n", 
Thread.currentThread().getName(),
-               iterations);
-   }
-
-   @Test
-   void testAppendBucketNameHostHeader() throws SecurityException, 
NoSuchMethodException {
-      GeneratedHttpRequest request = processor.createRequest(
-            method(S3Client.class, "getBucketLocation", String.class),
-            ImmutableList.<Object> of("bucket"));
-      StringBuilder builder = new StringBuilder();
-      filter.appendBucketName(request, builder);
-      assertEquals(builder.toString(), "");
-   }
-
-   @Test
-   void testAclQueryString() throws SecurityException, NoSuchMethodException {
-      HttpRequest request = putBucketAcl();
-      StringBuilder builder = new StringBuilder();
-      filter.appendUriPath(request, builder);
-      assertEquals(builder.toString(), "/" + bucketName + "?acl");
-   }
-
-   private GeneratedHttpRequest putBucketAcl() throws NoSuchMethodException {
-      return processor.createRequest(
-            method(S3Client.class, "putBucketACL", String.class, 
AccessControlList.class),
-            ImmutableList.<Object> of("bucket",
-                  
AccessControlList.fromCannedAccessPolicy(CannedAccessPolicy.PRIVATE, "1234")));
-   }
-
-   // "?acl", "?location", "?logging", "?uploads", or "?torrent"
-
-   @Test
-   void testAppendBucketNameHostHeaderService() throws SecurityException, 
NoSuchMethodException {
-      HttpRequest request = listOwnedBuckets();
-      StringBuilder builder = new StringBuilder();
-      filter.appendBucketName(request, builder);
-      assertEquals(builder.toString(), "");
-   }
-
-   private GeneratedHttpRequest listOwnedBuckets() throws 
NoSuchMethodException {
-      return processor.createRequest(method(S3Client.class, 
"listOwnedBuckets"),
-            ImmutableList.of());
-   }
-
-   @Test
-   void testHeadersGoLowercase() throws SecurityException, 
NoSuchMethodException {
-      HttpRequest request = putObject();
-      SortedSetMultimap<String, String> canonicalizedHeaders = 
TreeMultimap.create();
-      filter.appendHttpHeaders(request, canonicalizedHeaders);
-      StringBuilder builder = new StringBuilder();
-      filter.appendAmzHeaders(canonicalizedHeaders, builder);
-      assertEquals(builder.toString(), S3Headers.USER_METADATA_PREFIX + 
"adrian:foo\n");
-   }
-
-   private HttpRequest putObject() throws NoSuchMethodException {
-      S3Object object = 
blobToS3Object.apply(BindBlobToMultipartFormTest.TEST_BLOB);
-      object.getMetadata().getUserMetadata().put("Adrian", "foo");
-      return processor.createRequest(method(S3Client.class, "putObject", 
String.class,
-            S3Object.class, PutObjectOptions[].class), ImmutableList.<Object> 
of("bucket", object));
-   }
-
-   @Test
-   void testAppendBucketNameInURIPath() throws SecurityException, 
NoSuchMethodException {
-      GeneratedHttpRequest request = processor.createRequest(
-            method(S3Client.class, "getBucketLocation", String.class),
-            ImmutableList.<Object> of(bucketName));
-      URI uri = request.getEndpoint();
-      assertEquals(uri.getHost(), "localhost");
-      assertEquals(uri.getPath(), "/" + bucketName);
-   }
-
-   @Override
-   protected Properties setupProperties() {
-      Properties overrides = super.setupProperties();
-      overrides.setProperty(PROPERTY_SESSION_INTERVAL, 1 + "");
-      return overrides;
-   }
-}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV2Test.java
----------------------------------------------------------------------
diff --git 
a/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV2Test.java
 
b/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV2Test.java
new file mode 100644
index 0000000..1733a1b
--- /dev/null
+++ 
b/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV2Test.java
@@ -0,0 +1,158 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.s3.filters;
+
+import static org.jclouds.Constants.PROPERTY_SESSION_INTERVAL;
+import static org.jclouds.reflect.Reflection2.method;
+import static org.testng.Assert.assertEquals;
+
+import java.net.URI;
+import java.util.Properties;
+
+import org.jclouds.blobstore.binders.BindBlobToMultipartFormTest;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.rest.internal.GeneratedHttpRequest;
+import org.jclouds.s3.S3Client;
+import org.jclouds.s3.domain.AccessControlList;
+import org.jclouds.s3.domain.CannedAccessPolicy;
+import org.jclouds.s3.domain.S3Object;
+import org.jclouds.s3.internal.BaseS3ClientTest;
+import org.jclouds.s3.options.PutObjectOptions;
+import org.jclouds.s3.reference.S3Headers;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.SortedSetMultimap;
+import com.google.common.collect.TreeMultimap;
+import com.google.common.net.HttpHeaders;
+
+/**
+ * Tests behavior of {@code RequestAuthorizeSignatureV2}
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during 
surefire
+@Test(groups = "unit", testName = "RequestAuthorizeSignatureV2Test")
+public class RequestAuthorizeSignatureV2Test extends 
BaseS3ClientTest<S3Client> {
+   String bucketName = "bucket";
+
+   @DataProvider(parallel = true)
+   public Object[][] dataProvider() throws NoSuchMethodException {
+      return new Object[][]{{listOwnedBuckets()}, {putObject()}, 
{putBucketAcl()}
+
+      };
+   }
+
+   /**
+    * NOTE this test is dependent on how frequently the timestamp updates. At 
the time of writing,
+    * this was once per second. If this timestamp update interval is 
increased, it could make this
+    * test appear to hang for a long time.
+    */
+   @Test(threadPoolSize = 3, dataProvider = "dataProvider", timeOut = 10000)
+   void testIdempotent(HttpRequest request) {
+      request = filter.filter(request);
+      String signature = 
request.getFirstHeaderOrNull(HttpHeaders.AUTHORIZATION);
+      String date = request.getFirstHeaderOrNull(HttpHeaders.DATE);
+      int iterations = 1;
+      while (request.getFirstHeaderOrNull(HttpHeaders.DATE).equals(date)) {
+         date = request.getFirstHeaderOrNull(HttpHeaders.DATE);
+         request = filter.filter(request);
+         if (request.getFirstHeaderOrNull(HttpHeaders.DATE).equals(date))
+            assert 
signature.equals(request.getFirstHeaderOrNull(HttpHeaders.AUTHORIZATION)) : 
String.format(
+               "sig: %s != %s on attempt %s", signature, 
request.getFirstHeaderOrNull(HttpHeaders.AUTHORIZATION),
+               iterations);
+         else
+            iterations++;
+
+      }
+      System.out.printf("%s: %d iterations before the timestamp updated %n", 
Thread.currentThread().getName(),
+         iterations);
+   }
+
+   @Test
+   void testAppendBucketNameHostHeader() throws SecurityException, 
NoSuchMethodException {
+      GeneratedHttpRequest request = processor.createRequest(
+         method(S3Client.class, "getBucketLocation", String.class),
+         ImmutableList.<Object>of("bucket"));
+      StringBuilder builder = new StringBuilder();
+      ((RequestAuthorizeSignatureV2) filter).appendBucketName(request, 
builder);
+      assertEquals(builder.toString(), "");
+   }
+
+   @Test
+   void testAclQueryString() throws SecurityException, NoSuchMethodException {
+      HttpRequest request = putBucketAcl();
+      StringBuilder builder = new StringBuilder();
+      ((RequestAuthorizeSignatureV2) filter).appendUriPath(request, builder);
+      assertEquals(builder.toString(), "/" + bucketName + "?acl");
+   }
+
+   private GeneratedHttpRequest putBucketAcl() throws NoSuchMethodException {
+      return processor.createRequest(
+         method(S3Client.class, "putBucketACL", String.class, 
AccessControlList.class),
+         ImmutableList.<Object>of("bucket",
+            
AccessControlList.fromCannedAccessPolicy(CannedAccessPolicy.PRIVATE, "1234")));
+   }
+
+   // "?acl", "?location", "?logging", "?uploads", or "?torrent"
+
+   @Test
+   void testAppendBucketNameHostHeaderService() throws SecurityException, 
NoSuchMethodException {
+      HttpRequest request = listOwnedBuckets();
+      StringBuilder builder = new StringBuilder();
+      ((RequestAuthorizeSignatureV2) filter).appendBucketName(request, 
builder);
+      assertEquals(builder.toString(), "");
+   }
+
+   private GeneratedHttpRequest listOwnedBuckets() throws 
NoSuchMethodException {
+      return processor.createRequest(method(S3Client.class, 
"listOwnedBuckets"),
+         ImmutableList.of());
+   }
+
+   @Test
+   void testHeadersGoLowercase() throws SecurityException, 
NoSuchMethodException {
+      HttpRequest request = putObject();
+      SortedSetMultimap<String, String> canonicalizedHeaders = 
TreeMultimap.create();
+      ((RequestAuthorizeSignatureV2) filter).appendHttpHeaders(request, 
canonicalizedHeaders);
+      StringBuilder builder = new StringBuilder();
+      ((RequestAuthorizeSignatureV2) 
filter).appendAmzHeaders(canonicalizedHeaders, builder);
+      assertEquals(builder.toString(), S3Headers.USER_METADATA_PREFIX + 
"adrian:foo\n");
+   }
+
+   private HttpRequest putObject() throws NoSuchMethodException {
+      S3Object object = 
blobToS3Object.apply(BindBlobToMultipartFormTest.TEST_BLOB);
+      object.getMetadata().getUserMetadata().put("Adrian", "foo");
+      return processor.createRequest(method(S3Client.class, "putObject", 
String.class,
+         S3Object.class, PutObjectOptions[].class), 
ImmutableList.<Object>of("bucket", object));
+   }
+
+   @Test
+   void testAppendBucketNameInURIPath() throws SecurityException, 
NoSuchMethodException {
+      GeneratedHttpRequest request = processor.createRequest(
+         method(S3Client.class, "getBucketLocation", String.class),
+         ImmutableList.<Object>of(bucketName));
+      URI uri = request.getEndpoint();
+      assertEquals(uri.getHost(), "localhost");
+      assertEquals(uri.getPath(), "/" + bucketName);
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties overrides = super.setupProperties();
+      overrides.setProperty(PROPERTY_SESSION_INTERVAL, 1 + "");
+      return overrides;
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV4ChunkedUploadTest.java
----------------------------------------------------------------------
diff --git 
a/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV4ChunkedUploadTest.java
 
b/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV4ChunkedUploadTest.java
new file mode 100644
index 0000000..39a6fe1
--- /dev/null
+++ 
b/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV4ChunkedUploadTest.java
@@ -0,0 +1,199 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.s3.filters;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableList;
+import com.google.common.hash.Hasher;
+import com.google.common.hash.Hashing;
+import com.google.common.net.HttpHeaders;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+import org.jclouds.Constants;
+import org.jclouds.ContextBuilder;
+import org.jclouds.date.DateService;
+import org.jclouds.date.TimeStamp;
+import org.jclouds.domain.Credentials;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.io.Payload;
+import org.jclouds.io.Payloads;
+import org.jclouds.logging.config.NullLoggingModule;
+import org.jclouds.reflect.Invocation;
+import org.jclouds.rest.ConfiguresHttpApi;
+import org.jclouds.rest.internal.BaseRestApiTest;
+import org.jclouds.rest.internal.GeneratedHttpRequest;
+import org.jclouds.s3.S3ApiMetadata;
+import org.jclouds.s3.S3Client;
+import org.jclouds.s3.config.S3HttpApiModule;
+import org.jclouds.s3.domain.S3Object;
+import org.jclouds.s3.options.PutObjectOptions;
+import org.jclouds.util.Closeables2;
+import org.testng.annotations.Test;
+
+import javax.inject.Named;
+import javax.xml.ws.http.HTTPException;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.Date;
+
+import static com.google.common.io.BaseEncoding.base16;
+import static org.jclouds.reflect.Reflection2.method;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
+
+/**
+ * Tests behavior of {@code RequestAuthorizeSignature}
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during 
surefire
+@Test(groups = "unit", testName = 
"RequestAuthorizeSignatureV4ChunkedUploadTest")
+public class RequestAuthorizeSignatureV4ChunkedUploadTest {
+   private static final String CONTENT_SEED =
+         "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc tortor 
metus, sagittis eget augue ut,\n"
+               + "feugiat vehicula risus. Integer tortor mauris, vehicula nec 
mollis et, consectetur eget tortor. In ut\n"
+               + "elit sagittis, ultrices est ut, iaculis turpis. In hac 
habitasse platea dictumst. Donec laoreet tellus\n"
+               + "at auctor tempus. Praesent nec diam sed urna sollicitudin 
vehicula eget id est. Vivamus sed laoreet\n"
+               + "lectus. Aliquam convallis condimentum risus, vitae porta 
justo venenatis vitae. Phasellus vitae nunc\n"
+               + "varius, volutpat quam nec, mollis urna. Donec tempus, nisi 
vitae gravida facilisis, sapien sem malesuada\n"
+               + "purus, id semper libero ipsum condimentum nulla. Suspendisse 
vel mi leo. Morbi pellentesque placerat congue.\n"
+               + "Nunc sollicitudin nunc diam, nec hendrerit dui commodo sed. 
Duis dapibus commodo elit, id commodo erat\n"
+               + "congue id. Aliquam erat volutpat.\n";
+
+   private static final String CHUKED_UPLOAD_PAYLOAD_SHA256 = 
"2b6da230b03189254b2ceafe689c5298cfdd288869e80b2b9369da8f8f0a3d99";
+
+   private static final String PUT_OBJECT_AUTHORIZATION = "AWS4-HMAC-SHA256 "
+         + 
"Credential=AKIAPAEBI3QI4EXAMPLE/20150203/cn-north-1/s3/aws4_request, "
+         + 
"SignedHeaders=content-encoding;content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-decoded-content-length;x-amz-storage-class,
 "
+         + 
"Signature=3db48b3d786d599e8e785ba66030e8a9249c678a52f2432bf6fd44c97cb3145f";
+
+
+   private static final String IDENTITY = "AKIAPAEBI3QI4EXAMPLE";
+   private static final String CREDENTIAL = 
"oHkkcPcOjJnoAXpjT8GXdNeBjo6Ru7QeFExAmPlE";
+   private static final String TIMESTAMP = "Thu, 03 Feb 2015 07:11:11 GMT";
+
+   private static final String BUCKET_NAME = "test-bucket";
+   private static final String OBJECT_NAME = "ExampleChunkedObject.txt";
+
+   @ConfiguresHttpApi
+   private static final class TestS3HttpApiModule extends 
S3HttpApiModule<S3Client> {
+      @Override
+      protected String provideTimeStamp(@TimeStamp Supplier<String> cache) {
+         return TIMESTAMP;
+      }
+
+      @Override
+      protected Supplier<Date> provideTimeStampCacheDate(
+            @Named(Constants.PROPERTY_SESSION_INTERVAL) long seconds,
+            @TimeStamp final Supplier<String> timestamp,
+            final DateService dateService) {
+         return Suppliers.ofInstance(dateService.rfc822DateParse(TIMESTAMP));
+      }
+   }
+
+   public static Injector injector(Credentials creds) {
+      return ContextBuilder.newBuilder(new S3ApiMetadata())
+            .credentialsSupplier(Suppliers.<Credentials>ofInstance(creds))
+            .modules(ImmutableList.<Module>of(new 
BaseRestApiTest.MockModule(), new NullLoggingModule(),
+                  new TestS3HttpApiModule()))
+            .buildInjector();
+   }
+
+   public static RequestAuthorizeSignatureV4 filter(Credentials creds) {
+      return injector(creds).getInstance(RequestAuthorizeSignatureV4.class);
+   }
+
+   Credentials temporaryCredentials = new Credentials.Builder()
+         .identity(IDENTITY)
+         .credential(CREDENTIAL)
+         .build();
+
+
+   @Test
+   void testPutObjectWithChunkedUpload() {
+      Invocation invocation = Invocation.create(
+            method(S3Client.class, "putObject", String.class, S3Object.class, 
PutObjectOptions[].class),
+            ImmutableList.<Object>of(BUCKET_NAME));
+      byte[] content = make65KPayload().getBytes(Charset.forName("UTF-8"));
+      HttpRequest putObject = 
GeneratedHttpRequest.builder().invocation(invocation)
+            .method("PUT")
+            .endpoint("https://"; + BUCKET_NAME + 
".s3.cn-north-1.amazonaws.com.cn/" + OBJECT_NAME)
+            .addHeader(HttpHeaders.HOST, BUCKET_NAME + 
".s3.cn-north-1.amazonaws.com.cn")
+            .addHeader("x-amz-storage-class", "REDUCED_REDUNDANCY")
+            .build();
+      Payload payload = Payloads.newInputStreamPayload(new 
ByteArrayInputStream(content));
+      payload.getContentMetadata().setContentLength((long) content.length);
+      payload.getContentMetadata().setContentType("text/plain");
+      putObject.setPayload(payload);
+      HttpRequest filtered = filter(temporaryCredentials).filter(putObject);
+      assertEquals(filtered.getFirstHeaderOrNull("Authorization"), 
PUT_OBJECT_AUTHORIZATION);
+      assertEquals(filtered.getPayload().getClass(), 
ChunkedUploadPayload.class);
+
+      InputStream is = null;
+      try {
+         is = filtered.getPayload().openStream();
+         assertEquals(base16().lowerCase().encode(hash(is)), 
CHUKED_UPLOAD_PAYLOAD_SHA256);
+      } catch (IOException e) {
+         fail("open stream error", e);
+      } finally {
+         Closeables2.closeQuietly(is);
+      }
+   }
+
+   /**
+    * Want sample to upload 3 chunks for our selected chunk size of 64K; one
+    * full size chunk, one partial chunk and then the 0-byte terminator chunk.
+    * This routine just takes 1K of seed text and turns it into a 65K-or-so
+    * string for sample use.
+    */
+   private static String make65KPayload() {
+      StringBuilder oneKSeed = new StringBuilder();
+      while (oneKSeed.length() < 1024) {
+         oneKSeed.append(CONTENT_SEED);
+      }
+
+      // now scale up to meet/exceed our requirement
+      StringBuilder output = new StringBuilder();
+      for (int i = 0; i < 66; i++) {
+         output.append(oneKSeed);
+      }
+      return output.toString();
+   }
+
+   /**
+    * hash input with sha256
+    *
+    * @param input
+    * @return hash result
+    * @throws HTTPException
+    */
+   private static byte[] hash(InputStream input) {
+      try {
+         Hasher hasher = Hashing.sha256().newHasher();
+         byte[] buffer = new byte[4096];
+         int r;
+         while ((r = input.read(buffer)) != -1) {
+            hasher.putBytes(buffer, 0, r);
+         }
+         return hasher.hash().asBytes();
+      } catch (Exception e) {
+         throw new RuntimeException("Unable to compute hash while signing 
request: " + e.getMessage(), e);
+      }
+   }
+
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV4Test.java
----------------------------------------------------------------------
diff --git 
a/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV4Test.java
 
b/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV4Test.java
new file mode 100644
index 0000000..9494a86
--- /dev/null
+++ 
b/apis/s3/src/test/java/org/jclouds/s3/filters/RequestAuthorizeSignatureV4Test.java
@@ -0,0 +1,193 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.s3.filters;
+
+import static org.jclouds.reflect.Reflection2.method;
+import static org.testng.Assert.assertEquals;
+
+import java.util.Date;
+
+import javax.inject.Named;
+
+import org.jclouds.Constants;
+import org.jclouds.ContextBuilder;
+import org.jclouds.date.DateService;
+import org.jclouds.date.TimeStamp;
+import org.jclouds.domain.Credentials;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.http.options.GetOptions;
+import org.jclouds.io.Payload;
+import org.jclouds.io.Payloads;
+import org.jclouds.logging.config.NullLoggingModule;
+import org.jclouds.reflect.Invocation;
+import org.jclouds.rest.ConfiguresHttpApi;
+import org.jclouds.rest.internal.BaseRestApiTest;
+import org.jclouds.rest.internal.GeneratedHttpRequest;
+import org.jclouds.s3.S3ApiMetadata;
+import org.jclouds.s3.S3Client;
+import org.jclouds.s3.config.S3HttpApiModule;
+import org.jclouds.s3.domain.S3Object;
+import org.jclouds.s3.options.PutObjectOptions;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableList;
+import com.google.common.net.HttpHeaders;
+import com.google.inject.Injector;
+import com.google.inject.Module;
+
+/**
+ * Tests behavior of {@code RequestAuthorizeSignature}
+ */
+// NOTE:without testName, this will not call @Before* and fail w/NPE during 
surefire
+@Test(groups = "unit", testName = "RequestAuthorizeSignatureV4Test")
+public class RequestAuthorizeSignatureV4Test {
+   private static final String IDENTITY = "AKIAPAEBI3QI4EXAMPLE";
+   private static final String CREDENTIAL = 
"oHkkcPcOjJnoAXpjT8GXdNeBjo6Ru7QeFExAmPlE";
+   private static final String TIMESTAMP = "Thu, 03 Feb 2015 07:11:11 GMT";
+
+   private static final String GET_BUCKET_LOCATION_SIGNATURE_RESULT = 
"AWS4-HMAC-SHA256 "
+         + 
"Credential=AKIAPAEBI3QI4EXAMPLE/20150203/cn-north-1/s3/aws4_request, "
+         + "SignedHeaders=host;x-amz-content-sha256;x-amz-date, "
+         + 
"Signature=5634847b3ad6a857887ab0ccff2fcaf3d35ef3dc549a3c27ebc0f584a80494c3";
+
+   private static final String GET_OBJECT_RESULT = "AWS4-HMAC-SHA256 "
+         + 
"Credential=AKIAPAEBI3QI4EXAMPLE/20150203/cn-north-1/s3/aws4_request, "
+         + "SignedHeaders=host;x-amz-content-sha256;x-amz-date, "
+         + 
"Signature=fbd1d0f04a72907fb20ecd771644afd62cb689f91d26e9471b7a234531ec4718";
+
+   private static final String GET_OBJECT_ACL_RESULT = "AWS4-HMAC-SHA256 "
+         + 
"Credential=AKIAPAEBI3QI4EXAMPLE/20150203/cn-north-1/s3/aws4_request, "
+         + "SignedHeaders=host;x-amz-content-sha256;x-amz-date, "
+         + 
"Signature=52d7f31d249032b59781fe69c8124ff4bf209be3f374b28657a60d906c752381";
+
+   private static final String PUT_OBJECT_CONTENT = "text sign";
+
+   private static final String PUT_OBJECT_RESULT = "AWS4-HMAC-SHA256 "
+         + 
"Credential=AKIAPAEBI3QI4EXAMPLE/20150203/cn-north-1/s3/aws4_request, "
+         + 
"SignedHeaders=content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-storage-class,
 "
+         + 
"Signature=090f1bb1db984221ae1a20c5d12a82820a0d74b4be85f20daa1431604f41df08";
+
+   private static final String BUCKET_NAME = "test-bucket";
+   private static final String OBJECT_NAME = "ExampleObject.txt";
+
+   @ConfiguresHttpApi
+   private static final class TestS3HttpApiModule extends 
S3HttpApiModule<S3Client> {
+      @Override
+      protected String provideTimeStamp(@TimeStamp Supplier<String> cache) {
+         return TIMESTAMP;
+      }
+
+      @Override
+      protected Supplier<Date> provideTimeStampCacheDate(
+            @Named(Constants.PROPERTY_SESSION_INTERVAL) long seconds,
+            @TimeStamp final Supplier<String> timestamp,
+            final DateService dateService) {
+         return Suppliers.ofInstance(dateService.rfc822DateParse(TIMESTAMP));
+      }
+   }
+
+   public static Injector injector(Credentials creds) {
+      return ContextBuilder.newBuilder(new S3ApiMetadata())
+            .credentialsSupplier(Suppliers.<Credentials>ofInstance(creds))
+            .modules(ImmutableList.<Module>of(new 
BaseRestApiTest.MockModule(), new NullLoggingModule(),
+                  new TestS3HttpApiModule()))
+            .buildInjector();
+   }
+
+   public static RequestAuthorizeSignatureV4 filter(Credentials creds) {
+      return injector(creds).getInstance(RequestAuthorizeSignatureV4.class);
+   }
+
+   Credentials temporaryCredentials = new Credentials.Builder()
+         .identity(IDENTITY)
+         .credential(CREDENTIAL)
+         .build();
+
+
+   @Test
+   void testGetBucketLocationSignature() {
+      Invocation invocation = Invocation.create(method(S3Client.class, 
"getBucketLocation", String.class),
+            ImmutableList.<Object>of(BUCKET_NAME));
+
+      HttpRequest getBucketLocation = 
GeneratedHttpRequest.builder().method("GET")
+            .invocation(invocation)
+            .endpoint("https://"; + BUCKET_NAME + 
".s3.cn-north-1.amazonaws.com.cn/")
+            .addHeader(HttpHeaders.HOST, BUCKET_NAME + 
".s3.cn-north-1.amazonaws.com.cn")
+            .addQueryParam("location", "")
+            .build();
+      HttpRequest filtered = 
filter(temporaryCredentials).filter(getBucketLocation);
+      assertEquals(filtered.getFirstHeaderOrNull("Authorization"), 
GET_BUCKET_LOCATION_SIGNATURE_RESULT);
+   }
+
+   @Test
+   void testGetObjectSignature() {
+      Invocation invocation = Invocation.create(method(S3Client.class, 
"getObject", String.class,
+                  String.class, GetOptions[].class),
+            ImmutableList.<Object>of(BUCKET_NAME, OBJECT_NAME, new 
GetOptions[0]));
+
+      HttpRequest getObject = GeneratedHttpRequest.builder().method("GET")
+            .invocation(invocation)
+            .endpoint("https://"; + BUCKET_NAME + 
".s3.cn-north-1.amazonaws.com.cn/" + OBJECT_NAME)
+            .addHeader(HttpHeaders.HOST, BUCKET_NAME + 
".s3.cn-north-1.amazonaws.com.cn")
+            .build();
+
+      HttpRequest filtered = filter(temporaryCredentials).filter(getObject);
+      assertEquals(filtered.getFirstHeaderOrNull("Authorization"), 
GET_OBJECT_RESULT);
+
+   }
+
+   @Test
+   void testGetObjectACLSignature() {
+
+      Invocation invocation = Invocation.create(method(S3Client.class, 
"getObjectACL", String.class, String.class),
+            ImmutableList.<Object>of(BUCKET_NAME));
+
+      HttpRequest getObjectACL = GeneratedHttpRequest.builder().method("GET")
+            .invocation(invocation)
+            .endpoint("https://"; + BUCKET_NAME + 
".s3.cn-north-1.amazonaws.com.cn/" + OBJECT_NAME)
+            .addHeader(HttpHeaders.HOST, BUCKET_NAME + 
".s3.cn-north-1.amazonaws.com.cn")
+            .addQueryParam("acl", "")
+            .build();
+
+      HttpRequest filtered = filter(temporaryCredentials).filter(getObjectACL);
+      assertEquals(filtered.getFirstHeaderOrNull("Authorization"), 
GET_OBJECT_ACL_RESULT);
+   }
+
+   @Test
+   void testPutObjectSignature() {
+      Invocation invocation = Invocation.create(method(S3Client.class, 
"putObject", String.class, S3Object.class,
+                  PutObjectOptions[].class),
+            ImmutableList.<Object>of(BUCKET_NAME));
+
+      Payload payload = Payloads.newStringPayload(PUT_OBJECT_CONTENT);
+      payload.getContentMetadata().setContentType("text/plain");
+
+      HttpRequest putObject = GeneratedHttpRequest.builder().method("PUT")
+            .invocation(invocation)
+            .endpoint("https://"; + BUCKET_NAME + 
".s3.cn-north-1.amazonaws.com.cn/" + OBJECT_NAME)
+            .addHeader(HttpHeaders.HOST, BUCKET_NAME + 
".s3.cn-north-1.amazonaws.com.cn")
+            .addHeader("x-amz-storage-class", "REDUCED_REDUNDANCY")
+            .payload(payload)
+            .build();
+
+      HttpRequest filtered = filter(temporaryCredentials).filter(putObject);
+      assertEquals(filtered.getFirstHeaderOrNull("Authorization"), 
PUT_OBJECT_RESULT);
+
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/apis/s3/src/test/java/org/jclouds/s3/internal/BaseS3ClientTest.java
----------------------------------------------------------------------
diff --git 
a/apis/s3/src/test/java/org/jclouds/s3/internal/BaseS3ClientTest.java 
b/apis/s3/src/test/java/org/jclouds/s3/internal/BaseS3ClientTest.java
index 63d4481..ee48a0f 100644
--- a/apis/s3/src/test/java/org/jclouds/s3/internal/BaseS3ClientTest.java
+++ b/apis/s3/src/test/java/org/jclouds/s3/internal/BaseS3ClientTest.java
@@ -26,6 +26,7 @@ import org.jclouds.s3.S3ApiMetadata;
 import org.jclouds.s3.S3Client;
 import org.jclouds.s3.blobstore.functions.BlobToObject;
 import org.jclouds.s3.filters.RequestAuthorizeSignature;
+import org.jclouds.s3.filters.RequestAuthorizeSignatureV2;
 import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
@@ -47,7 +48,7 @@ public abstract class BaseS3ClientTest<T extends S3Client> 
extends BaseRestAnnot
    protected void setupFactory() throws IOException {
       super.setupFactory();
       blobToS3Object = injector.getInstance(BlobToObject.class);
-      filter = injector.getInstance(RequestAuthorizeSignature.class);
+      filter = injector.getInstance(RequestAuthorizeSignatureV2.class);
    }
 
    public BaseS3ClientTest() {

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/AWSS3BlobRequestSigner.java
----------------------------------------------------------------------
diff --git 
a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/AWSS3BlobRequestSigner.java
 
b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/AWSS3BlobRequestSigner.java
index 39649af..aa967c9 100644
--- 
a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/AWSS3BlobRequestSigner.java
+++ 
b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/AWSS3BlobRequestSigner.java
@@ -33,7 +33,7 @@ import org.jclouds.reflect.Invocation;
 import org.jclouds.rest.internal.RestAnnotationProcessor;
 import org.jclouds.s3.blobstore.S3BlobRequestSigner;
 import org.jclouds.s3.blobstore.functions.BlobToObject;
-import org.jclouds.s3.filters.RequestAuthorizeSignature;
+import org.jclouds.s3.filters.RequestAuthorizeSignatureV2;
 
 import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableList;
@@ -44,7 +44,7 @@ import com.google.inject.Provider;
 public class AWSS3BlobRequestSigner extends S3BlobRequestSigner<AWSS3Client> {
    public static final String TEMPORARY_SIGNATURE_PARAM = "Signature";
 
-   private final RequestAuthorizeSignature authSigner;
+   private final RequestAuthorizeSignatureV2 authSigner;
    private final String identity;
    private final DateService dateService;
    private final Provider<String> timeStampProvider;
@@ -53,7 +53,7 @@ public class AWSS3BlobRequestSigner extends 
S3BlobRequestSigner<AWSS3Client> {
    public AWSS3BlobRequestSigner(RestAnnotationProcessor processor, 
BlobToObject blobToObject,
          BlobToHttpGetOptions blob2HttpGetOptions, Class<AWSS3Client> 
interfaceClass,
          @org.jclouds.location.Provider Supplier<Credentials> credentials,
-         RequestAuthorizeSignature authSigner, @TimeStamp Provider<String> 
timeStampProvider,
+         RequestAuthorizeSignatureV2 authSigner, @TimeStamp Provider<String> 
timeStampProvider,
          DateService dateService) throws SecurityException, 
NoSuchMethodException {
       super(processor, blobToObject, blob2HttpGetOptions, interfaceClass);
       this.authSigner = authSigner;

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/AWSS3BlobRequestSignerV4.java
----------------------------------------------------------------------
diff --git 
a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/AWSS3BlobRequestSignerV4.java
 
b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/AWSS3BlobRequestSignerV4.java
new file mode 100644
index 0000000..f470999
--- /dev/null
+++ 
b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/blobstore/AWSS3BlobRequestSignerV4.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.aws.s3.blobstore;
+
+import static org.jclouds.blobstore.util.BlobStoreUtils.cleanRequest;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import org.jclouds.aws.s3.AWSS3Client;
+import org.jclouds.blobstore.domain.Blob;
+import org.jclouds.blobstore.functions.BlobToHttpGetOptions;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.reflect.Invocation;
+import org.jclouds.rest.internal.RestAnnotationProcessor;
+import org.jclouds.s3.blobstore.S3BlobRequestSigner;
+import org.jclouds.s3.blobstore.functions.BlobToObject;
+import org.jclouds.s3.filters.RequestAuthorizeSignatureV4;
+
+import com.google.common.collect.ImmutableList;
+import com.google.inject.Inject;
+
+public class AWSS3BlobRequestSignerV4 extends S3BlobRequestSigner<AWSS3Client> 
{
+
+   private final RequestAuthorizeSignatureV4 authSigner;
+
+   @Inject
+   public AWSS3BlobRequestSignerV4(RestAnnotationProcessor processor, 
BlobToObject blobToObject,
+                                   BlobToHttpGetOptions blob2HttpGetOptions, 
Class<AWSS3Client> interfaceClass,
+                                   RequestAuthorizeSignatureV4 authSigner) 
throws SecurityException, NoSuchMethodException {
+      super(processor, blobToObject, blob2HttpGetOptions, interfaceClass);
+      this.authSigner = authSigner;
+   }
+
+   @Override
+   public HttpRequest signGetBlob(String container, String name, long 
timeInSeconds) {
+      checkNotNull(container, "container");
+      checkNotNull(name, "name");
+      HttpRequest request = processor.apply(Invocation.create(getMethod, 
ImmutableList.<Object>of(container, name)));
+      request = authSigner.signForTemporaryAccess(request, timeInSeconds);
+      return cleanRequest(request);
+   }
+
+   @Override
+   public HttpRequest signPutBlob(String container, Blob blob, long 
timeInSeconds) {
+      checkNotNull(container, "container");
+      checkNotNull(blob, "blob");
+      HttpRequest request = processor.apply(Invocation.create(createMethod,
+         ImmutableList.<Object>of(container, blobToObject.apply(blob))));
+      request = authSigner.signForTemporaryAccess(request, timeInSeconds);
+      return cleanRequest(request);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/providers/aws-s3/src/main/java/org/jclouds/aws/s3/filters/AWSRequestAuthorizeSignature.java
----------------------------------------------------------------------
diff --git 
a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/filters/AWSRequestAuthorizeSignature.java
 
b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/filters/AWSRequestAuthorizeSignature.java
index 86c1bb9..f494ba1 100644
--- 
a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/filters/AWSRequestAuthorizeSignature.java
+++ 
b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/filters/AWSRequestAuthorizeSignature.java
@@ -34,13 +34,13 @@ import org.jclouds.domain.Credentials;
 import org.jclouds.http.HttpRequest;
 import org.jclouds.http.HttpUtils;
 import org.jclouds.http.internal.SignatureWire;
-import org.jclouds.s3.filters.RequestAuthorizeSignature;
+import org.jclouds.s3.filters.RequestAuthorizeSignatureV2;
 
 import com.google.common.base.Supplier;
 
 /** Signs the AWS S3 request, supporting temporary signatures. */
 @Singleton
-public class AWSRequestAuthorizeSignature extends RequestAuthorizeSignature {
+public class AWSRequestAuthorizeSignature extends RequestAuthorizeSignatureV2 {
 
    @Inject
    public AWSRequestAuthorizeSignature(SignatureWire signatureWire, 
@Named(PROPERTY_AUTH_TAG) String authTag,

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/providers/aws-s3/src/main/java/org/jclouds/aws/s3/filters/AWSRequestAuthorizeSignatureV4.java
----------------------------------------------------------------------
diff --git 
a/providers/aws-s3/src/main/java/org/jclouds/aws/s3/filters/AWSRequestAuthorizeSignatureV4.java
 
b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/filters/AWSRequestAuthorizeSignatureV4.java
new file mode 100644
index 0000000..6cb3bf1
--- /dev/null
+++ 
b/providers/aws-s3/src/main/java/org/jclouds/aws/s3/filters/AWSRequestAuthorizeSignatureV4.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.aws.s3.filters;
+
+import static org.jclouds.http.utils.Queries.queryParser;
+import static 
org.jclouds.s3.filters.AwsSignatureV4Constants.AMZ_SIGNATURE_PARAM;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import org.jclouds.http.HttpRequest;
+import org.jclouds.s3.filters.Aws4SignerForAuthorizationHeader;
+import org.jclouds.s3.filters.Aws4SignerForChunkedUpload;
+import org.jclouds.s3.filters.Aws4SignerForQueryString;
+import org.jclouds.s3.filters.RequestAuthorizeSignatureV4;
+
+/**
+ * Signs the AWS S3 request, supporting temporary signatures.
+ */
+@Singleton
+public class AWSRequestAuthorizeSignatureV4 extends 
RequestAuthorizeSignatureV4 {
+
+   @Inject
+   public AWSRequestAuthorizeSignatureV4(Aws4SignerForAuthorizationHeader 
signerForAuthorizationHeader,
+         Aws4SignerForChunkedUpload signerForChunkedUpload,
+         Aws4SignerForQueryString signerForQueryString) {
+      super(signerForAuthorizationHeader, signerForChunkedUpload, 
signerForQueryString);
+   }
+
+   @Override
+   protected HttpRequest signForAuthorizationHeader(HttpRequest request) {
+      /*
+      * Only add the Authorization header if the query string doesn't already 
contain
+      * the 'X-Amz-Signature' parameter, otherwise S3 will fail the request 
complaining about
+      * duplicate authentication methods. The 'Signature' parameter will be 
added for signed URLs
+      * with expiration.
+      */
+
+      if 
(queryParser().apply(request.getEndpoint().getQuery()).containsKey(AMZ_SIGNATURE_PARAM))
 {
+         return request;
+      }
+      return super.signForAuthorizationHeader(request);
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/8bddbb49/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/AWSS3BlobSignerV4ExpectTest.java
----------------------------------------------------------------------
diff --git 
a/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/AWSS3BlobSignerV4ExpectTest.java
 
b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/AWSS3BlobSignerV4ExpectTest.java
new file mode 100644
index 0000000..cdfccf1
--- /dev/null
+++ 
b/providers/aws-s3/src/test/java/org/jclouds/aws/s3/blobstore/AWSS3BlobSignerV4ExpectTest.java
@@ -0,0 +1,201 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jclouds.aws.s3.blobstore;
+
+import static org.jclouds.Constants.PROPERTY_CREDENTIAL;
+import static org.jclouds.Constants.PROPERTY_IDENTITY;
+import static org.testng.Assert.assertEquals;
+
+import java.util.Date;
+import java.util.Properties;
+
+import javax.inject.Named;
+
+import org.jclouds.Constants;
+import org.jclouds.aws.s3.AWSS3ApiMetadata;
+import org.jclouds.aws.s3.AWSS3ProviderMetadata;
+import org.jclouds.aws.s3.blobstore.config.AWSS3BlobStoreContextModule;
+import org.jclouds.aws.s3.config.AWSS3HttpApiModule;
+import org.jclouds.aws.s3.filters.AWSRequestAuthorizeSignatureV4;
+import org.jclouds.blobstore.BlobRequestSigner;
+import org.jclouds.blobstore.BlobStore;
+import org.jclouds.blobstore.domain.Blob;
+import org.jclouds.date.DateService;
+import org.jclouds.date.TimeStamp;
+import org.jclouds.http.HttpRequest;
+import org.jclouds.providers.ProviderMetadata;
+import org.jclouds.rest.ConfiguresHttpApi;
+import org.jclouds.s3.blobstore.S3BlobSignerExpectTest;
+import org.jclouds.s3.filters.RequestAuthorizeSignature;
+import org.testng.SkipException;
+import org.testng.annotations.Test;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.net.HttpHeaders;
+import com.google.inject.Module;
+import com.google.inject.Scopes;
+
+@Test(groups = "unit", testName = "AWSS3BlobSignerV4ExpectTest")
+public class AWSS3BlobSignerV4ExpectTest extends S3BlobSignerExpectTest {
+   private static final String IDENTITY = "AKIAPAEBI3QI4EXAMPLE";
+   private static final String CREDENTIAL = 
"oHkkcPcOjJnoAXpjT8GXdNeBjo6Ru7QeFExAmPlE";
+   private static final String TIMESTAMP = "Thu, 03 Feb 2015 07:11:11 GMT";
+
+   private static final String BUCKET_NAME = "test-bucket";
+   private static final String OBJECT_NAME = "ExampleObject.txt";
+   private static final String HOST = BUCKET_NAME + ".s3.amazonaws.com";
+
+   public AWSS3BlobSignerV4ExpectTest() {
+      provider = null;
+   }
+
+   @Override
+   protected HttpRequest getBlobWithTime() {
+      return HttpRequest.builder().method("GET")
+            .endpoint("https://"; + HOST + "/" + OBJECT_NAME
+                  + "?X-Amz-Algorithm=AWS4-HMAC-SHA256"
+                  + 
"&X-Amz-Credential=AKIAPAEBI3QI4EXAMPLE/20150203/us-east-1/s3/aws4_request"
+                  + "&X-Amz-Date=20150203T071111Z"
+                  + "&X-Amz-Expires=86400"
+                  + "&X-Amz-SignedHeaders=host"
+                  + 
"&X-Amz-Signature=0bafb6a0d99c8b7c39abe5496e9897e8c442b09278f1a647267acb25e8d1c550")
+            .addHeader(HttpHeaders.HOST, HOST)
+            .build();
+   }
+
+   @Test
+   @Override
+   public void testSignGetBlobWithTime() {
+      BlobStore getBlobWithTime = requestsSendResponses(init());
+      HttpRequest compare = getBlobWithTime();
+      HttpRequest signedRequest = 
getBlobWithTime.getContext().getSigner().signGetBlob(BUCKET_NAME, OBJECT_NAME,
+            86400l /* seconds */);
+      assertEquals(signedRequest, compare);
+   }
+
+   protected HttpRequest _putBlobWithTime() {
+      return HttpRequest.builder().method("PUT")
+            .endpoint("https://"; + HOST + "/" + OBJECT_NAME
+                  + "?X-Amz-Algorithm=AWS4-HMAC-SHA256"
+                  + 
"&X-Amz-Credential=AKIAPAEBI3QI4EXAMPLE/20150203/us-east-1/s3/aws4_request"
+                  + "&X-Amz-Date=20150203T071111Z"
+                  + "&X-Amz-Expires=86400"
+                  + "&X-Amz-SignedHeaders=host"
+                  + 
"&X-Amz-Signature=41484fb83e0c51b289907979ff96b2c743f6faf8dc70fca1c6fa78d8aeda132f")
+            .addHeader(HttpHeaders.EXPECT, "100-continue")
+            .addHeader(HttpHeaders.HOST, HOST)
+            .build();
+   }
+
+   @Test
+   @Override
+   public void testSignPutBlobWithTime() throws Exception {
+      BlobStore signPutBloblWithTime = requestsSendResponses(init());
+      Blob blob = 
signPutBloblWithTime.blobBuilder(OBJECT_NAME).payload(text).contentType("text/plain").build();
+      HttpRequest compare = _putBlobWithTime();
+      compare.setPayload(blob.getPayload());
+      HttpRequest signedRequest = 
signPutBloblWithTime.getContext().getSigner().signPutBlob(BUCKET_NAME, blob,
+            86400l /* seconds */);
+      assertEquals(signedRequest, compare);
+   }
+
+   @Override
+   protected HttpRequest putBlob() {
+      throw new SkipException("skip putBlob");
+   }
+
+   @Override
+   public void testSignPutBlob() {
+      throw new SkipException("skip testSignPutBlob");
+   }
+
+   @Override
+   public void testSignGetBlob() {
+      throw new SkipException("skip testSignGetBlob");
+   }
+
+   @Override
+   public void testSignGetBlobWithOptions() {
+      throw new SkipException("skip testSignGetBlobWithOptions");
+   }
+
+   @Override
+   public void testSignRemoveBlob() {
+      throw new SkipException("skip testSignRemoveBlob");
+   }
+
+   @Override
+   protected Module createModule() {
+      return new TestAWSS3SignerV4HttpApiModule();
+   }
+
+   @Override
+   protected Properties setupProperties() {
+      Properties props = super.setupProperties();
+      props.put(PROPERTY_IDENTITY, IDENTITY);
+      props.put(PROPERTY_CREDENTIAL, CREDENTIAL);
+      return props;
+   }
+
+   @Override
+   protected ProviderMetadata createProviderMetadata() {
+      AWSS3ApiMetadata.Builder apiBuilder = new AWSS3ApiMetadata().toBuilder();
+      apiBuilder.defaultModules(ImmutableSet.<Class<? extends 
Module>>of(TestAWSS3SignerV4HttpApiModule.class,
+            TestAWSS3BlobStoreContextModule.class));
+      return new 
AWSS3ProviderMetadata().toBuilder().apiMetadata(apiBuilder.build()).build();
+   }
+
+   public static final class TestAWSS3BlobStoreContextModule extends 
AWSS3BlobStoreContextModule {
+
+      @Override
+      protected void bindRequestSigner() {
+         // replace AWSS3BlobRequestSigner aws s3 with AWSS3BlobRequestSignerV4
+         bind(BlobRequestSigner.class).to(AWSS3BlobRequestSignerV4.class);
+      }
+
+   }
+
+   @ConfiguresHttpApi
+   public static final class TestAWSS3SignerV4HttpApiModule extends 
AWSS3HttpApiModule {
+      @Override
+      protected void configure() {
+         super.configure();
+      }
+
+      @Override
+      protected void bindRequestSigner() {
+         
bind(RequestAuthorizeSignature.class).to(AWSRequestAuthorizeSignatureV4.class).in(Scopes.SINGLETON);
+      }
+
+      @Override
+      @TimeStamp
+      protected String provideTimeStamp(@TimeStamp Supplier<String> cache) {
+         return TIMESTAMP;
+      }
+
+      @Override
+      @TimeStamp
+      protected Supplier<Date> provideTimeStampCacheDate(
+            @Named(Constants.PROPERTY_SESSION_INTERVAL) long seconds,
+            @TimeStamp final Supplier<String> timestamp,
+            final DateService dateService) {
+         return Suppliers.ofInstance(dateService.rfc822DateParse(TIMESTAMP));
+      }
+   }
+}

Reply via email to