Skip to content

Commit

Permalink
Move to Netty SslContext API, close #1023
Browse files Browse the repository at this point in the history
  • Loading branch information
slandelle committed Oct 28, 2015
1 parent 5c937e6 commit b661e0e
Show file tree
Hide file tree
Showing 10 changed files with 203 additions and 55 deletions.
Expand Up @@ -11,7 +11,6 @@
import java.util.Map;
import java.util.concurrent.ThreadFactory;

import org.asynchttpclient.channel.SSLEngineFactory;
import org.asynchttpclient.channel.pool.KeepAliveStrategy;
import org.asynchttpclient.filter.IOExceptionFilter;
import org.asynchttpclient.filter.RequestFilter;
Expand Down Expand Up @@ -249,7 +248,7 @@ public interface AsyncHttpClientConfig {

int getHandshakeTimeout();

SSLEngineFactory getSslEngineFactory();
SslEngineFactory getSslEngineFactory();

int getChunkedFileChunkSize();

Expand Down
Expand Up @@ -31,7 +31,6 @@
import java.util.Properties;
import java.util.concurrent.ThreadFactory;

import org.asynchttpclient.channel.SSLEngineFactory;
import org.asynchttpclient.channel.pool.KeepAliveStrategy;
import org.asynchttpclient.filter.IOExceptionFilter;
import org.asynchttpclient.filter.RequestFilter;
Expand Down Expand Up @@ -100,7 +99,7 @@ public class DefaultAsyncHttpClientConfig implements AsyncHttpClientConfig {
private final int sslSessionCacheSize;
private final int sslSessionTimeout;
private final SslContext sslContext;
private final SSLEngineFactory sslEngineFactory;
private final SslEngineFactory sslEngineFactory;

// filters
private final List<RequestFilter> requestFilters;
Expand Down Expand Up @@ -163,7 +162,7 @@ private DefaultAsyncHttpClientConfig(//
int sslSessionCacheSize,//
int sslSessionTimeout,//
SslContext sslContext,//
SSLEngineFactory sslEngineFactory,//
SslEngineFactory sslEngineFactory,//

// filters
List<RequestFilter> requestFilters,//
Expand Down Expand Up @@ -416,7 +415,7 @@ public SslContext getSslContext() {
}

@Override
public SSLEngineFactory getSslEngineFactory() {
public SslEngineFactory getSslEngineFactory() {
return sslEngineFactory;
}

Expand Down Expand Up @@ -557,7 +556,7 @@ public static class Builder {
private int sslSessionCacheSize = defaultSslSessionCacheSize();
private int sslSessionTimeout = defaultSslSessionTimeout();
private SslContext sslContext;
private SSLEngineFactory sslEngineFactory;
private SslEngineFactory sslEngineFactory;

// filters
private final List<RequestFilter> requestFilters = new LinkedList<>();
Expand Down Expand Up @@ -831,7 +830,7 @@ public Builder setSslContext(final SslContext sslContext) {
return this;
}

public Builder setSslEngineFactory(SSLEngineFactory sslEngineFactory) {
public Builder setSslEngineFactory(SslEngineFactory sslEngineFactory) {
this.sslEngineFactory = sslEngineFactory;
return this;
}
Expand Down
29 changes: 29 additions & 0 deletions client/src/main/java/org/asynchttpclient/SslEngineFactory.java
@@ -0,0 +1,29 @@
/*
* Copyright (c) 2014 AsyncHttpClient Project. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package org.asynchttpclient;

import javax.net.ssl.SSLEngine;

public interface SslEngineFactory {

/**
* Creates new {@link SSLEngine}.
*
* @param config the client config
* @param peerHost the peer hostname
* @param peerPort the peer port
* @return new engine
*/
SSLEngine newSslEngine(AsyncHttpClientConfig config, String peerHost, int peerPort);
}
Expand Up @@ -49,7 +49,7 @@

import org.asynchttpclient.AsyncHandler;
import org.asynchttpclient.AsyncHttpClientConfig;
import org.asynchttpclient.channel.SSLEngineFactory;
import org.asynchttpclient.SslEngineFactory;
import org.asynchttpclient.channel.pool.ConnectionPoolPartitioning;
import org.asynchttpclient.handler.AsyncHandlerExtensions;
import org.asynchttpclient.netty.Callback;
Expand All @@ -61,6 +61,7 @@
import org.asynchttpclient.netty.handler.Processor;
import org.asynchttpclient.netty.handler.WebSocketProtocol;
import org.asynchttpclient.netty.request.NettyRequestSender;
import org.asynchttpclient.netty.ssl.DefaultSslEngineFactory;
import org.asynchttpclient.proxy.ProxyServer;
import org.asynchttpclient.uri.Uri;
import org.slf4j.Logger;
Expand All @@ -81,7 +82,7 @@ public class ChannelManager {
public static final String WS_ENCODER_HANDLER = "ws-encoder";

private final AsyncHttpClientConfig config;
private final SSLEngineFactory sslEngineFactory;
private final SslEngineFactory sslEngineFactory;
private final EventLoopGroup eventLoopGroup;
private final boolean allowReleaseEventLoopGroup;
private final Class<? extends Channel> socketChannelClass;
Expand All @@ -107,7 +108,7 @@ public ChannelManager(final AsyncHttpClientConfig config, Timer nettyTimer) {

this.config = config;
try {
this.sslEngineFactory = config.getSslEngineFactory() != null ? config.getSslEngineFactory() : new SSLEngineFactory.DefaultSSLEngineFactory(config);
this.sslEngineFactory = config.getSslEngineFactory() != null ? config.getSslEngineFactory() : new DefaultSslEngineFactory(config);
} catch (SSLException e) {
throw new ExceptionInInitializerError(e);
}
Expand Down Expand Up @@ -392,7 +393,7 @@ private HttpClientCodec newHttpClientCodec() {
}

private SslHandler createSslHandler(String peerHost, int peerPort) {
SSLEngine sslEngine = sslEngineFactory.newSSLEngine(peerHost, peerPort);
SSLEngine sslEngine = sslEngineFactory.newSslEngine(config, peerHost, peerPort);
SslHandler sslHandler = new SslHandler(sslEngine);
if (handshakeTimeout > 0)
sslHandler.setHandshakeTimeoutMillis(handshakeTimeout);
Expand Down
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2015 AsyncHttpClient Project. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package org.asynchttpclient.netty.ssl;

import io.netty.buffer.ByteBufAllocator;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;

import org.asynchttpclient.AsyncHttpClientConfig;

public class DefaultSslEngineFactory extends SslEngineFactoryBase {

private final SslContext sslContext;

public DefaultSslEngineFactory(AsyncHttpClientConfig config) throws SSLException {
this.sslContext = getSslContext(config);
}

private SslContext getSslContext(AsyncHttpClientConfig config) throws SSLException {
if (config.getSslContext() != null)
return config.getSslContext();

SslContextBuilder sslContextBuilder = SslContextBuilder.forClient()//
.sslProvider(config.isUseOpenSsl() ? SslProvider.OPENSSL : SslProvider.JDK)//
.sessionCacheSize(config.getSslSessionCacheSize())//
.sessionTimeout(config.getSslSessionTimeout());

if (config.isAcceptAnyCertificate())
sslContextBuilder.trustManager(InsecureTrustManagerFactory.INSTANCE);

return sslContextBuilder.build();
}

@Override
public SSLEngine newSslEngine(AsyncHttpClientConfig config, String peerHost, int peerPort) {
// FIXME should be using ctx allocator
SSLEngine sslEngine = sslContext.newEngine(ByteBufAllocator.DEFAULT, peerHost, peerPort);
configureSslEngine(sslEngine, config);
return sslEngine;
}
}
@@ -0,0 +1,36 @@
/*
* Copyright (c) 2015 AsyncHttpClient Project. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package org.asynchttpclient.netty.ssl;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;

import org.asynchttpclient.AsyncHttpClientConfig;

public class JsseSslEngineFactory extends SslEngineFactoryBase {

private final SSLContext sslContext;

public JsseSslEngineFactory(SSLContext sslContext) {
this.sslContext = sslContext;
}

@Override
public SSLEngine newSslEngine(AsyncHttpClientConfig config, String peerHost, int peerPort) {
SSLEngine sslEngine = sslContext.createSSLEngine(peerHost, peerPort);
configureSslEngine(sslEngine, config);
return sslEngine;
}

}
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2015 AsyncHttpClient Project. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/
package org.asynchttpclient.netty.ssl;

import static org.asynchttpclient.util.MiscUtils.isNonEmpty;

import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLParameters;

import org.asynchttpclient.AsyncHttpClientConfig;
import org.asynchttpclient.SslEngineFactory;

public abstract class SslEngineFactoryBase implements SslEngineFactory {

protected void configureSslEngine(SSLEngine sslEngine, AsyncHttpClientConfig config) {
sslEngine.setUseClientMode(true);
if (!config.isAcceptAnyCertificate()) {
SSLParameters params = sslEngine.getSSLParameters();
params.setEndpointIdentificationAlgorithm("HTTPS");
sslEngine.setSSLParameters(params);
}

if (isNonEmpty(config.getEnabledProtocols()))
sslEngine.setEnabledProtocols(config.getEnabledProtocols());

if (isNonEmpty(config.getEnabledCipherSuites()))
sslEngine.setEnabledCipherSuites(config.getEnabledCipherSuites());
}
}
10 changes: 5 additions & 5 deletions client/src/test/java/org/asynchttpclient/BasicHttpsTest.java
Expand Up @@ -42,7 +42,7 @@ protected String getTargetUrl() {
@Test(groups = "standalone")
public void zeroCopyPostTest() throws Exception {

try (AsyncHttpClient client = asyncHttpClient(config().setSslContext(createSslContext(new AtomicBoolean(true))))) {
try (AsyncHttpClient client = asyncHttpClient(config().setSslEngineFactory(createSslEngineFactory(new AtomicBoolean(true))))) {
Response resp = client.preparePost(getTargetUrl()).setBody(SIMPLE_TEXT_FILE).setHeader("Content-Type", "text/html").execute().get();
assertNotNull(resp);
assertEquals(resp.getStatusCode(), HttpServletResponse.SC_OK);
Expand All @@ -52,7 +52,7 @@ public void zeroCopyPostTest() throws Exception {

@Test(groups = "standalone")
public void multipleSSLRequestsTest() throws Exception {
try (AsyncHttpClient c = asyncHttpClient(config().setSslContext(createSslContext(new AtomicBoolean(true))))) {
try (AsyncHttpClient c = asyncHttpClient(config().setSslEngineFactory(createSslEngineFactory(new AtomicBoolean(true))))) {
String body = "hello there";

// once
Expand All @@ -78,7 +78,7 @@ public boolean keepAlive(Request ahcRequest, HttpRequest nettyRequest, HttpRespo
}
};

try (AsyncHttpClient c = asyncHttpClient(config().setSslContext(createSslContext(new AtomicBoolean(true))).setKeepAliveStrategy(keepAliveStrategy))) {
try (AsyncHttpClient c = asyncHttpClient(config().setSslEngineFactory(createSslEngineFactory(new AtomicBoolean(true))).setKeepAliveStrategy(keepAliveStrategy))) {
String body = "hello there";
c.preparePost(getTargetUrl()).setBody(body).setHeader("Content-Type", "text/html").execute();

Expand All @@ -94,7 +94,7 @@ public boolean keepAlive(Request ahcRequest, HttpRequest nettyRequest, HttpRespo
public void reconnectsAfterFailedCertificationPath() throws Exception {

AtomicBoolean trust = new AtomicBoolean(false);
try (AsyncHttpClient client = asyncHttpClient(config().setSslEngineFactory(createSSLEngineFactory(trust)))) {
try (AsyncHttpClient client = asyncHttpClient(config().setSslEngineFactory(createSslEngineFactory(trust)))) {
String body = "hello there";

// first request fails because server certificate is rejected
Expand Down Expand Up @@ -128,7 +128,7 @@ public void failInstantlyIfNotAllowedSelfSignedCertificate() throws Throwable {

@Test(groups = "standalone")
public void testNormalEventsFired() throws Exception {
try (AsyncHttpClient client = asyncHttpClient(config().setSslContext(createSslContext(new AtomicBoolean(true))))) {
try (AsyncHttpClient client = asyncHttpClient(config().setSslEngineFactory(createSslEngineFactory(new AtomicBoolean(true))))) {
EventCollectingHandler handler = new EventCollectingHandler();
client.preparePost(getTargetUrl()).setBody("whatever").execute(handler).get(3, TimeUnit.SECONDS);
handler.waitForCompletion(3, TimeUnit.SECONDS);
Expand Down

0 comments on commit b661e0e

Please sign in to comment.