You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by ad...@apache.org on 2014/10/25 20:01:39 UTC

[3/4] git commit: JCLOUDS-264 Conditionally allow default http driver to support sending >2GB payloads without chunked encoding.

JCLOUDS-264 Conditionally allow default http driver to support sending >2GB payloads without chunked encoding.


Project: http://git-wip-us.apache.org/repos/asf/jclouds/repo
Commit: http://git-wip-us.apache.org/repos/asf/jclouds/commit/6f1c6621
Tree: http://git-wip-us.apache.org/repos/asf/jclouds/tree/6f1c6621
Diff: http://git-wip-us.apache.org/repos/asf/jclouds/diff/6f1c6621

Branch: refs/heads/1.8.x
Commit: 6f1c6621a516f7b32b720f28bdf5441d954102bf
Parents: d6940dc
Author: Adrian Cole <ad...@gmail.com>
Authored: Fri Oct 24 19:41:03 2014 -0700
Committer: Adrian Cole <ad...@gmail.com>
Committed: Sat Oct 25 10:39:06 2014 -0700

----------------------------------------------------------------------
 .../JavaUrlHttpCommandExecutorService.java      | 40 +++++++++--------
 .../main/java/org/jclouds/rest/HttpClient.java  |  3 +-
 ...tpCommandExecutorServiceIntegrationTest.java | 45 ++++++++++++++++++--
 3 files changed, 67 insertions(+), 21 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/6f1c6621/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java b/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java
index b326bc7..6afd80d 100644
--- a/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java
+++ b/core/src/main/java/org/jclouds/http/internal/JavaUrlHttpCommandExecutorService.java
@@ -16,7 +16,6 @@
  */
 package org.jclouds.http.internal;
 
-import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Preconditions.checkNotNull;
 import static com.google.common.base.Throwables.propagate;
 import static com.google.common.io.ByteStreams.toByteArray;
@@ -65,14 +64,11 @@ import com.google.common.io.Closeables;
 import com.google.common.io.CountingOutputStream;
 import com.google.inject.Inject;
 
-/**
- * Basic implementation of a {@link HttpCommandExecutorService}.
- */
 @Singleton
 public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorService<HttpURLConnection> {
 
-   public static final String DEFAULT_USER_AGENT = String.format("jclouds/%s java/%s", JcloudsVersion.get(), System
-            .getProperty("java.version"));
+   public static final String DEFAULT_USER_AGENT = String.format("jclouds/%s java/%s", JcloudsVersion.get(),
+         System.getProperty("java.version"));
 
    protected final Supplier<SSLContext> untrustedSSLContextProvider;
    protected final Function<URI, Proxy> proxyForURI;
@@ -82,13 +78,13 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
 
    @Inject
    public JavaUrlHttpCommandExecutorService(HttpUtils utils, ContentMetadataCodec contentMetadataCodec,
-            DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
-            DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier,
-            @Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider, Function<URI, Proxy> proxyForURI) 
-                  throws SecurityException, NoSuchFieldException {
+         DelegatingRetryHandler retryHandler, IOExceptionRetryHandler ioRetryHandler,
+         DelegatingErrorHandler errorHandler, HttpWire wire, @Named("untrusted") HostnameVerifier verifier,
+         @Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider, Function<URI, Proxy> proxyForURI) {
       super(utils, contentMetadataCodec, retryHandler, ioRetryHandler, errorHandler, wire);
-      if (utils.getMaxConnections() > 0)
+      if (utils.getMaxConnections() > 0) {
          System.setProperty("http.maxConnections", String.valueOf(checkNotNull(utils, "utils").getMaxConnections()));
+      }
       this.untrustedSSLContextProvider = checkNotNull(untrustedSSLContextProvider, "untrustedSSLContextProvider");
       this.verifier = checkNotNull(verifier, "verifier");
       this.proxyForURI = checkNotNull(proxyForURI, "proxyForURI");
@@ -179,14 +175,13 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
             writePayloadToConnection(payload, "streaming", connection);
          } else {
             Long length = checkNotNull(md.getContentLength(), "payload.getContentLength");
-            // TODO: remove check after moving to JDK 7.
-            checkArgument(length <= Integer.MAX_VALUE,
-                  "Cannot transfer 2 GB or larger chunks due to JDK 1.6 limitations." +
-                  " Use chunked encoding or multi-part upload, if possible." +
-                  " For more information: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6755625");
             if (length > 0) {
                connection.setRequestProperty(CONTENT_LENGTH, length.toString());
-               connection.setFixedLengthStreamingMode(length.intValue());
+               if (length <= Integer.MAX_VALUE) {
+                  connection.setFixedLengthStreamingMode(length.intValue());
+               } else {
+                  setFixedLengthStreamingMode(connection, length);
+               }
                writePayloadToConnection(payload, length, connection);
             } else {
                writeNothing(connection);
@@ -198,6 +193,17 @@ public class JavaUrlHttpCommandExecutorService extends BaseHttpCommandExecutorSe
       return connection;
    }
 
+   /** Uses {@link HttpURLConnection#setFixedLengthStreamingMode(long)} if possible or throws if not. */
+   private static void setFixedLengthStreamingMode(HttpURLConnection connection, long length) {
+      try { // Not caching method as invocation is literally sending > 2GB, which means reflection isn't a limiter!
+         HttpURLConnection.class.getMethod("setFixedLengthStreamingMode", long.class).invoke(connection, length);
+      } catch (Exception e) {
+         throw new IllegalArgumentException("Cannot transfer 2 GB or larger chunks due to JDK 1.6 limitations." +
+               " Use chunked encoding or multi-part upload, if possible, or use a different http driver." +
+               " For more information: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6755625");
+      }
+   }
+
    /**
     * Creates and initializes the connection.
     */

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6f1c6621/core/src/main/java/org/jclouds/rest/HttpClient.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/jclouds/rest/HttpClient.java b/core/src/main/java/org/jclouds/rest/HttpClient.java
index 126af29..f982a66 100644
--- a/core/src/main/java/org/jclouds/rest/HttpClient.java
+++ b/core/src/main/java/org/jclouds/rest/HttpClient.java
@@ -16,6 +16,7 @@
  */
 package org.jclouds.rest;
 
+import java.io.Closeable;
 import java.io.InputStream;
 import java.net.URI;
 
@@ -38,7 +39,7 @@ import org.jclouds.rest.annotations.ResponseParser;
 /**
  * Simple client
  */
-public interface HttpClient {
+public interface HttpClient extends Closeable {
    /**
     * @return eTag
     */

http://git-wip-us.apache.org/repos/asf/jclouds/blob/6f1c6621/core/src/test/java/org/jclouds/http/JavaUrlHttpCommandExecutorServiceIntegrationTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/jclouds/http/JavaUrlHttpCommandExecutorServiceIntegrationTest.java b/core/src/test/java/org/jclouds/http/JavaUrlHttpCommandExecutorServiceIntegrationTest.java
index d1d4c60..265048a 100644
--- a/core/src/test/java/org/jclouds/http/JavaUrlHttpCommandExecutorServiceIntegrationTest.java
+++ b/core/src/test/java/org/jclouds/http/JavaUrlHttpCommandExecutorServiceIntegrationTest.java
@@ -16,20 +16,29 @@
  */
 package org.jclouds.http;
 
+import static com.google.common.io.Closeables.close;
+import static com.google.common.net.HttpHeaders.CONTENT_LENGTH;
 import static org.jclouds.Constants.PROPERTY_MAX_CONNECTIONS_PER_CONTEXT;
 import static org.jclouds.Constants.PROPERTY_MAX_CONNECTIONS_PER_HOST;
 import static org.jclouds.Constants.PROPERTY_USER_THREADS;
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.fail;
 
 import java.util.Properties;
 
 import org.jclouds.http.config.JavaUrlHttpCommandExecutorServiceModule;
+import org.jclouds.io.Payload;
+import org.jclouds.io.payloads.StringPayload;
+import org.jclouds.rest.HttpClient;
+import org.jclouds.utils.TestUtils;
 import org.testng.annotations.Test;
 
 import com.google.inject.Module;
+import com.squareup.okhttp.mockwebserver.Dispatcher;
+import com.squareup.okhttp.mockwebserver.MockResponse;
+import com.squareup.okhttp.mockwebserver.MockWebServer;
+import com.squareup.okhttp.mockwebserver.RecordedRequest;
 
-/**
- * Tests the functionality of the {@link JavaUrlHttpCommandExecutorService}
- */
 @Test(groups = "integration")
 public class JavaUrlHttpCommandExecutorServiceIntegrationTest extends BaseHttpCommandExecutorServiceIntegrationTest {
 
@@ -43,4 +52,34 @@ public class JavaUrlHttpCommandExecutorServiceIntegrationTest extends BaseHttpCo
       props.setProperty(PROPERTY_USER_THREADS, 5 + "");
    }
 
+   @Test public void longContentLengthSupported() throws Exception {
+      long reallyLongContent = TestUtils.isJava6() ? Integer.MAX_VALUE : Long.MAX_VALUE;
+
+      // Setup a mock server that doesn't attempt to read the request payload.
+      MockWebServer server = new MockWebServer();
+      server.setDispatcher(new Dispatcher() {
+         @Override public MockResponse dispatch(RecordedRequest recordedRequest) {
+            return new MockResponse();
+         }
+      });
+      server.play();
+
+      HttpClient client =  api(HttpClient.class, server.getUrl("/").toString());
+
+      // Make a fake payload that has no data, but says there's a lot!
+      Payload fakePayload = new StringPayload("");
+      fakePayload.getContentMetadata().setContentLength(reallyLongContent);
+
+      try {
+         try {
+            client.post(server.getUrl("/").toURI(), fakePayload);
+            fail("Should have errored since we didn't sent that much data!");
+         } catch (HttpResponseException expected) {
+         }
+         assertEquals(server.takeRequest().getHeader(CONTENT_LENGTH), String.valueOf(reallyLongContent));
+      } finally {
+         close(client, true);
+         server.shutdown();
+      }
+   }
 }