mirror of https://github.com/apache/jclouds.git
JCLOUDS-753: Make ConnectionSpec configurable in the OkHttp driver
This commit is contained in:
parent
c635b3006d
commit
958d09ecbd
|
@ -85,12 +85,19 @@ public abstract class BaseMockWebServerTest {
|
||||||
* Creates a test api for the given class and URL.
|
* Creates a test api for the given class and URL.
|
||||||
*/
|
*/
|
||||||
protected <T extends Closeable> T api(Class<T> apiClass, String 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 properties = new Properties();
|
||||||
properties.setProperty(PROPERTY_TRUST_ALL_CERTS, "true");
|
properties.setProperty(PROPERTY_TRUST_ALL_CERTS, "true");
|
||||||
properties.setProperty(PROPERTY_RELAX_HOSTNAME, "true");
|
properties.setProperty(PROPERTY_RELAX_HOSTNAME, "true");
|
||||||
addOverrideProperties(properties);
|
addOverrideProperties(properties);
|
||||||
return ContextBuilder.newBuilder(AnonymousProviderMetadata.forApiOnEndpoint(apiClass, url))
|
return ContextBuilder.newBuilder(AnonymousProviderMetadata.forApiOnEndpoint(apiClass, url))
|
||||||
.modules(ImmutableSet.<Module> of(createConnectionModule())).overrides(properties).buildApi(apiClass);
|
.modules(ImmutableSet.copyOf(connectionModules)).overrides(properties).buildApi(apiClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,7 +57,7 @@ import com.squareup.okhttp.Response;
|
||||||
|
|
||||||
public final class OkHttpCommandExecutorService extends BaseHttpCommandExecutorService<Request> {
|
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"));
|
System.getProperty("java.version"));
|
||||||
|
|
||||||
private final Function<URI, Proxy> proxyForURI;
|
private final Function<URI, Proxy> proxyForURI;
|
||||||
|
|
|
@ -26,6 +26,7 @@ import org.jclouds.http.HttpCommandExecutorService;
|
||||||
import org.jclouds.http.HttpUtils;
|
import org.jclouds.http.HttpUtils;
|
||||||
import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
|
import org.jclouds.http.config.ConfiguresHttpCommandExecutorService;
|
||||||
import org.jclouds.http.config.SSLModule;
|
import org.jclouds.http.config.SSLModule;
|
||||||
|
import org.jclouds.http.okhttp.OkHttpClientSupplier;
|
||||||
import org.jclouds.http.okhttp.OkHttpCommandExecutorService;
|
import org.jclouds.http.okhttp.OkHttpCommandExecutorService;
|
||||||
|
|
||||||
import com.google.common.base.Supplier;
|
import com.google.common.base.Supplier;
|
||||||
|
@ -51,24 +52,23 @@ public class OkHttpCommandExecutorServiceModule extends AbstractModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class OkHttpClientProvider implements Provider<OkHttpClient> {
|
private static final class OkHttpClientProvider implements Provider<OkHttpClient> {
|
||||||
|
|
||||||
@Inject(optional = true)
|
|
||||||
private Supplier<SSLContext> sslContextSupplier;
|
|
||||||
private final HostnameVerifier verifier;
|
private final HostnameVerifier verifier;
|
||||||
private final Supplier<SSLContext> untrustedSSLContextProvider;
|
private final Supplier<SSLContext> untrustedSSLContextProvider;
|
||||||
private final HttpUtils utils;
|
private final HttpUtils utils;
|
||||||
|
private final OkHttpClientSupplier clientSupplier;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
OkHttpClientProvider(HttpUtils utils, @Named("untrusted") HostnameVerifier verifier,
|
OkHttpClientProvider(HttpUtils utils, @Named("untrusted") HostnameVerifier verifier,
|
||||||
@Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider) {
|
@Named("untrusted") Supplier<SSLContext> untrustedSSLContextProvider, OkHttpClientSupplier clientSupplier) {
|
||||||
this.utils = utils;
|
this.utils = utils;
|
||||||
this.verifier = verifier;
|
this.verifier = verifier;
|
||||||
this.untrustedSSLContextProvider = untrustedSSLContextProvider;
|
this.untrustedSSLContextProvider = untrustedSSLContextProvider;
|
||||||
|
this.clientSupplier = clientSupplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public OkHttpClient get() {
|
public OkHttpClient get() {
|
||||||
OkHttpClient client = new OkHttpClient();
|
OkHttpClient client = clientSupplier.get();
|
||||||
client.setConnectTimeout(utils.getConnectionTimeout(), TimeUnit.MILLISECONDS);
|
client.setConnectTimeout(utils.getConnectionTimeout(), TimeUnit.MILLISECONDS);
|
||||||
client.setReadTimeout(utils.getSocketOpenTimeout(), TimeUnit.MILLISECONDS);
|
client.setReadTimeout(utils.getSocketOpenTimeout(), TimeUnit.MILLISECONDS);
|
||||||
// do not follow redirects since https redirects don't work properly
|
// do not follow redirects since https redirects don't work properly
|
||||||
|
@ -79,18 +79,12 @@ public class OkHttpCommandExecutorServiceModule extends AbstractModule {
|
||||||
if (utils.relaxHostname()) {
|
if (utils.relaxHostname()) {
|
||||||
client.setHostnameVerifier(verifier);
|
client.setHostnameVerifier(verifier);
|
||||||
}
|
}
|
||||||
if (sslContextSupplier != null) {
|
if (utils.trustAllCerts()) {
|
||||||
// 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()) {
|
|
||||||
client.setSslSocketFactory(untrustedSSLContextProvider.get().getSocketFactory());
|
client.setSslSocketFactory(untrustedSSLContextProvider.get().getSocketFactory());
|
||||||
}
|
}
|
||||||
|
|
||||||
return client;
|
return client;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,15 @@ import static org.jclouds.util.Closeables2.closeQuietly;
|
||||||
import static org.testng.Assert.assertEquals;
|
import static org.testng.Assert.assertEquals;
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
import javax.ws.rs.Path;
|
import javax.ws.rs.Path;
|
||||||
import javax.ws.rs.PathParam;
|
import javax.ws.rs.PathParam;
|
||||||
|
|
||||||
import org.jclouds.http.BaseHttpCommandExecutorServiceIntegrationTest;
|
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.http.okhttp.config.OkHttpCommandExecutorServiceModule;
|
||||||
import org.jclouds.rest.annotations.BinderParam;
|
import org.jclouds.rest.annotations.BinderParam;
|
||||||
import org.jclouds.rest.annotations.PATCH;
|
import org.jclouds.rest.annotations.PATCH;
|
||||||
|
@ -36,7 +39,12 @@ import org.jclouds.rest.binders.BindToStringPayload;
|
||||||
import org.testng.annotations.Test;
|
import org.testng.annotations.Test;
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
|
import com.google.common.collect.ImmutableList;
|
||||||
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Module;
|
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.MockResponse;
|
||||||
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||||
import com.squareup.okhttp.mockwebserver.RecordedRequest;
|
import com.squareup.okhttp.mockwebserver.RecordedRequest;
|
||||||
|
@ -149,4 +157,91 @@ public class OkHttpCommandExecutorServiceTest extends BaseHttpCommandExecutorSer
|
||||||
server.shutdown();
|
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;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue