You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jclouds.apache.org by na...@apache.org on 2014/12/02 18:34:28 UTC

jclouds git commit: JCLOUDS-753: Make ConnectionSpec configurable in the OkHttp driver

Repository: jclouds
Updated Branches:
  refs/heads/master c635b3006 -> 958d09ecb


JCLOUDS-753: Make ConnectionSpec configurable in the OkHttp driver


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

Branch: refs/heads/master
Commit: 958d09ecbd2956f6cedfadfa2c67040c386bd105
Parents: c635b30
Author: Ignasi Barrera <na...@apache.org>
Authored: Fri Nov 28 00:02:03 2014 +0100
Committer: Ignasi Barrera <na...@apache.org>
Committed: Tue Dec 2 18:17:17 2014 +0100

----------------------------------------------------------------------
 .../org/jclouds/http/BaseMockWebServerTest.java |  9 +-
 .../http/okhttp/OkHttpClientSupplier.java       | 43 +++++++++
 .../okhttp/OkHttpCommandExecutorService.java    |  2 +-
 .../OkHttpCommandExecutorServiceModule.java     | 18 ++--
 .../OkHttpCommandExecutorServiceTest.java       | 95 ++++++++++++++++++++
 5 files changed, 153 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/jclouds/blob/958d09ec/core/src/test/java/org/jclouds/http/BaseMockWebServerTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/jclouds/http/BaseMockWebServerTest.java b/core/src/test/java/org/jclouds/http/BaseMockWebServerTest.java
index 6ec09b3..b3434ae 100644
--- a/core/src/test/java/org/jclouds/http/BaseMockWebServerTest.java
+++ b/core/src/test/java/org/jclouds/http/BaseMockWebServerTest.java
@@ -85,12 +85,19 @@ public abstract class BaseMockWebServerTest {
     * Creates a test api for the given class and URL.
     */
    protected <T extends Closeable> T api(Class<T> apiClass, String url) {
+      return api(apiClass, url, createConnectionModule());
+   }
+
+   /**
+    * Creates a test api for the given class, URI and Module.
+    */
+   protected <T extends Closeable> T api(Class<T> apiClass, String url, Module... connectionModules) {
       Properties properties = new Properties();
       properties.setProperty(PROPERTY_TRUST_ALL_CERTS, "true");
       properties.setProperty(PROPERTY_RELAX_HOSTNAME, "true");
       addOverrideProperties(properties);
       return ContextBuilder.newBuilder(AnonymousProviderMetadata.forApiOnEndpoint(apiClass, url))
-            .modules(ImmutableSet.<Module> of(createConnectionModule())).overrides(properties).buildApi(apiClass);
+            .modules(ImmutableSet.copyOf(connectionModules)).overrides(properties).buildApi(apiClass);
    }
 
    /**

http://git-wip-us.apache.org/repos/asf/jclouds/blob/958d09ec/drivers/okhttp/src/main/java/org/jclouds/http/okhttp/OkHttpClientSupplier.java
----------------------------------------------------------------------
diff --git a/drivers/okhttp/src/main/java/org/jclouds/http/okhttp/OkHttpClientSupplier.java b/drivers/okhttp/src/main/java/org/jclouds/http/okhttp/OkHttpClientSupplier.java
new file mode 100644
index 0000000..9254ec5
--- /dev/null
+++ b/drivers/okhttp/src/main/java/org/jclouds/http/okhttp/OkHttpClientSupplier.java
@@ -0,0 +1,43 @@
+/*
+ * 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.http.okhttp;
+
+import org.jclouds.http.okhttp.OkHttpClientSupplier.NewOkHttpClient;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Supplier;
+import com.google.inject.ImplementedBy;
+import com.squareup.okhttp.OkHttpClient;
+
+/**
+ * Provides the OkHttp client used for all requests. This could be used to
+ * designate a custom SSL context or limit TLS ciphers.
+ * <p>
+ * Note that it should configured it in the Guice module designated as
+ * <code>@ConfiguresHttpApi</code>.
+ */
+@Beta
+@ImplementedBy(NewOkHttpClient.class)
+public interface OkHttpClientSupplier extends Supplier<OkHttpClient> {
+
+   static final class NewOkHttpClient implements OkHttpClientSupplier {
+      @Override
+      public OkHttpClient get() {
+         return new OkHttpClient();
+      }
+   }
+}

http://git-wip-us.apache.org/repos/asf/jclouds/blob/958d09ec/drivers/okhttp/src/main/java/org/jclouds/http/okhttp/OkHttpCommandExecutorService.java
----------------------------------------------------------------------
diff --git a/drivers/okhttp/src/main/java/org/jclouds/http/okhttp/OkHttpCommandExecutorService.java b/drivers/okhttp/src/main/java/org/jclouds/http/okhttp/OkHttpCommandExecutorService.java
index d6610fb..72370d3 100644
--- a/drivers/okhttp/src/main/java/org/jclouds/http/okhttp/OkHttpCommandExecutorService.java
+++ b/drivers/okhttp/src/main/java/org/jclouds/http/okhttp/OkHttpCommandExecutorService.java
@@ -57,7 +57,7 @@ import com.squareup.okhttp.Response;
 
 public final class OkHttpCommandExecutorService extends BaseHttpCommandExecutorService<Request> {
 
-   private static final String DEFAULT_USER_AGENT = String.format("jclouds/%s java/%s", JcloudsVersion.get(),
+   private static final String DEFAULT_USER_AGENT = String.format("jclouds-okhttp/%s java/%s", JcloudsVersion.get(),
          System.getProperty("java.version"));
 
    private final Function<URI, Proxy> proxyForURI;

http://git-wip-us.apache.org/repos/asf/jclouds/blob/958d09ec/drivers/okhttp/src/main/java/org/jclouds/http/okhttp/config/OkHttpCommandExecutorServiceModule.java
----------------------------------------------------------------------
diff --git a/drivers/okhttp/src/main/java/org/jclouds/http/okhttp/config/OkHttpCommandExecutorServiceModule.java b/drivers/okhttp/src/main/java/org/jclouds/http/okhttp/config/OkHttpCommandExecutorServiceModule.java
index 07a92e7..9c47d81 100644
--- a/drivers/okhttp/src/main/java/org/jclouds/http/okhttp/config/OkHttpCommandExecutorServiceModule.java
+++ b/drivers/okhttp/src/main/java/org/jclouds/http/okhttp/config/OkHttpCommandExecutorServiceModule.java
@@ -26,6 +26,7 @@ import org.jclouds.http.HttpCommandExecutorService;
 import org.jclouds.http.HttpUtils;
 import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
 import org.jclouds.http.config.SSLModule;
+import org.jclouds.http.okhttp.OkHttpClientSupplier;
 import org.jclouds.http.okhttp.OkHttpCommandExecutorService;
 
 import com.google.common.base.Supplier;
@@ -51,24 +52,23 @@ public class OkHttpCommandExecutorServiceModule extends AbstractModule {
    }
 
    private static final class OkHttpClientProvider implements Provider<OkHttpClient> {
-
-      @Inject(optional = true)
-      private Supplier<SSLContext> sslContextSupplier;
       private final HostnameVerifier verifier;
       private final Supplier<SSLContext> untrustedSSLContextProvider;
       private final HttpUtils utils;
+      private final OkHttpClientSupplier clientSupplier;
 
       @Inject
       OkHttpClientProvider(HttpUtils utils, @Named("untrusted") HostnameVerifier verifier,
-            @Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider) {
+            @Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider, OkHttpClientSupplier clientSupplier) {
          this.utils = utils;
          this.verifier = verifier;
          this.untrustedSSLContextProvider = untrustedSSLContextProvider;
+         this.clientSupplier = clientSupplier;
       }
 
       @Override
       public OkHttpClient get() {
-         OkHttpClient client = new OkHttpClient();
+         OkHttpClient client = clientSupplier.get();
          client.setConnectTimeout(utils.getConnectionTimeout(), TimeUnit.MILLISECONDS);
          client.setReadTimeout(utils.getSocketOpenTimeout(), TimeUnit.MILLISECONDS);
          // do not follow redirects since https redirects don't work properly
@@ -79,18 +79,12 @@ public class OkHttpCommandExecutorServiceModule extends AbstractModule {
          if (utils.relaxHostname()) {
             client.setHostnameVerifier(verifier);
          }
-         if (sslContextSupplier != null) {
-            // used for providers which e.g. use certs for authentication (like
-            // FGCP) Provider provides SSLContext impl (which inits context with
-            // key manager)
-            client.setSslSocketFactory(sslContextSupplier.get().getSocketFactory());
-         } else if (utils.trustAllCerts()) {
+         if (utils.trustAllCerts()) {
             client.setSslSocketFactory(untrustedSSLContextProvider.get().getSocketFactory());
          }
 
          return client;
       }
-
    }
 
 }

http://git-wip-us.apache.org/repos/asf/jclouds/blob/958d09ec/drivers/okhttp/src/test/java/org/jclouds/http/okhttp/OkHttpCommandExecutorServiceTest.java
----------------------------------------------------------------------
diff --git a/drivers/okhttp/src/test/java/org/jclouds/http/okhttp/OkHttpCommandExecutorServiceTest.java b/drivers/okhttp/src/test/java/org/jclouds/http/okhttp/OkHttpCommandExecutorServiceTest.java
index 0d204b9..c0b3afa 100644
--- a/drivers/okhttp/src/test/java/org/jclouds/http/okhttp/OkHttpCommandExecutorServiceTest.java
+++ b/drivers/okhttp/src/test/java/org/jclouds/http/okhttp/OkHttpCommandExecutorServiceTest.java
@@ -23,12 +23,15 @@ import static org.jclouds.util.Closeables2.closeQuietly;
 import static org.testng.Assert.assertEquals;
 
 import java.io.Closeable;
+import java.util.List;
 import java.util.Properties;
 
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 
 import org.jclouds.http.BaseHttpCommandExecutorServiceIntegrationTest;
+import org.jclouds.http.HttpResponseException;
+import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
 import org.jclouds.http.okhttp.config.OkHttpCommandExecutorServiceModule;
 import org.jclouds.rest.annotations.BinderParam;
 import org.jclouds.rest.annotations.PATCH;
@@ -36,7 +39,12 @@ import org.jclouds.rest.binders.BindToStringPayload;
 import org.testng.annotations.Test;
 
 import com.google.common.base.Charsets;
+import com.google.common.collect.ImmutableList;
+import com.google.inject.AbstractModule;
 import com.google.inject.Module;
+import com.squareup.okhttp.ConnectionSpec;
+import com.squareup.okhttp.OkHttpClient;
+import com.squareup.okhttp.TlsVersion;
 import com.squareup.okhttp.mockwebserver.MockResponse;
 import com.squareup.okhttp.mockwebserver.MockWebServer;
 import com.squareup.okhttp.mockwebserver.RecordedRequest;
@@ -149,4 +157,91 @@ public class OkHttpCommandExecutorServiceTest extends BaseHttpCommandExecutorSer
          server.shutdown();
       }
    }
+
+   @Test(expectedExceptions = HttpResponseException.class, expectedExceptionsMessageRegExp = ".*exhausted connection specs.*")
+   public void testSSLConnectionFailsIfOnlyHttpConfigured() throws Exception {
+      MockWebServer server = mockWebServer(new MockResponse());
+      server.useHttps(sslContext.getSocketFactory(), false);
+      Module httpConfigModule = new ConnectionSpecModule(ConnectionSpec.CLEARTEXT);
+      PatchApi api = api(PatchApi.class, server.getUrl("/").toString(), httpConfigModule);
+      try {
+         api.patchNothing("");
+      } finally {
+         closeQuietly(api);
+         server.shutdown();
+      }
+   }
+
+   @Test(expectedExceptions = HttpResponseException.class, expectedExceptionsMessageRegExp = ".*exhausted connection specs.*")
+   public void testHTTPConnectionFailsIfOnlySSLConfigured() throws Exception {
+      MockWebServer server = mockWebServer(new MockResponse());
+      Module httpConfigModule = new ConnectionSpecModule(ConnectionSpec.MODERN_TLS);
+      PatchApi api = api(PatchApi.class, server.getUrl("/").toString(), httpConfigModule);
+      try {
+         api.patchNothing("");
+      } finally {
+         closeQuietly(api);
+         server.shutdown();
+      }
+   }
+
+   @Test
+   public void testBothProtocolsSucceedIfSSLAndHTTPConfigured() throws Exception {
+      MockWebServer redirectTarget = mockWebServer(new MockResponse());
+      MockWebServer server = mockWebServer(new MockResponse().setResponseCode(302).setHeader("Location",
+            redirectTarget.getUrl("/").toString()));
+      server.useHttps(sslContext.getSocketFactory(), false);
+      Module httpConfigModule = new ConnectionSpecModule(ConnectionSpec.CLEARTEXT, ConnectionSpec.MODERN_TLS);
+      PatchApi api = api(PatchApi.class, server.getUrl("/").toString(), httpConfigModule);
+      try {
+         api.patchNothing("");
+         assertEquals(server.getRequestCount(), 1);
+         assertEquals(redirectTarget.getRequestCount(), 1);
+      } finally {
+         closeQuietly(api);
+         server.shutdown();
+         redirectTarget.shutdown();
+      }
+   }
+
+   @Test
+   public void testRestrictedSSLProtocols() throws Exception {
+      MockWebServer server = mockWebServer(new MockResponse());
+      server.useHttps(sslContext.getSocketFactory(), false);
+      ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS).tlsVersions(TlsVersion.TLS_1_2)
+            .build();
+      PatchApi api = api(PatchApi.class, server.getUrl("/").toString(), new ConnectionSpecModule(spec));
+      try {
+         api.patchNothing("");
+         assertEquals(server.getRequestCount(), 1);
+         RecordedRequest request = server.takeRequest();
+         assertEquals(request.getSslProtocol(), "TLSv1.2");
+      } finally {
+         closeQuietly(api);
+         server.shutdown();
+      }
+   }
+
+   @ConfiguresHttpCommandExecutorService
+   private static final class ConnectionSpecModule extends AbstractModule {
+      private final List<ConnectionSpec> connectionSpecs;
+
+      public ConnectionSpecModule(ConnectionSpec... connectionSpecs) {
+         this.connectionSpecs = ImmutableList.copyOf(connectionSpecs);
+      }
+
+      @Override
+      protected void configure() {
+         install(new OkHttpCommandExecutorServiceModule());
+         bind(OkHttpClientSupplier.class).toInstance(new OkHttpClientSupplier() {
+            @Override
+            public OkHttpClient get() {
+               OkHttpClient client = new OkHttpClient();
+               client.setConnectionSpecs(connectionSpecs);
+               return client;
+            }
+         });
+      }
+   }
+
 }