Skip to content

Commit 9c22be1

Browse files
committed
Port fix for AsyncHttpClient#102 [websocket] Support redirect
1 parent 1f7a4f6 commit 9c22be1

File tree

3 files changed

+92
-70
lines changed

3 files changed

+92
-70
lines changed

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

Lines changed: 86 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -987,13 +987,16 @@ private <T> ListenableFuture<T> doConnect(final Request request, final AsyncHand
987987
remoteAddress = new InetSocketAddress(request.getInetAddress(), AsyncHttpProviderUtils.getPort(uri));
988988
} else if (proxyServer == null || avoidProxy) {
989989
remoteAddress = new InetSocketAddress(AsyncHttpProviderUtils.getHost(uri), AsyncHttpProviderUtils.getPort(uri));
990-
} else if(request.getLocalAddress() != null) {
991-
remoteAddress = new InetSocketAddress(request.getLocalAddress(), 0);
992990
} else {
993991
remoteAddress = new InetSocketAddress(proxyServer.getHost(), proxyServer.getPort());
994992
}
995993

996-
channelFuture = bootstrap.connect(remoteAddress);
994+
if(request.getLocalAddress() != null){
995+
channelFuture = bootstrap.connect(remoteAddress, new InetSocketAddress(request.getLocalAddress(), 0));
996+
}else{
997+
channelFuture = bootstrap.connect(remoteAddress);
998+
}
999+
9971000
} catch (Throwable t) {
9981001
if (acquiredConnection) {
9991002
freeConnections.release();
@@ -1966,6 +1969,81 @@ private static final boolean validateWebSocketRequest(Request request, AsyncHand
19661969
return true;
19671970
}
19681971

1972+
private boolean redirect(Request request,
1973+
NettyResponseFuture<?> future,
1974+
HttpResponse response,
1975+
final ChannelHandlerContext ctx) throws Exception {
1976+
1977+
int statusCode = response.getStatus().getCode();
1978+
boolean redirectEnabled = request.isRedirectOverrideSet() ? request.isRedirectEnabled() : config.isRedirectEnabled();
1979+
if (redirectEnabled && (statusCode == 302
1980+
|| statusCode == 301
1981+
|| statusCode == 303
1982+
|| statusCode == 307)) {
1983+
1984+
if (future.incrementAndGetCurrentRedirectCount() < config.getMaxRedirects()) {
1985+
// We must allow 401 handling again.
1986+
future.getAndSetAuth(false);
1987+
1988+
String location = response.getHeader(HttpHeaders.Names.LOCATION);
1989+
URI uri = AsyncHttpProviderUtils.getRedirectUri(future.getURI(), location);
1990+
boolean stripQueryString = config.isRemoveQueryParamOnRedirect();
1991+
if (!uri.toString().equalsIgnoreCase(future.getURI().toString())) {
1992+
final RequestBuilder nBuilder = stripQueryString ?
1993+
new RequestBuilder(future.getRequest()).setQueryParameters(null)
1994+
: new RequestBuilder(future.getRequest());
1995+
1996+
if (!(statusCode < 302 || statusCode > 303)
1997+
&& !(statusCode == 302
1998+
&& config.isStrict302Handling())) {
1999+
nBuilder.setMethod("GET");
2000+
}
2001+
final URI initialConnectionUri = future.getURI();
2002+
final boolean initialConnectionKeepAlive = future.getKeepAlive();
2003+
future.setURI(uri);
2004+
String newUrl = uri.toString();
2005+
if (request.getUrl().startsWith(WEBSOCKET)) {
2006+
newUrl = newUrl.replace(HTTP, WEBSOCKET);
2007+
}
2008+
2009+
log.debug("Redirecting to {}", newUrl);
2010+
for (String cookieStr : future.getHttpResponse().getHeaders(HttpHeaders.Names.SET_COOKIE)) {
2011+
Cookie c = AsyncHttpProviderUtils.parseCookie(cookieStr);
2012+
nBuilder.addOrReplaceCookie(c);
2013+
}
2014+
2015+
for (String cookieStr : future.getHttpResponse().getHeaders(HttpHeaders.Names.SET_COOKIE2)) {
2016+
Cookie c = AsyncHttpProviderUtils.parseCookie(cookieStr);
2017+
nBuilder.addOrReplaceCookie(c);
2018+
}
2019+
2020+
AsyncCallable ac = new AsyncCallable(future) {
2021+
public Object call() throws Exception {
2022+
if (initialConnectionKeepAlive && ctx.getChannel().isReadable() &&
2023+
connectionsPool.offer(AsyncHttpProviderUtils.getBaseUrl(initialConnectionUri), ctx.getChannel())) {
2024+
return null;
2025+
}
2026+
finishChannel(ctx);
2027+
return null;
2028+
}
2029+
};
2030+
2031+
if (response.isChunked()) {
2032+
// We must make sure there is no bytes left before executing the next request.
2033+
ctx.setAttachment(ac);
2034+
} else {
2035+
ac.call();
2036+
}
2037+
nextRequest(nBuilder.setUrl(newUrl).build(), future);
2038+
return true;
2039+
}
2040+
} else {
2041+
throw new MaxRedirectException("Maximum redirect reached: " + config.getMaxRedirects());
2042+
}
2043+
}
2044+
return false;
2045+
}
2046+
19692047
private final class HttpProtocol implements Protocol {
19702048
// @Override
19712049
public void handle(final ChannelHandlerContext ctx, final MessageEvent e) throws Exception {
@@ -2142,69 +2220,7 @@ public Object call() throws Exception {
21422220
return;
21432221
}
21442222

2145-
boolean redirectEnabled = request.isRedirectOverrideSet() ? request.isRedirectEnabled() : config.isRedirectEnabled();
2146-
if (redirectEnabled && (statusCode == 302
2147-
|| statusCode == 301
2148-
|| statusCode == 303
2149-
|| statusCode == 307)) {
2150-
2151-
if (future.incrementAndGetCurrentRedirectCount() < config.getMaxRedirects()) {
2152-
// We must allow 401 handling again.
2153-
future.getAndSetAuth(false);
2154-
2155-
String location = response.getHeader(HttpHeaders.Names.LOCATION);
2156-
URI uri = AsyncHttpProviderUtils.getRedirectUri(future.getURI(), location);
2157-
boolean stripQueryString = config.isRemoveQueryParamOnRedirect();
2158-
if (!uri.toString().equalsIgnoreCase(future.getURI().toString())) {
2159-
final RequestBuilder nBuilder = stripQueryString ?
2160-
new RequestBuilder(future.getRequest()).setQueryParameters(null)
2161-
: new RequestBuilder(future.getRequest());
2162-
2163-
if (!(statusCode < 302 || statusCode > 303)
2164-
&& !(statusCode == 302
2165-
&& config.isStrict302Handling())) {
2166-
nBuilder.setMethod("GET");
2167-
}
2168-
final URI initialConnectionUri = future.getURI();
2169-
final boolean initialConnectionKeepAlive = future.getKeepAlive();
2170-
future.setURI(uri);
2171-
final String newUrl = uri.toString();
2172-
2173-
log.debug("Redirecting to {}", newUrl);
2174-
for (String cookieStr : future.getHttpResponse().getHeaders(HttpHeaders.Names.SET_COOKIE)) {
2175-
Cookie c = AsyncHttpProviderUtils.parseCookie(cookieStr);
2176-
nBuilder.addOrReplaceCookie(c);
2177-
}
2178-
2179-
for (String cookieStr : future.getHttpResponse().getHeaders(HttpHeaders.Names.SET_COOKIE2)) {
2180-
Cookie c = AsyncHttpProviderUtils.parseCookie(cookieStr);
2181-
nBuilder.addOrReplaceCookie(c);
2182-
}
2183-
2184-
AsyncCallable ac = new AsyncCallable(future) {
2185-
public Object call() throws Exception {
2186-
if (initialConnectionKeepAlive && ctx.getChannel().isReadable() &&
2187-
connectionsPool.offer(AsyncHttpProviderUtils.getBaseUrl(initialConnectionUri), ctx.getChannel())) {
2188-
return null;
2189-
}
2190-
finishChannel(ctx);
2191-
return null;
2192-
}
2193-
};
2194-
2195-
if (response.isChunked()) {
2196-
// We must make sure there is no bytes left before executing the next request.
2197-
ctx.setAttachment(ac);
2198-
} else {
2199-
ac.call();
2200-
}
2201-
nextRequest(nBuilder.setUrl(newUrl).build(), future);
2202-
return;
2203-
}
2204-
} else {
2205-
throw new MaxRedirectException("Maximum redirect reached: " + config.getMaxRedirects());
2206-
}
2207-
}
2223+
if (redirect(request, future, response, ctx)) return;
22082224

22092225
if (!future.getAndSetStatusReceived(true) && updateStatusAndInterrupt(handler, status)) {
22102226
finishUpdate(future, ctx, response.isChunked());
@@ -2297,7 +2313,7 @@ public void handle(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
22972313
}
22982314
} catch (FilterException efe) {
22992315
abort(future, efe);
2300-
} // @Override
2316+
}
23012317

23022318
}
23032319

@@ -2310,6 +2326,9 @@ public void handle(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
23102326
return;
23112327
}
23122328

2329+
future.setHttpResponse(response);
2330+
if (redirect(request, future, response, ctx)) return;
2331+
23132332
final org.jboss.netty.handler.codec.http.HttpResponseStatus status =
23142333
new org.jboss.netty.handler.codec.http.HttpResponseStatus(101, "Web Socket Protocol Handshake");
23152334

src/main/java/com/ning/http/util/AsyncHttpProviderUtils.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,12 @@ public final static URI getRedirectUri(URI uri, String location) {
221221

222222
String scheme = newUri.getScheme();
223223

224-
if (scheme == null || !scheme.equalsIgnoreCase("http") && !scheme.equalsIgnoreCase("https")) {
224+
if (scheme == null || !scheme.equalsIgnoreCase("http")
225+
&& !scheme.equalsIgnoreCase("https")
226+
&& !scheme.equals("ws")
227+
&& !scheme.equals("wss")) {
225228
throw new IllegalArgumentException("The URI scheme, of the URI " + newUri
226-
+ ", must be equal (ignoring case) to 'http' or 'https'");
229+
+ ", must be equal (ignoring case) to 'ws, 'wss', 'http', or 'https'");
227230
}
228231

229232
return newUri;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727

2828
public abstract class TextMessageTest extends AbstractBasicTest {
2929

30-
private final class EchoTextWebSocket implements org.eclipse.jetty.websocket.WebSocket, org.eclipse.jetty.websocket.WebSocket.OnTextMessage {
30+
public static final class EchoTextWebSocket implements org.eclipse.jetty.websocket.WebSocket, org.eclipse.jetty.websocket.WebSocket.OnTextMessage {
3131

3232
private Connection connection;
3333

0 commit comments

Comments
 (0)