Skip to content

Commit 237ba25

Browse files
author
Stephane Landelle
committed
Merge pull request AsyncHttpClient#657 from jroper/websocket-proxy-connect
Ensured Netty provider uses CONNECT when proxying ws
2 parents 486cc11 + df775c9 commit 237ba25

File tree

6 files changed

+112
-40
lines changed

6 files changed

+112
-40
lines changed

src/main/java/com/ning/http/client/AsyncHttpClientConfig.java

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public class AsyncHttpClientConfig {
8383
protected HostnameVerifier hostnameVerifier;
8484
protected int ioThreadMultiplier;
8585
protected boolean strict302Handling;
86-
protected boolean useRelativeURIsWithSSLProxies;
86+
protected boolean useRelativeURIsWithConnectProxies;
8787
protected int maxConnectionLifeTimeInMs;
8888
protected TimeConverter timeConverter;
8989

@@ -120,7 +120,7 @@ private AsyncHttpClientConfig(int maxTotalConnections,
120120
HostnameVerifier hostnameVerifier,
121121
int ioThreadMultiplier,
122122
boolean strict302Handling,
123-
boolean useRelativeURIsWithSSLProxies,
123+
boolean useRelativeURIsWithConnectProxies,
124124
TimeConverter timeConverter) {
125125

126126
this.maxTotalConnections = maxTotalConnections;
@@ -151,7 +151,7 @@ private AsyncHttpClientConfig(int maxTotalConnections,
151151
this.hostnameVerifier = hostnameVerifier;
152152
this.ioThreadMultiplier = ioThreadMultiplier;
153153
this.strict302Handling = strict302Handling;
154-
this.useRelativeURIsWithSSLProxies = useRelativeURIsWithSSLProxies;
154+
this.useRelativeURIsWithConnectProxies = useRelativeURIsWithConnectProxies;
155155

156156
if (applicationThreadPool == null) {
157157
this.applicationThreadPool = Executors.newCachedThreadPool();
@@ -494,15 +494,27 @@ public boolean isStrict302Handling() {
494494
}
495495

496496
/**
497-
* @return<code>true</code> if AHC should use relative URIs instead of absolute ones when talking with a SSL proxy,
498-
* otherwise <code>false</code>.
497+
* @return<code>true</code> if AHC should use relative URIs instead of absolute ones when talking with a SSL proxy
498+
* or WebSocket proxy, otherwise <code>false</code>.
499499
*
500500
* @since 1.7.12
501+
* @deprecated Use isUseRelativeURIsWithConnectProxies instead.
501502
*/
503+
@Deprecated
502504
public boolean isUseRelativeURIsWithSSLProxies() {
503-
return useRelativeURIsWithSSLProxies;
505+
return useRelativeURIsWithConnectProxies;
506+
}
507+
508+
/**
509+
* @return<code>true</code> if AHC should use relative URIs instead of absolute ones when talking with a proxy
510+
* using the CONNECT method, for example when using SSL or WebSockets.
511+
*
512+
* @since 1.8.13
513+
*/
514+
public boolean isUseRelativeURIsWithConnectProxies() {
515+
return useRelativeURIsWithConnectProxies;
504516
}
505-
517+
506518
/**
507519
* Return the maximum time in millisecond an {@link com.ning.http.client.AsyncHttpClient} will keep connection in the pool, or -1 to keep connection while possible.
508520
*
@@ -538,7 +550,7 @@ public static class Builder {
538550
private boolean useProxyProperties = defaultUseProxyProperties();
539551
private boolean useProxySelector = defaultUseProxySelector();
540552
private boolean allowPoolingConnection = defaultAllowPoolingConnection();
541-
private boolean useRelativeURIsWithSSLProxies = defaultUseRelativeURIsWithSSLProxies();
553+
private boolean useRelativeURIsWithConnectProxies = defaultUseRelativeURIsWithConnectProxies();
542554
private int requestCompressionLevel = defaultRequestCompressionLevel();
543555
private int maxRequestRetry = defaultMaxRequestRetry();
544556
private int ioThreadMultiplier = defaultIoThreadMultiplier();
@@ -1005,15 +1017,31 @@ public Builder setStrict302Handling(final boolean strict302Handling) {
10051017
}
10061018

10071019
/**
1008-
* Configures this AHC instance to use relative URIs instead of absolute ones when talking with a SSL proxy.
1020+
* Configures this AHC instance to use relative URIs instead of absolute ones when talking with a SSL proxy or
1021+
* WebSocket proxy.
10091022
*
10101023
* @param useRelativeURIsWithSSLProxies
10111024
* @return this
10121025
*
10131026
* @since 1.7.2
1027+
* @deprecated Use setUseRelativeURIsWithConnectProxies instead.
10141028
*/
10151029
public Builder setUseRelativeURIsWithSSLProxies(boolean useRelativeURIsWithSSLProxies) {
1016-
this.useRelativeURIsWithSSLProxies = useRelativeURIsWithSSLProxies;
1030+
this.useRelativeURIsWithConnectProxies = useRelativeURIsWithSSLProxies;
1031+
return this;
1032+
}
1033+
1034+
/**
1035+
* Configures this AHC instance to use relative URIs instead of absolute ones when making requests through
1036+
* proxies using the CONNECT method, such as when making SSL requests or WebSocket requests.
1037+
*
1038+
* @param useRelativeURIsWithConnectProxies
1039+
* @return this
1040+
*
1041+
* @since 1.8.13
1042+
*/
1043+
public Builder setUseRelativeURIsWithConnectProxies(boolean useRelativeURIsWithConnectProxies) {
1044+
this.useRelativeURIsWithConnectProxies = useRelativeURIsWithConnectProxies;
10171045
return this;
10181046
}
10191047

@@ -1139,7 +1167,7 @@ public Thread newThread(Runnable r) {
11391167
hostnameVerifier,
11401168
ioThreadMultiplier,
11411169
strict302Handling,
1142-
useRelativeURIsWithSSLProxies,
1170+
useRelativeURIsWithConnectProxies,
11431171
timeConverter);
11441172
}
11451173
}

src/main/java/com/ning/http/client/AsyncHttpClientConfigBean.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ void configureDefaults() {
5858
compressionEnabled = defaultCompressionEnabled();
5959
userAgent = defaultUserAgent();
6060
allowPoolingConnection = defaultAllowPoolingConnection();
61-
useRelativeURIsWithSSLProxies = defaultUseRelativeURIsWithSSLProxies();
61+
useRelativeURIsWithConnectProxies = defaultUseRelativeURIsWithConnectProxies();
6262
requestCompressionLevel = defaultRequestCompressionLevel();
6363
maxRequestRetry = defaultMaxRequestRetry();
6464
ioThreadMultiplier = defaultIoThreadMultiplier();

src/main/java/com/ning/http/client/AsyncHttpClientConfigDefaults.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,17 @@
1313
package com.ning.http.client;
1414

1515
import com.ning.http.util.AllowAllHostnameVerifier;
16+
import org.slf4j.Logger;
17+
import org.slf4j.LoggerFactory;
18+
1619
import static com.ning.http.util.MiscUtil.getBoolean;
1720

1821
import javax.net.ssl.HostnameVerifier;
1922

2023
public final class AsyncHttpClientConfigDefaults {
2124

25+
private final static Logger log = LoggerFactory.getLogger(AsyncHttpClientConfigDefaults.class);
26+
2227
private AsyncHttpClientConfigDefaults() {
2328
}
2429

@@ -92,8 +97,23 @@ public static boolean defaultAllowPoolingConnection() {
9297
return getBoolean(ASYNC_CLIENT + "allowPoolingConnection", true);
9398
}
9499

100+
/**
101+
* @deprecated Use defaultUseRelativeURIsWithConnectProxies instead.
102+
*/
103+
@Deprecated
95104
public static boolean defaultUseRelativeURIsWithSSLProxies() {
96-
return getBoolean(ASYNC_CLIENT + "useRelativeURIsWithSSLProxies", true);
105+
String systemPropValue = System.getProperty(ASYNC_CLIENT + "useRelativeURIsWithSSLProxies");
106+
if (systemPropValue != null) {
107+
log.warn(ASYNC_CLIENT + "useRelativeURIsWithSSLProxies is deprecated, use " + ASYNC_CLIENT +
108+
"defaultUseRelativeURIsWithConnectProxies instead");
109+
return systemPropValue.equalsIgnoreCase("true");
110+
} else {
111+
return true;
112+
}
113+
}
114+
115+
public static boolean defaultUseRelativeURIsWithConnectProxies() {
116+
return getBoolean(ASYNC_CLIENT + "useRelativeURIsWithConnectProxies", defaultUseRelativeURIsWithSSLProxies());
97117
}
98118

99119
// unused/broken, left there for compatibility, fixed in Netty 4

src/main/java/com/ning/http/client/providers/grizzly/GrizzlyAsyncHttpProvider.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -891,7 +891,7 @@ private boolean sendAsGrizzlyRequest(final Request request,
891891
httpCtx.establishingTunnel = true;
892892
builder.method(Method.CONNECT);
893893
builder.uri(AsyncHttpProviderUtils.getAuthority(uri));
894-
} else if (secure && config.isUseRelativeURIsWithSSLProxies()){
894+
} else if ((secure || httpCtx.isWSRequest) && config.isUseRelativeURIsWithConnectProxies()){
895895
builder.uri(uri.getPath());
896896
} else {
897897
builder.uri(uri.toString());

src/main/java/com/ning/http/client/providers/netty/NettyAsyncHttpProvider.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -649,12 +649,16 @@ protected final static HttpRequest buildRequest(AsyncHttpClientConfig config, Re
649649
throws IOException {
650650

651651
String method = request.getMethod();
652-
if (allowConnect && proxyServer != null && isSecure(uri)) {
652+
if (allowConnect && proxyServer != null && useProxyConnect(uri)) {
653653
method = HttpMethod.CONNECT.toString();
654654
}
655655
return construct(config, request, new HttpMethod(method), uri, buffer, proxyServer);
656656
}
657657

658+
protected final static boolean useProxyConnect(URI uri) {
659+
return isSecure(uri) || isWebSocket(uri.getScheme());
660+
}
661+
658662
private static SpnegoEngine getSpnegoEngine() {
659663
if (spnegoEngine == null)
660664
spnegoEngine = new SpnegoEngine();
@@ -676,7 +680,7 @@ private static HttpRequest construct(AsyncHttpClientConfig config, Request reque
676680
nettyRequest = new DefaultHttpRequest(HttpVersion.HTTP_1_0, m, AsyncHttpProviderUtils.getAuthority(uri));
677681
} else {
678682
String path = null;
679-
if (proxyServer != null && !(isSecure(uri) && config.isUseRelativeURIsWithSSLProxies()))
683+
if (proxyServer != null && !(useProxyConnect(uri) && config.isUseRelativeURIsWithConnectProxies()))
680684
path = uri.toString();
681685
else if (uri.getRawQuery() != null)
682686
path = uri.getRawPath() + "?" + uri.getRawQuery();

src/test/java/com/ning/http/client/websocket/ProxyTunnellingTest.java

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.eclipse.jetty.server.nio.SelectChannelConnector;
2828
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
2929
import org.testng.annotations.AfterClass;
30+
import org.testng.annotations.AfterMethod;
3031
import org.testng.annotations.BeforeClass;
3132
import org.testng.annotations.Test;
3233

@@ -43,39 +44,46 @@ public abstract class ProxyTunnellingTest extends AbstractBasicTest {
4344
int port2;
4445
private Server server2;
4546

46-
@BeforeClass(alwaysRun = true)
4747
public void setUpGlobal() throws Exception {
48-
server2 = new Server();
48+
}
49+
50+
private void setUpServers(Connector server2Connector) throws Exception {
4951

5052
port1 = findFreePort();
5153
port2 = findFreePort();
52-
5354
Connector listener = new SelectChannelConnector();
54-
5555
listener.setHost("127.0.0.1");
5656
listener.setPort(port1);
57-
5857
addConnector(listener);
58+
setHandler(new ConnectHandler());
59+
start();
5960

60-
SslSelectChannelConnector connector = new SslSelectChannelConnector();
61-
connector.setHost("127.0.0.1");
62-
connector.setPort(port2);
61+
server2 = new Server();
62+
63+
server2Connector.setHost("127.0.0.1");
64+
server2Connector.setPort(port2);
65+
66+
server2.addConnector(server2Connector);
67+
68+
server2.setHandler(getWebSocketHandler());
69+
server2.start();
70+
log.info("Local HTTP server started successfully");
71+
72+
}
73+
74+
private void setUpServer() throws Exception {
75+
setUpServers(new SelectChannelConnector());
76+
}
6377

78+
private void setUpSSlServer2() throws Exception {
79+
SslSelectChannelConnector connector = new SslSelectChannelConnector();
6480
ClassLoader cl = getClass().getClassLoader();
6581
URL keystoreUrl = cl.getResource("ssltest-keystore.jks");
6682
String keyStoreFile = new File(keystoreUrl.toURI()).getAbsolutePath();
6783
connector.setKeystore(keyStoreFile);
6884
connector.setKeyPassword("changeit");
6985
connector.setKeystoreType("JKS");
70-
71-
server2.addConnector(connector);
72-
73-
setHandler(new ConnectHandler());
74-
start();
75-
76-
server2.setHandler(getWebSocketHandler());
77-
server2.start();
78-
log.info("Local HTTP server started successfully");
86+
setUpServers(connector);
7987
}
8088

8189
@Override
@@ -88,27 +96,38 @@ public org.eclipse.jetty.websocket.WebSocket doWebSocketConnect(HttpServletReque
8896
};
8997
}
9098

91-
@AfterClass(alwaysRun = true)
99+
@AfterMethod(alwaysRun = true)
92100
public void tearDownGlobal() throws Exception {
93101
stop();
94-
server2.stop();
102+
if (server2 != null) {
103+
server2.stop();
104+
}
105+
server2 = null;
95106
}
96107

97-
protected String getTargetUrl() {
98-
return String.format("wss://127.0.0.1:%d/", port2);
108+
@Test(timeOut = 60000)
109+
public void echoWSText() throws Exception {
110+
setUpServer();
111+
runTest("ws");
99112
}
100113

101114
@Test(timeOut = 60000)
102-
public void echoText() throws Exception {
115+
public void echoWSSText() throws Exception {
116+
setUpSSlServer2();
117+
runTest("wss");
118+
}
103119

104-
ProxyServer ps = new ProxyServer(ProxyServer.Protocol.HTTPS, "127.0.0.1", port1);
120+
private void runTest(String protocol) throws Exception {
121+
String targetUrl = String.format("%s://127.0.0.1:%d/", protocol, port2);
122+
123+
ProxyServer ps = new ProxyServer(ProxyServer.Protocol.HTTP, "127.0.0.1", port1);
105124
AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().setProxyServer(ps).build();
106125
AsyncHttpClient asyncHttpClient = getAsyncHttpClient(config);
107126
try {
108127
final CountDownLatch latch = new CountDownLatch(1);
109128
final AtomicReference<String> text = new AtomicReference<String>("");
110129

111-
WebSocket websocket = asyncHttpClient.prepareGet(getTargetUrl()).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketTextListener() {
130+
WebSocket websocket = asyncHttpClient.prepareGet(targetUrl).execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(new WebSocketTextListener() {
112131

113132
@Override
114133
public void onMessage(String message) {
@@ -143,5 +162,6 @@ public void onError(Throwable t) {
143162
} finally {
144163
asyncHttpClient.close();
145164
}
165+
146166
}
147167
}

0 commit comments

Comments
 (0)