Skip to content

Commit 442c146

Browse files
author
oleksiys
committed
[master] + rework the fix for the issue #525
#525 "Move the hostname verification to after the SSL handshake has completed"
1 parent 226995d commit 442c146

File tree

3 files changed

+84
-58
lines changed

3 files changed

+84
-58
lines changed

providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/ConnectionManager.java

Lines changed: 19 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013 Sonatype, Inc. All rights reserved.
2+
* Copyright (c) 2013-2014 Sonatype, Inc. All rights reserved.
33
*
44
* This program is licensed to you under the Apache License Version 2.0,
55
* and you may not use this file except in compliance with the Apache License Version 2.0.
@@ -21,7 +21,6 @@
2121
import org.asynchttpclient.ProxyServer;
2222
import org.asynchttpclient.Request;
2323
import org.asynchttpclient.uri.UriComponents;
24-
import org.asynchttpclient.util.Base64;
2524
import org.glassfish.grizzly.CompletionHandler;
2625
import org.glassfish.grizzly.Connection;
2726
import org.glassfish.grizzly.Grizzly;
@@ -30,19 +29,14 @@
3029
import org.glassfish.grizzly.connectionpool.EndpointKey;
3130
import org.glassfish.grizzly.filterchain.FilterChainBuilder;
3231
import org.glassfish.grizzly.impl.FutureImpl;
33-
import org.glassfish.grizzly.ssl.SSLBaseFilter;
34-
import org.glassfish.grizzly.ssl.SSLFilter;
35-
import org.glassfish.grizzly.ssl.SSLUtils;
3632
import org.glassfish.grizzly.utils.Futures;
3733
import org.glassfish.grizzly.utils.IdleTimeoutFilter;
3834
import org.slf4j.Logger;
3935
import org.slf4j.LoggerFactory;
4036

4137
import javax.net.ssl.HostnameVerifier;
42-
import javax.net.ssl.SSLSession;
4338

4439
import java.io.IOException;
45-
import java.net.ConnectException;
4640
import java.net.InetAddress;
4741
import java.net.InetSocketAddress;
4842
import java.net.SocketAddress;
@@ -52,6 +46,7 @@
5246
import java.util.concurrent.CancellationException;
5347
import java.util.concurrent.ExecutionException;
5448
import java.util.concurrent.TimeoutException;
49+
import org.asynchttpclient.providers.grizzly.filters.SwitchingSSLFilter;
5550

5651
public class ConnectionManager {
5752

@@ -66,15 +61,13 @@ public class ConnectionManager {
6661
private final FilterChainBuilder secureBuilder;
6762
private final FilterChainBuilder nonSecureBuilder;
6863
private final boolean asyncConnect;
69-
private final SSLFilter sslFilter;
7064

7165
// ------------------------------------------------------------ Constructors
7266

7367
ConnectionManager(final GrizzlyAsyncHttpProvider provider,//
7468
final ConnectionPool connectionPool,//
7569
final FilterChainBuilder secureBuilder,//
76-
final FilterChainBuilder nonSecureBuilder,//
77-
final SSLFilter sslFilter) {
70+
final FilterChainBuilder nonSecureBuilder) {
7871

7972
this.provider = provider;
8073
final AsyncHttpClientConfig config = provider.getClientConfig();
@@ -95,37 +88,45 @@ public class ConnectionManager {
9588
AsyncHttpProviderConfig<?, ?> providerConfig = config.getAsyncHttpProviderConfig();
9689
asyncConnect = providerConfig instanceof GrizzlyAsyncHttpProviderConfig ? GrizzlyAsyncHttpProviderConfig.class.cast(providerConfig)
9790
.isAsyncConnectMode() : false;
98-
this.sslFilter = sslFilter;
9991
}
10092

10193
// ---------------------------------------------------------- Public Methods
10294

10395
public void doTrackedConnection(final Request request,//
10496
final GrizzlyResponseFuture requestFuture,//
105-
final CompletionHandler<Connection> connectHandler) throws IOException {
97+
CompletionHandler<Connection> completionHandler) throws IOException {
10698
final EndpointKey<SocketAddress> key = getEndPointKey(request, requestFuture.getProxyServer());
107-
CompletionHandler<Connection> handler = wrapHandler(request, getVerifier(), connectHandler, sslFilter);
99+
100+
final HostnameVerifier verifier = getVerifier();
101+
final UriComponents uri = request.getURI();
102+
103+
if (Utils.isSecure(uri) && verifier != null) {
104+
completionHandler =
105+
SwitchingSSLFilter.wrapWithHostnameVerifierHandler(
106+
completionHandler, verifier, uri.getHost());
107+
}
108+
108109
if (asyncConnect) {
109-
connectionPool.take(key, handler);
110+
connectionPool.take(key, completionHandler);
110111
} else {
111112
IOException ioe = null;
112113
GrizzlyFuture<Connection> future = connectionPool.take(key);
113114
try {
114115
// No explicit timeout when calling get() here as the Grizzly
115116
// endpoint pool will time it out based on the connect timeout
116117
// setting.
117-
handler.completed(future.get());
118+
completionHandler.completed(future.get());
118119
} catch (CancellationException e) {
119-
handler.cancelled();
120+
completionHandler.cancelled();
120121
} catch (ExecutionException ee) {
121122
final Throwable cause = ee.getCause();
122123
if (cause instanceof ConnectionPool.MaxCapacityException) {
123124
ioe = (IOException) cause;
124125
} else {
125-
handler.failed(ee.getCause());
126+
completionHandler.failed(ee.getCause());
126127
}
127128
} catch (Exception ie) {
128-
handler.failed(ie);
129+
completionHandler.failed(ie);
129130
}
130131
if (ioe != null) {
131132
throw ioe;
@@ -144,37 +145,6 @@ public Connection obtainConnection(final Request request, final GrizzlyResponseF
144145

145146
// --------------------------------------------------Package Private Methods
146147

147-
static CompletionHandler<Connection> wrapHandler(final Request request, final HostnameVerifier verifier,
148-
final CompletionHandler<Connection> delegate, final SSLFilter sslFilter) {
149-
final UriComponents uri = request.getURI();
150-
if (Utils.isSecure(uri) && verifier != null) {
151-
SSLBaseFilter.HandshakeListener handshakeListener = new SSLBaseFilter.HandshakeListener() {
152-
@Override
153-
public void onStart(Connection connection) {
154-
// do nothing
155-
LOGGER.debug("SSL Handshake onStart: ");
156-
}
157-
158-
@Override
159-
public void onComplete(Connection connection) {
160-
sslFilter.removeHandshakeListener(this);
161-
162-
final String host = uri.getHost();
163-
final SSLSession session = SSLUtils.getSSLEngine(connection).getSession();
164-
LOGGER.debug("SSL Handshake onComplete: session = {}, id = {}, isValid = {}, host = {}", session.toString(), Base64.encode(session.getId()), session.isValid(), host);
165-
166-
if (!verifier.verify(host, session)) {
167-
connection.close(); // XXX what's the correct way to kill a connection?
168-
IOException e = new ConnectException("Host name verification failed for host " + host);
169-
delegate.failed(e);
170-
}
171-
}
172-
};
173-
sslFilter.addHandshakeListener(handshakeListener);
174-
}
175-
return delegate;
176-
}
177-
178148
static void markConnectionAsDoNotCache(final Connection c) {
179149
DO_NOT_CACHE.set(c, Boolean.TRUE);
180150
}

providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/GrizzlyAsyncHttpProvider.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,9 @@ public void onTimeout(Connection connection) {
259259
}
260260
}
261261
final SSLEngineConfigurator configurator = new SSLEngineConfigurator(context, true, false, false);
262-
final SwitchingSSLFilter filter = new SwitchingSSLFilter(configurator);
263-
secure.add(filter);
262+
final SwitchingSSLFilter sslFilter = new SwitchingSSLFilter(configurator);
263+
secure.add(sslFilter);
264+
264265
GrizzlyAsyncHttpProviderConfig providerConfig = (GrizzlyAsyncHttpProviderConfig) clientConfig.getAsyncHttpProviderConfig();
265266

266267
boolean npnEnabled = NextProtoNegSupport.isEnabled();
@@ -335,7 +336,7 @@ public void onTimeout(Connection connection) {
335336
} else {
336337
pool = null;
337338
}
338-
connectionManager = new ConnectionManager(this, pool, secure, nonSecure, filter);
339+
connectionManager = new ConnectionManager(this, pool, secure, nonSecure);
339340

340341
}
341342

providers/grizzly/src/main/java/org/asynchttpclient/providers/grizzly/filters/SwitchingSSLFilter.java

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013 Sonatype, Inc. All rights reserved.
2+
* Copyright (c) 2013-2014 Sonatype, Inc. All rights reserved.
33
*
44
* This program is licensed to you under the Apache License Version 2.0,
55
* and you may not use this file except in compliance with the Apache License Version 2.0.
@@ -13,7 +13,15 @@
1313

1414
package org.asynchttpclient.providers.grizzly.filters;
1515

16+
import java.io.IOException;
17+
import java.net.ConnectException;
18+
import javax.net.ssl.HostnameVerifier;
19+
import javax.net.ssl.SSLEngine;
20+
import javax.net.ssl.SSLHandshakeException;
21+
import javax.net.ssl.SSLSession;
1622
import org.asynchttpclient.providers.grizzly.filters.events.SSLSwitchingEvent;
23+
import org.asynchttpclient.util.Base64;
24+
import org.glassfish.grizzly.CompletionHandler;
1725
import org.glassfish.grizzly.Connection;
1826
import org.glassfish.grizzly.EmptyCompletionHandler;
1927
import org.glassfish.grizzly.Grizzly;
@@ -25,11 +33,9 @@
2533
import org.glassfish.grizzly.filterchain.NextAction;
2634
import org.glassfish.grizzly.ssl.SSLEngineConfigurator;
2735
import org.glassfish.grizzly.ssl.SSLFilter;
28-
29-
import javax.net.ssl.SSLEngine;
30-
import javax.net.ssl.SSLHandshakeException;
31-
32-
import java.io.IOException;
36+
import org.glassfish.grizzly.ssl.SSLUtils;
37+
import org.slf4j.Logger;
38+
import org.slf4j.LoggerFactory;
3339

3440
/**
3541
* SSL Filter that may be present within the FilterChain and may be
@@ -45,6 +51,8 @@ public final class SwitchingSSLFilter extends SSLFilter {
4551
private static final Attribute<Throwable> HANDSHAKE_ERROR = Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute(SwitchingSSLFilter.class
4652
.getName() + "-HANDSHAKE-ERROR");
4753

54+
private final static Logger LOGGER = LoggerFactory.getLogger(SwitchingSSLFilter.class);
55+
4856
// ------------------------------------------------------------ Constructors
4957

5058
public SwitchingSSLFilter(final SSLEngineConfigurator clientConfig) {
@@ -161,4 +169,51 @@ private static void setError(final Connection c, Throwable t) {
161169
private static void enableRead(final Connection c) throws IOException {
162170
c.enableIOEvent(IOEvent.READ);
163171
}
172+
173+
// ================= HostnameVerifier section ========================
174+
175+
public static CompletionHandler<Connection> wrapWithHostnameVerifierHandler(
176+
final CompletionHandler<Connection> delegateCompletionHandler,
177+
final HostnameVerifier verifier, final String host) {
178+
179+
return new CompletionHandler<Connection>() {
180+
181+
public void cancelled() {
182+
if (delegateCompletionHandler != null) {
183+
delegateCompletionHandler.cancelled();
184+
}
185+
}
186+
187+
public void failed(final Throwable throwable) {
188+
if (delegateCompletionHandler != null) {
189+
delegateCompletionHandler.failed(throwable);
190+
}
191+
}
192+
193+
public void completed(final Connection connection) {
194+
final SSLSession session = SSLUtils.getSSLEngine(connection).getSession();
195+
if (LOGGER.isDebugEnabled()) {
196+
LOGGER.debug("SSL Handshake onComplete: session = {}, id = {}, isValid = {}, host = {}",
197+
session.toString(), Base64.encode(session.getId()), session.isValid(), host);
198+
}
199+
200+
if (!verifier.verify(host, session)) {
201+
connection.terminateSilently();
202+
203+
if (delegateCompletionHandler != null) {
204+
IOException e = new ConnectException("Host name verification failed for host " + host);
205+
delegateCompletionHandler.failed(e);
206+
}
207+
} else if (delegateCompletionHandler != null) {
208+
delegateCompletionHandler.completed(connection);
209+
}
210+
}
211+
212+
public void updated(final Connection connection) {
213+
if (delegateCompletionHandler != null) {
214+
delegateCompletionHandler.updated(connection);
215+
}
216+
}
217+
};
218+
}
164219
}

0 commit comments

Comments
 (0)