Skip to content

Commit cc91f87

Browse files
committed
Add HTTPS proxy support and fix broken test.
1 parent c959fa0 commit cc91f87

File tree

7 files changed

+69
-36
lines changed

7 files changed

+69
-36
lines changed

client/src/main/java/org/asynchttpclient/channel/ChannelPoolPartitioning.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public Object getPartitionKey(Uri uri, String virtualHost, ProxyServer proxyServ
4242
targetHostBaseUrl,
4343
virtualHost,
4444
proxyServer.getHost(),
45-
uri.isSecured() && proxyServer.getProxyType() == ProxyType.HTTP ?
45+
uri.isSecured() && proxyServer.getProxyType().isHttp() ?
4646
proxyServer.getSecuredPort() :
4747
proxyServer.getPort(),
4848
proxyServer.getProxyType());

client/src/main/java/org/asynchttpclient/netty/channel/ChannelManager.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.asynchttpclient.netty.request.NettyRequestSender;
5252
import org.asynchttpclient.netty.ssl.DefaultSslEngineFactory;
5353
import org.asynchttpclient.proxy.ProxyServer;
54+
import org.asynchttpclient.proxy.ProxyType;
5455
import org.asynchttpclient.uri.Uri;
5556
import org.slf4j.Logger;
5657
import org.slf4j.LoggerFactory;
@@ -71,6 +72,7 @@ public class ChannelManager {
7172

7273
public static final String HTTP_CLIENT_CODEC = "http";
7374
public static final String SSL_HANDLER = "ssl";
75+
public static final String SSL_TUNNEL_HANDLER = "ssl-tunnel";
7476
public static final String SOCKS_HANDLER = "socks";
7577
public static final String INFLATER_HANDLER = "inflater";
7678
public static final String CHUNKED_WRITER_HANDLER = "chunked-writer";
@@ -156,6 +158,10 @@ public ChannelManager(final AsyncHttpClientConfig config, Timer nettyTimer) {
156158
public static boolean isSslHandlerConfigured(ChannelPipeline pipeline) {
157159
return pipeline.get(SSL_HANDLER) != null;
158160
}
161+
162+
public static boolean isSslTunnelHandlerConfigured(ChannelPipeline pipeline) {
163+
return pipeline.get(SSL_TUNNEL_HANDLER) != null;
164+
}
159165

160166
private Bootstrap newBootstrap(ChannelFactory<? extends Channel> channelFactory, EventLoopGroup eventLoopGroup, AsyncHttpClientConfig config) {
161167
@SuppressWarnings("deprecation")
@@ -340,7 +346,7 @@ private SslHandler createSslHandler(String peerHost, int peerPort) {
340346
return sslHandler;
341347
}
342348

343-
public Future<Channel> updatePipelineForHttpTunneling(ChannelPipeline pipeline, Uri requestUri) {
349+
public Future<Channel> updatePipelineForHttpTunneling(ChannelPipeline pipeline, Uri requestUri, ProxyType proxyType) {
344350

345351
Future<Channel> whenHandshaked = null;
346352

@@ -354,6 +360,11 @@ public Future<Channel> updatePipelineForHttpTunneling(ChannelPipeline pipeline,
354360
pipeline.addBefore(INFLATER_HANDLER, SSL_HANDLER, sslHandler);
355361
}
356362
pipeline.addAfter(SSL_HANDLER, HTTP_CLIENT_CODEC, newHttpClientCodec());
363+
if(ProxyType.HTTPS.equals(proxyType) && !isSslTunnelHandlerConfigured(pipeline)) {
364+
SslHandler sslHandler = createSslHandler(requestUri.getHost(), requestUri.getExplicitPort());
365+
whenHandshaked = sslHandler.handshakeFuture();
366+
pipeline.addAfter(SSL_HANDLER, SSL_TUNNEL_HANDLER, sslHandler);
367+
}
357368

358369
} else {
359370
pipeline.addBefore(AHC_HTTP_HANDLER, HTTP_CLIENT_CODEC, newHttpClientCodec());

client/src/main/java/org/asynchttpclient/netty/channel/NettyConnectListener.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.asynchttpclient.netty.request.NettyRequestSender;
2525
import org.asynchttpclient.netty.timeout.TimeoutsHolder;
2626
import org.asynchttpclient.proxy.ProxyServer;
27+
import org.asynchttpclient.proxy.ProxyType;
2728
import org.asynchttpclient.uri.Uri;
2829
import org.slf4j.Logger;
2930
import org.slf4j.LoggerFactory;
@@ -107,10 +108,10 @@ public void onSuccess(Channel channel, InetSocketAddress remoteAddress) {
107108
ProxyServer proxyServer = future.getProxyServer();
108109

109110
// in case of proxy tunneling, we'll add the SslHandler later, after the CONNECT request
110-
if ((proxyServer == null || proxyServer.getProxyType().isSocks()) && uri.isSecured()) {
111+
if ((proxyServer == null || ProxyType.HTTPS.equals(proxyServer.getProxyType()) || proxyServer.getProxyType().isSocks()) && uri.isSecured()) {
111112
SslHandler sslHandler;
112113
try {
113-
sslHandler = channelManager.addSslHandler(channel.pipeline(), uri, request.getVirtualHost(), proxyServer != null);
114+
sslHandler = channelManager.addSslHandler(channel.pipeline(), uri, request.getVirtualHost(), proxyServer != null && proxyServer.getProxyType().isSocks());
114115
} catch (Exception sslError) {
115116
onFailure(channel, sslError);
116117
return;

client/src/main/java/org/asynchttpclient/netty/handler/intercept/ConnectSuccessInterceptor.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ public boolean exitAfterHandlingConnect(Channel channel,
4848
Uri requestUri = request.getUri();
4949
LOGGER.debug("Connecting to proxy {} for scheme {}", proxyServer, requestUri.getScheme());
5050

51-
Future<Channel> whenHandshaked = channelManager.updatePipelineForHttpTunneling(channel.pipeline(), requestUri);
51+
Future<Channel> whenHandshaked = channelManager.updatePipelineForHttpTunneling(channel.pipeline(), requestUri, proxyServer.getProxyType());
5252

5353
future.setReuseChannel(true);
5454
future.setConnectAllowed(false);

client/src/main/java/org/asynchttpclient/proxy/ProxyType.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
package org.asynchttpclient.proxy;
1515

1616
public enum ProxyType {
17-
HTTP(true), SOCKS_V4(false), SOCKS_V5(false);
17+
HTTP(true), HTTPS(true), SOCKS_V4(false), SOCKS_V5(false);
1818

1919
private final boolean http;
2020

client/src/test/java/org/asynchttpclient/AsyncStreamHandlerTest.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ public void asyncOptionsTest() throws Throwable {
442442
// FIXME: Actually refactor this test to account for both cases
443443
final String[] expected = {"GET", "HEAD", "OPTIONS", "POST"};
444444
final String[] expectedWithTrace = {"GET", "HEAD", "OPTIONS", "POST", "TRACE"};
445-
Future<String> f = client.prepareOptions("http://www.apache.org/").execute(new AsyncHandlerAdapter() {
445+
Future<String> f = client.prepareOptions("https://www.apache.org/").execute(new AsyncHandlerAdapter() {
446446

447447
@Override
448448
public State onHeadersReceived(HttpHeaders headers) {

client/src/test/java/org/asynchttpclient/proxy/HttpsProxyTest.java

+50-29
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,19 @@
1212
*/
1313
package org.asynchttpclient.proxy;
1414

15+
import java.util.ArrayList;
16+
import java.util.List;
1517
import org.asynchttpclient.*;
1618
import org.asynchttpclient.request.body.generator.ByteArrayBodyGenerator;
1719
import org.asynchttpclient.test.EchoHandler;
1820
import org.eclipse.jetty.proxy.ConnectHandler;
21+
import org.eclipse.jetty.server.Handler;
1922
import org.eclipse.jetty.server.Server;
2023
import org.eclipse.jetty.server.ServerConnector;
2124
import org.eclipse.jetty.server.handler.AbstractHandler;
2225
import org.testng.annotations.AfterClass;
2326
import org.testng.annotations.BeforeClass;
27+
import org.testng.annotations.DataProvider;
2428
import org.testng.annotations.Test;
2529

2630
import static org.asynchttpclient.Dsl.*;
@@ -34,50 +38,67 @@
3438
*/
3539
public class HttpsProxyTest extends AbstractBasicTest {
3640

37-
private Server server2;
41+
private List<Server> servers;
42+
private int httpsProxyPort;
3843

3944
public AbstractHandler configureHandler() throws Exception {
4045
return new ConnectHandler();
4146
}
47+
48+
@DataProvider (name = "serverPorts")
49+
public Object[][] serverPorts() {
50+
return new Object[][] {{port1, ProxyType.HTTP}, {httpsProxyPort, ProxyType.HTTPS}};
51+
}
52+
4253

4354
@BeforeClass(alwaysRun = true)
4455
public void setUpGlobal() throws Exception {
45-
server = new Server();
46-
ServerConnector connector = addHttpConnector(server);
47-
server.setHandler(configureHandler());
48-
server.start();
49-
port1 = connector.getLocalPort();
56+
servers = new ArrayList<>();
57+
port1 = startServer(configureHandler(), false);
58+
59+
port2 = startServer(new EchoHandler(), true);
5060

51-
server2 = new Server();
52-
ServerConnector connector2 = addHttpsConnector(server2);
53-
server2.setHandler(new EchoHandler());
54-
server2.start();
55-
port2 = connector2.getLocalPort();
61+
httpsProxyPort = startServer(configureHandler(), true);
5662

5763
logger.info("Local HTTP server started successfully");
5864
}
65+
66+
private int startServer(Handler handler, boolean secure) throws Exception {
67+
Server server = new Server();
68+
@SuppressWarnings("resource")
69+
ServerConnector connector = secure ? addHttpsConnector(server) : addHttpConnector(server);
70+
server.setHandler(handler);
71+
server.start();
72+
servers.add(server);
73+
return connector.getLocalPort();
74+
}
5975

6076
@AfterClass(alwaysRun = true)
61-
public void tearDownGlobal() throws Exception {
62-
server.stop();
63-
server2.stop();
77+
public void tearDownGlobal() {
78+
servers.forEach(t -> {
79+
try {
80+
t.stop();
81+
} catch (Exception e) {
82+
// couldn't stop server
83+
}
84+
});
6485
}
6586

66-
@Test
67-
public void testRequestProxy() throws Exception {
87+
@Test(dataProvider = "serverPorts")
88+
public void testRequestProxy(int proxyPort, ProxyType type) throws Exception {
6889

6990
try (AsyncHttpClient asyncHttpClient = asyncHttpClient(config().setFollowRedirect(true).setUseInsecureTrustManager(true))) {
70-
RequestBuilder rb = get(getTargetUrl2()).setProxyServer(proxyServer("localhost", port1));
91+
RequestBuilder rb = get(getTargetUrl2()).setProxyServer(proxyServer("localhost", proxyPort).setProxyType(type));
7192
Response r = asyncHttpClient.executeRequest(rb.build()).get();
7293
assertEquals(r.getStatusCode(), 200);
7394
}
7495
}
7596

76-
@Test
77-
public void testConfigProxy() throws Exception {
97+
@Test(dataProvider = "serverPorts")
98+
public void testConfigProxy(int proxyPort, ProxyType type) throws Exception {
7899
AsyncHttpClientConfig config = config()
79100
.setFollowRedirect(true)
80-
.setProxyServer(proxyServer("localhost", port1).build())
101+
.setProxyServer(proxyServer("localhost", proxyPort).setProxyType(type).build())
81102
.setUseInsecureTrustManager(true)
82103
.build();
83104
try (AsyncHttpClient asyncHttpClient = asyncHttpClient(config)) {
@@ -86,11 +107,11 @@ public void testConfigProxy() throws Exception {
86107
}
87108
}
88109

89-
@Test
90-
public void testNoDirectRequestBodyWithProxy() throws Exception {
110+
@Test(dataProvider = "serverPorts")
111+
public void testNoDirectRequestBodyWithProxy(int proxyPort, ProxyType type) throws Exception {
91112
AsyncHttpClientConfig config = config()
92113
.setFollowRedirect(true)
93-
.setProxyServer(proxyServer("localhost", port1).build())
114+
.setProxyServer(proxyServer("localhost", proxyPort).setProxyType(type).build())
94115
.setUseInsecureTrustManager(true)
95116
.build();
96117
try (AsyncHttpClient asyncHttpClient = asyncHttpClient(config)) {
@@ -99,11 +120,11 @@ public void testNoDirectRequestBodyWithProxy() throws Exception {
99120
}
100121
}
101122

102-
@Test
103-
public void testDecompressBodyWithProxy() throws Exception {
123+
@Test(dataProvider = "serverPorts")
124+
public void testDecompressBodyWithProxy(int proxyPort, ProxyType type) throws Exception {
104125
AsyncHttpClientConfig config = config()
105126
.setFollowRedirect(true)
106-
.setProxyServer(proxyServer("localhost", port1).build())
127+
.setProxyServer(proxyServer("localhost", proxyPort).setProxyType(type).build())
107128
.setUseInsecureTrustManager(true)
108129
.build();
109130
try (AsyncHttpClient asyncHttpClient = asyncHttpClient(config)) {
@@ -116,11 +137,11 @@ public void testDecompressBodyWithProxy() throws Exception {
116137
}
117138
}
118139

119-
@Test
120-
public void testPooledConnectionsWithProxy() throws Exception {
140+
@Test(dataProvider = "serverPorts")
141+
public void testPooledConnectionsWithProxy(int proxyPort, ProxyType type) throws Exception {
121142

122143
try (AsyncHttpClient asyncHttpClient = asyncHttpClient(config().setFollowRedirect(true).setUseInsecureTrustManager(true).setKeepAlive(true))) {
123-
RequestBuilder rb = get(getTargetUrl2()).setProxyServer(proxyServer("localhost", port1));
144+
RequestBuilder rb = get(getTargetUrl2()).setProxyServer(proxyServer("localhost", proxyPort).setProxyType(type));
124145

125146
Response r1 = asyncHttpClient.executeRequest(rb.build()).get();
126147
assertEquals(r1.getStatusCode(), 200);

0 commit comments

Comments
 (0)