Skip to content

Commit 7334dbc

Browse files
author
Stephane Landelle
committed
Reuse channel on 401, close AsyncHttpClient#785
1 parent a8b89b4 commit 7334dbc

File tree

4 files changed

+41
-33
lines changed

4 files changed

+41
-33
lines changed

src/main/java/com/ning/http/client/providers/netty/channel/ChannelManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -451,7 +451,7 @@ public void call() {
451451
};
452452
}
453453

454-
public void drainChannel(final Channel channel, final NettyResponseFuture<?> future) {
454+
public void drainChannelAndOffer(final Channel channel, final NettyResponseFuture<?> future) {
455455
Channels.setAttribute(channel, newDrainCallback(future, channel, future.isKeepAlive(), getPartitionId(future)));
456456
}
457457

src/main/java/com/ning/http/client/providers/netty/handler/HttpProtocol.java

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,7 @@ private Realm kerberosChallenge(Channel channel,//
7676
FluentCaseInsensitiveStringsMap headers,//
7777
Realm realm,//
7878
NettyResponseFuture<?> future,//
79-
boolean proxyInd)
80-
throws NTLMEngineException {
79+
boolean proxyInd) throws NTLMEngineException {
8180

8281
Uri uri = request.getUri();
8382
String host = request.getVirtualHost() == null ? uri.getHost() : request.getVirtualHost();
@@ -146,9 +145,8 @@ private Realm ntlmChallenge(String wwwAuth, Request request, ProxyServer proxySe
146145
}
147146
}
148147

149-
private Realm ntlmProxyChallenge(String wwwAuth, Request request, ProxyServer proxyServer,
150-
FluentCaseInsensitiveStringsMap headers, Realm realm, NettyResponseFuture<?> future, boolean proxyInd)
151-
throws NTLMEngineException {
148+
private Realm ntlmProxyChallenge(String wwwAuth, Request request, ProxyServer proxyServer, FluentCaseInsensitiveStringsMap headers,
149+
Realm realm, NettyResponseFuture<?> future, boolean proxyInd) throws NTLMEngineException {
152150
future.getAndSetAuth(false);
153151
headers.remove(HttpHeaders.Names.PROXY_AUTHORIZATION);
154152

@@ -161,8 +159,8 @@ private Realm ntlmProxyChallenge(String wwwAuth, Request request, ProxyServer pr
161159
.setMethodName(request.getMethod()).build();
162160
}
163161

164-
private void addType3NTLMAuthorizationHeader(String auth, FluentCaseInsensitiveStringsMap headers, String username,
165-
String password, String domain, String workstation, boolean proxyInd) throws NTLMEngineException {
162+
private void addType3NTLMAuthorizationHeader(String auth, FluentCaseInsensitiveStringsMap headers, String username, String password,
163+
String domain, String workstation, boolean proxyInd) throws NTLMEngineException {
166164
headers.remove(authorizationHeaderName(proxyInd));
167165

168166
if (isNonEmpty(auth) && auth.startsWith("NTLM ")) {
@@ -176,7 +174,7 @@ private void finishUpdate(final NettyResponseFuture<?> future, Channel channel,
176174

177175
boolean keepAlive = future.isKeepAlive();
178176
if (expectOtherChunks && keepAlive)
179-
channelManager.drainChannel(channel, future);
177+
channelManager.drainChannelAndOffer(channel, future);
180178
else
181179
channelManager.tryToOfferChannelToPool(channel, keepAlive, channelManager.getPartitionId(future));
182180
markAsDone(future, channel);
@@ -228,17 +226,11 @@ private boolean exitAfterHandling401(//
228226
// NTLM
229227
newRealm = ntlmChallenge(ntlmAuthenticate, request, proxyServer, request.getHeaders(), realm, future, false);
230228

231-
// don't forget to reuse channel: NTLM authenticates a connection
232-
future.setReuseChannel(true);
233-
234229
} else if (negociate) {
235230
// SPNEGO KERBEROS
236231
newRealm = kerberosChallenge(channel, wwwAuthHeaders, request, proxyServer, request.getHeaders(), realm, future, false);
237232
if (newRealm == null)
238233
return true;
239-
else
240-
// don't forget to reuse channel: KERBEROS authenticates a connection
241-
future.setReuseChannel(true);
242234

243235
} else {
244236
newRealm = new Realm.RealmBuilder()//
@@ -255,20 +247,22 @@ private boolean exitAfterHandling401(//
255247
final Request nextRequest = new RequestBuilder(future.getRequest()).setHeaders(request.getHeaders()).setRealm(nr).build();
256248

257249
logger.debug("Sending authentication to {}", request.getUri());
258-
Callback callback = new Callback(future) {
259-
public void call() throws IOException {
260-
channelManager.drainChannel(channel, future);
250+
if (future.isKeepAlive()) {
251+
if (HttpHeaders.isTransferEncodingChunked(response)) {
252+
// we must first drain the channel, let's use a fresh one for performing auth
253+
Channels.setAttribute(channel, new Callback(future) {
254+
public void call() throws IOException {
255+
requestSender.drainChannelAndExecuteNextRequest(channel, future, nextRequest);
256+
}
257+
});
258+
} else {
259+
future.setReuseChannel(true);
261260
requestSender.sendNextRequest(nextRequest, future);
262261
}
263-
};
264-
265-
if (future.isKeepAlive() && HttpHeaders.isTransferEncodingChunked(response))
266-
// We must make sure there is no bytes left
267-
// before executing the next request.
268-
Channels.setAttribute(channel, callback);
269-
else
270-
// call might crash with an IOException
271-
callback.call();
262+
} else {
263+
channelManager.closeChannel(channel);
264+
requestSender.sendNextRequest(nextRequest, future);
265+
}
272266

273267
return true;
274268
}
@@ -374,8 +368,8 @@ private boolean exitAfterHandlingConnect(//
374368
return false;
375369
}
376370

377-
private boolean exitAfterHandlingStatus(Channel channel, NettyResponseFuture<?> future, HttpResponse response,
378-
AsyncHandler<?> handler, NettyResponseStatus status) throws IOException, Exception {
371+
private boolean exitAfterHandlingStatus(Channel channel, NettyResponseFuture<?> future, HttpResponse response, AsyncHandler<?> handler,
372+
NettyResponseStatus status) throws IOException, Exception {
379373
if (!future.getAndSetStatusReceived(true) && handler.onStatusReceived(status) != STATE.CONTINUE) {
380374
finishUpdate(future, channel, HttpHeaders.isTransferEncodingChunked(response));
381375
return true;
@@ -393,8 +387,8 @@ private boolean exitAfterHandlingHeaders(Channel channel, NettyResponseFuture<?>
393387
}
394388

395389
// Netty 3: if the response is not chunked, the full body comes with the response
396-
private boolean exitAfterHandlingBody(Channel channel, NettyResponseFuture<?> future, HttpResponse response,
397-
AsyncHandler<?> handler) throws Exception {
390+
private boolean exitAfterHandlingBody(Channel channel, NettyResponseFuture<?> future, HttpResponse response, AsyncHandler<?> handler)
391+
throws Exception {
398392
if (!response.isChunked()) {
399393
// no chunks expected, exiting
400394
if (response.getContent().readableBytes() > 0)

src/main/java/com/ning/http/client/providers/netty/request/NettyRequestSender.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import com.ning.http.client.filter.FilterException;
4747
import com.ning.http.client.filter.IOExceptionFilter;
4848
import com.ning.http.client.listener.TransferCompletionHandler;
49+
import com.ning.http.client.providers.netty.Callback;
4950
import com.ning.http.client.providers.netty.NettyAsyncHttpProviderConfig;
5051
import com.ning.http.client.providers.netty.channel.ChannelManager;
5152
import com.ning.http.client.providers.netty.channel.Channels;
@@ -529,12 +530,26 @@ public void replayRequest(final NettyResponseFuture<?> future, FilterContext fc,
529530
if (future.getAsyncHandler() instanceof AsyncHandlerExtensions)
530531
AsyncHandlerExtensions.class.cast(future.getAsyncHandler()).onRetry();
531532

532-
channelManager.drainChannel(channel, future);
533+
channelManager.drainChannelAndOffer(channel, future);
533534
sendNextRequest(newRequest, future);
534535
return;
535536
}
536537

537538
public boolean isClosed() {
538539
return closed.get();
539540
}
541+
542+
public final Callback newExecuteNextRequestCallback(final NettyResponseFuture<?> future, final Request nextRequest) {
543+
544+
return new Callback(future) {
545+
@Override
546+
public void call() throws IOException {
547+
sendNextRequest(nextRequest, future);
548+
}
549+
};
550+
}
551+
552+
public void drainChannelAndExecuteNextRequest(final Channel channel, final NettyResponseFuture<?> future, Request nextRequest) {
553+
Channels.setAttribute(channel, newExecuteNextRequestCallback(future, nextRequest));
554+
}
540555
}

src/test/java/com/ning/http/client/async/BasicAuthTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import com.ning.http.client.Realm;
2525
import com.ning.http.client.Realm.AuthScheme;
2626
import com.ning.http.client.Response;
27-
import com.ning.http.client.generators.InputStreamBodyGenerator;
2827

2928
import org.apache.log4j.ConsoleAppender;
3029
import org.apache.log4j.Level;

0 commit comments

Comments
 (0)