Skip to content

Commit

Permalink
Merge pull request #47 from rpmoore/master
Browse files Browse the repository at this point in the history
Adds better handling for HTTPS
  • Loading branch information
hansdude committed Oct 13, 2014
2 parents 17955c8 + bc2f40a commit 1cc175c
Show file tree
Hide file tree
Showing 19 changed files with 197 additions and 55 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

allprojects {
group = 'com.spectralogic'
version = '0.7.4-SNAPSHOT'
version = '0.7.5-SNAPSHOT'
}

subprojects {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ public class Util {
private Util() {}

public static Ds3Client fromEnv() {
final Ds3ClientBuilder builder = clientBuilder();
builder.withHttps(false);
return builder.build();
}

public static Ds3Client insecureFromEnv() {
final Ds3ClientBuilder builder = clientBuilder();
builder.withCertificateVerification(false);
return builder.build();
}

private static Ds3ClientBuilder clientBuilder() {
final String endpoint = System.getenv("DS3_ENDPOINT");
final String accessKey = System.getenv("DS3_ACCESS_KEY");
final String secretKey = System.getenv("DS3_SECRET_KEY");
Expand All @@ -40,11 +52,10 @@ public static Ds3Client fromEnv() {
}

final Ds3ClientBuilder builder = Ds3ClientBuilder.create(endpoint,new Credentials(accessKey, secretKey));
builder.withHttpSecure(false);
if (httpProxy != null) {
builder.withProxy(httpProxy);
}
return builder.build();
return builder;
}

private static final String[] BOOKS = {"beowulf.txt", "sherlock_holmes.txt", "tale_of_two_cities.txt", "ulysses.txt"};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.spectralogic.ds3client.integration;

import com.spectralogic.ds3client.Ds3Client;
import com.spectralogic.ds3client.commands.GetServiceRequest;
import com.spectralogic.ds3client.commands.GetServiceResponse;
import org.junit.BeforeClass;
import org.junit.Test;

import java.io.IOException;
import java.security.SignatureException;

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.assertThat;

/**
*
* This test is intended to be a sanity check to make sure that we can successfully ignore ssl certificate validation.
*
*/
public class Insecure_Test {

private static Ds3Client client;

@BeforeClass
public static void startup() {
client = Util.insecureFromEnv();
}

@Test
public void getService() throws SignatureException, IOException{
final GetServiceResponse response = client.getService(new GetServiceRequest());

assertThat(response, is(notNullValue()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,19 @@ static class Builder implements com.spectralogic.ds3client.utils.Builder<Connect

private final String endpoint;
private final Credentials credentials;
private boolean secure = false;
private boolean https = false;
private URI proxy = null;
private int retries = 5;
private int bufferSize = 1024 * 1024;
private boolean certificateVerification;

private Builder(final String endpoint, final Credentials credentials) {
this.endpoint = endpoint;
this.credentials = credentials;
}

public Builder withSecure(final boolean secure) {
this.secure = secure;
public Builder withHttps(final boolean secure) {
this.https = secure;
return this;
}

Expand All @@ -55,18 +56,25 @@ public Builder withBufferSize(final int bufferSize) {
return this;
}

public Builder withCertificateVerification(final boolean certificateVerification) {
this.certificateVerification = certificateVerification;
return this;
}

@Override
public ConnectionDetailsImpl build() {
return new ConnectionDetailsImpl(this);
}

}

private final String endpoint;
private final Credentials credentials;
private final boolean secure;
private final boolean https;
private final URI proxy;
private final int retries;
private final int bufferSize;
private final boolean certificateVerification;

static Builder builder(final String uriEndpoint, final Credentials credentials) {
return new Builder(uriEndpoint, credentials);
Expand All @@ -75,10 +83,11 @@ static Builder builder(final String uriEndpoint, final Credentials credentials)
private ConnectionDetailsImpl(final Builder builder) {
this.endpoint = builder.endpoint;
this.credentials = builder.credentials;
this.secure = builder.secure;
this.https = builder.https;
this.proxy = builder.proxy;
this.retries = builder.retries;
this.bufferSize = builder.bufferSize;
this.certificateVerification = builder.certificateVerification;
}

@Override
Expand All @@ -92,8 +101,8 @@ public Credentials getCredentials() {
}

@Override
public boolean isSecure() {
return secure;
public boolean isHttps() {
return https;
}

@Override
Expand All @@ -110,4 +119,10 @@ public int getRetries() {
public int getBufferSize() {
return bufferSize;
}

@Override
public boolean isCertificateVerification() {
return certificateVerification;
}

}
17 changes: 13 additions & 4 deletions sdk/src/main/java/com/spectralogic/ds3client/Ds3ClientBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ public class Ds3ClientBuilder implements Builder<Ds3Client> {
final private String endpoint;
final private Credentials credentials;

private boolean secure = true;
private boolean https = true;
private boolean certificateVerification = true;
private URI proxy = null;
private int retries = 5;
private int bufferSize = 1024 * 1024;
Expand Down Expand Up @@ -49,8 +50,8 @@ public static Ds3ClientBuilder create(final String endpoint, final Credentials c
* @param secure True will use HTTPS, false will use HTTP.
* @return The current builder.
*/
public Ds3ClientBuilder withHttpSecure(final boolean secure) {
this.secure = secure;
public Ds3ClientBuilder withHttps(final boolean secure) {
this.https = secure;
return this;
}

Expand All @@ -63,6 +64,14 @@ public Ds3ClientBuilder withBufferSize(final int bufferSize) {
return this;
}

/**
* Specifies if the library should perform SSL certificate validation.
*/
public Ds3ClientBuilder withCertificateVerification(final boolean certificateVerification) {
this.certificateVerification = certificateVerification;
return this;
}

/**
* Sets a HTTP proxy.
* @param proxy The endpoint of the HTTP proxy.
Expand Down Expand Up @@ -102,7 +111,7 @@ public Ds3ClientBuilder withRedirectRetries(final int retries) {
@Override
public Ds3Client build() {
final ConnectionDetailsImpl.Builder connBuilder = ConnectionDetailsImpl.builder(this.endpoint, this.credentials)
.withProxy(this.proxy).withSecure(this.secure).withRedirectRetries(this.retries).withBufferSize(this.bufferSize);
.withProxy(this.proxy).withHttps(this.https).withCertificateVerification(this.certificateVerification).withRedirectRetries(this.retries).withBufferSize(this.bufferSize);

final NetworkClient netClient = new NetworkClientImpl(connBuilder.build());
return new Ds3ClientImpl(netClient);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import com.spectralogic.ds3client.models.SignatureDetails;
import com.spectralogic.ds3client.networking.*;
import com.spectralogic.ds3client.utils.DateFormatter;
import com.spectralogic.ds3client.utils.SSLSetupException;
import com.spectralogic.ds3client.utils.Signature;

import org.apache.http.HttpHost;
Expand All @@ -28,17 +29,22 @@
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.ssl.*;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.message.BasicHttpRequest;

import javax.net.ssl.SSLContext;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.security.SignatureException;
import java.security.*;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;

class NetworkClientImpl implements NetworkClient {
Expand Down Expand Up @@ -100,7 +106,7 @@ public CloseableHttpResponse execute() throws IOException, SignatureException {

final HttpRequest httpRequest = this.buildHttpRequest();
this.addHeaders(httpRequest);
return HttpClients.createDefault().execute(this.host, httpRequest, this.getContext());
return getClient().execute(this.host, httpRequest, this.getContext());
}

private HttpHost buildHost() throws MalformedURLException {
Expand All @@ -109,16 +115,32 @@ private HttpHost buildHost() throws MalformedURLException {
return new HttpHost(proxyUri.getHost(), proxyUri.getPort(), proxyUri.getScheme());
} else {
final URL url = NetUtils.buildUrl(NetworkClientImpl.this.connectionDetails, "/");
return new HttpHost(url.getHost(), this.getPort(url), url.getProtocol());
return new HttpHost(url.getHost(), NetUtils.getPort(url), url.getProtocol());
}
}

private int getPort(final URL url) {
final int port = url.getPort();
if(port < 0) {
return 80;
private CloseableHttpClient getClient() {
if (NetworkClientImpl.this.getConnectionDetails().isHttps() && !NetworkClientImpl.this.getConnectionDetails().isCertificateVerification()) {
try {

final SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, new TrustStrategy() {
@Override
public boolean isTrusted(final X509Certificate[] chain, final String authType) throws CertificateException {
return true;
}
}).useTLS().build();

final SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new AllowAllHostnameVerifier());
return HttpClients.custom().setSSLSocketFactory(
sslsf).build();

} catch (final NoSuchAlgorithmException | KeyStoreException | KeyManagementException e) {
throw new SSLSetupException(e);
}
}
else {
return HttpClients.createDefault();
}
return port;
}

private HttpRequest buildHttpRequest() throws IOException {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ private static int getObjectCount(final Collection<Objects> chunks) {

private static AutoCloseableCache<String, WindowedChannelFactory> buildCache(
final ObjectChannelBuilder channelBuilder) {
return new AutoCloseableCache<String, WindowedChannelFactory>(
return new AutoCloseableCache<>(
new ValueBuilder<String, WindowedChannelFactory>() {
@Override
public WindowedChannelFactory get(final String key) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,7 @@ public void transfer(final ObjectChannelBuilder channelBuilder)
while (jobState.hasObjects()) {
transferNextChunks(chunkTransferrer);
}
} catch (final SignatureException | IOException | XmlProcessingException e) {
throw e;
} catch (final RuntimeException e) {
} catch (final RuntimeException | SignatureException | IOException | XmlProcessingException e) {
throw e;
} catch (final Exception e) {
throw new RuntimeException(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,7 @@ public void transfer(final ObjectChannelBuilder channelBuilder)
for (final Objects chunk : filteredChunks) {
chunkTransferrer.transferChunks(this.masterObjectList.getNodes(), Arrays.asList(filterChunk(allocateChunk(chunk))));
}
} catch (final SignatureException | IOException | XmlProcessingException e) {
throw e;
} catch (final RuntimeException e) {
} catch (final SignatureException | IOException | XmlProcessingException | RuntimeException e) {
throw e;
} catch (final Exception e) {
throw new RuntimeException(e);
Expand Down Expand Up @@ -88,6 +86,12 @@ private Objects tryAllocateChunk(final Objects filtered) throws IOException, Sig
}
}

/**
* Filters out chunks that have already been completed. We will get the same chunk name back from the server, but it
* will not have any objects in it, so we remove that from the list of objects that are returned.
* @param chunks The list to be filtered
* @return The filtered list
*/
private static List<Objects> filterChunks(final List<Objects> chunks) {
final List<Objects> filteredChunks = new ArrayList<>();
for (final Objects chunk : chunks) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,21 @@ public interface ConnectionDetails {

public Credentials getCredentials();

public boolean isSecure();
/**
* If true the network layer will use Https.
* @return
*/
public boolean isHttps();

public URI getProxy();

public int getRetries();

public int getBufferSize();

/**
* Returns true if the network layer should perform certificate authentication for SSL. False will disable
* certificate authentication.
*/
boolean isCertificateVerification();
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public static URL buildUrl(final ConnectionDetails connectionDetails, final Stri

public static URL buildUrl(final ConnectionDetails connectionDetails, final String path, final Map<String, String> params) throws MalformedURLException {
final StringBuilder builder = new StringBuilder();
builder.append(connectionDetails.isSecure()? "https": "http").append("://");
builder.append(connectionDetails.isHttps()? "https": "http").append("://");
builder.append(connectionDetails.getEndpoint());
if(!path.startsWith("/")) {
builder.append('/');
Expand Down Expand Up @@ -115,4 +115,17 @@ private static String bucketPath(final String bucket) {
public static String buildHostField(final ConnectionDetails details) {
return details.getEndpoint();
}

public static int getPort(final URL url) {
final int port = url.getPort();
if(port > 0) {
return port;
}

if (url.getProtocol().equals("https")) {
return 443;
}
return 80;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.spectralogic.ds3client.utils;

public class SSLSetupException extends RuntimeException {
public SSLSetupException(final Exception e) {
super(e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class BulkPutExample {
public static void main(final String args[]) throws IOException, SignatureException, XmlProcessingException {
final Ds3Client client = Ds3ClientBuilder.create("endpoint:8080",
new Credentials("accessId", "secretKey"))
.withHttpSecure(false)
.withHttps(false)
.build();

// Wrap the Ds3Client with the helper functions
Expand Down
Loading

0 comments on commit 1cc175c

Please sign in to comment.