Skip to content

Commit ec58da8

Browse files
author
Stephane Landelle
committed
Reuse channel on 401, close AsyncHttpClient#785
1 parent 78be7ec commit ec58da8

File tree

6 files changed

+106
-58
lines changed

6 files changed

+106
-58
lines changed

providers/netty3/src/main/java/org/asynchttpclient/providers/netty3/channel/ChannelManager.java

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

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

providers/netty3/src/main/java/org/asynchttpclient/providers/netty3/handler/HttpProtocol.java

Lines changed: 34 additions & 29 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();
@@ -111,8 +110,13 @@ private void addNTLMAuthorizationHeader(FluentCaseInsensitiveStringsMap headers,
111110
headers.add(authorizationHeaderName(proxyInd), "NTLM " + challengeHeader);
112111
}
113112

114-
private Realm ntlmChallenge(String wwwAuth, Request request, ProxyServer proxyServer, FluentCaseInsensitiveStringsMap headers,
115-
Realm realm, NettyResponseFuture<?> future, boolean proxyInd) throws NTLMEngineException {
113+
private Realm ntlmChallenge(String authenticateHeader,//
114+
Request request,//
115+
ProxyServer proxyServer,//
116+
FluentCaseInsensitiveStringsMap headers,//
117+
Realm realm,//
118+
NettyResponseFuture<?> future,//
119+
boolean proxyInd) throws NTLMEngineException {
116120

117121
boolean useRealm = proxyServer == null && realm != null;
118122

@@ -122,7 +126,7 @@ private Realm ntlmChallenge(String wwwAuth, Request request, ProxyServer proxySe
122126
String password = useRealm ? realm.getPassword() : proxyServer.getPassword();
123127
Uri uri = request.getUri();
124128

125-
if (wwwAuth.equals("NTLM")) {
129+
if (authenticateHeader.equals("NTLM")) {
126130
// server replied bare NTLM => we didn't preemptively sent Type1Msg
127131
String challengeHeader = NTLMEngine.INSTANCE.generateType1Msg();
128132

@@ -136,7 +140,7 @@ private Realm ntlmChallenge(String wwwAuth, Request request, ProxyServer proxySe
136140

137141
} else {
138142
// probably receiving Type2Msg, so we issue Type3Msg
139-
addType3NTLMAuthorizationHeader(wwwAuth, headers, principal, password, ntlmDomain, ntlmHost, proxyInd);
143+
addType3NTLMAuthorizationHeader(authenticateHeader, headers, principal, password, ntlmDomain, ntlmHost, proxyInd);
140144
Realm.AuthScheme authScheme = realm != null ? realm.getAuthScheme() : Realm.AuthScheme.NTLM;
141145
return newRealmBuilder(realm)//
142146
.setScheme(authScheme)//
@@ -146,13 +150,18 @@ private Realm ntlmChallenge(String wwwAuth, Request request, ProxyServer proxySe
146150
}
147151
}
148152

149-
private Realm ntlmProxyChallenge(String wwwAuth, Request request, ProxyServer proxyServer,
150-
FluentCaseInsensitiveStringsMap headers, Realm realm, NettyResponseFuture<?> future, boolean proxyInd)
151-
throws NTLMEngineException {
153+
private Realm ntlmProxyChallenge(String authenticateHeader,//
154+
Request request,//
155+
ProxyServer proxyServer,//
156+
FluentCaseInsensitiveStringsMap headers,//
157+
Realm realm,//
158+
NettyResponseFuture<?> future,//
159+
boolean proxyInd) throws NTLMEngineException {
160+
152161
future.getAndSetAuth(false);
153162
headers.remove(HttpHeaders.Names.PROXY_AUTHORIZATION);
154163

155-
addType3NTLMAuthorizationHeader(wwwAuth, headers, proxyServer.getPrincipal(), proxyServer.getPassword(),
164+
addType3NTLMAuthorizationHeader(authenticateHeader, headers, proxyServer.getPrincipal(), proxyServer.getPassword(),
156165
proxyServer.getNtlmDomain(), proxyServer.getHost(), proxyInd);
157166

158167
return newRealmBuilder(realm)//
@@ -176,7 +185,7 @@ private void finishUpdate(final NettyResponseFuture<?> future, Channel channel,
176185

177186
boolean keepAlive = future.isKeepAlive();
178187
if (expectOtherChunks && keepAlive)
179-
channelManager.drainChannel(channel, future);
188+
channelManager.drainChannelAndOffer(channel, future);
180189
else
181190
channelManager.tryToOfferChannelToPool(channel, keepAlive, channelManager.getPartitionId(future));
182191
markAsDone(future, channel);
@@ -228,17 +237,11 @@ private boolean exitAfterHandling401(//
228237
// NTLM
229238
newRealm = ntlmChallenge(ntlmAuthenticate, request, proxyServer, request.getHeaders(), realm, future, false);
230239

231-
// don't forget to reuse channel: NTLM authenticates a connection
232-
future.setReuseChannel(true);
233-
234240
} else if (negociate) {
235241
// SPNEGO KERBEROS
236242
newRealm = kerberosChallenge(channel, wwwAuthHeaders, request, proxyServer, request.getHeaders(), realm, future, false);
237243
if (newRealm == null)
238244
return true;
239-
else
240-
// don't forget to reuse channel: KERBEROS authenticates a connection
241-
future.setReuseChannel(true);
242245

243246
} else {
244247
newRealm = new Realm.RealmBuilder()//
@@ -255,20 +258,22 @@ private boolean exitAfterHandling401(//
255258
final Request nextRequest = new RequestBuilder(future.getRequest()).setHeaders(request.getHeaders()).setRealm(nr).build();
256259

257260
logger.debug("Sending authentication to {}", request.getUri());
258-
Callback callback = new Callback(future) {
259-
public void call() throws IOException {
260-
channelManager.drainChannel(channel, future);
261+
if (future.isKeepAlive()) {
262+
if (HttpHeaders.isTransferEncodingChunked(response)) {
263+
// we must first drain the channel, let's use a fresh one for performing auth
264+
Channels.setAttribute(channel, new Callback(future) {
265+
public void call() throws IOException {
266+
requestSender.drainChannelAndExecuteNextRequest(channel, future, nextRequest);
267+
}
268+
});
269+
} else {
270+
future.setReuseChannel(true);
261271
requestSender.sendNextRequest(nextRequest, future);
262272
}
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();
273+
} else {
274+
channelManager.closeChannel(channel);
275+
requestSender.sendNextRequest(nextRequest, future);
276+
}
272277

273278
return true;
274279
}

providers/netty3/src/main/java/org/asynchttpclient/providers/netty3/request/NettyRequestSender.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.asynchttpclient.filter.FilterException;
4141
import org.asynchttpclient.filter.IOExceptionFilter;
4242
import org.asynchttpclient.listener.TransferCompletionHandler;
43+
import org.asynchttpclient.providers.netty3.Callback;
4344
import org.asynchttpclient.providers.netty3.NettyAsyncHttpProviderConfig;
4445
import org.asynchttpclient.providers.netty3.channel.ChannelManager;
4546
import org.asynchttpclient.providers.netty3.channel.Channels;
@@ -531,12 +532,26 @@ public void replayRequest(final NettyResponseFuture<?> future, FilterContext fc,
531532
if (future.getAsyncHandler() instanceof AsyncHandlerExtensions)
532533
AsyncHandlerExtensions.class.cast(future.getAsyncHandler()).onRetry();
533534

534-
channelManager.drainChannel(channel, future);
535+
channelManager.drainChannelAndOffer(channel, future);
535536
sendNextRequest(newRequest, future);
536537
return;
537538
}
538539

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

providers/netty4/src/main/java/org/asynchttpclient/providers/netty4/channel/ChannelManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ public void call() {
424424
};
425425
}
426426

427-
public void drainChannel(final Channel channel, final NettyResponseFuture<?> future) {
427+
public void drainChannelAndOffer(final Channel channel, final NettyResponseFuture<?> future) {
428428
Channels.setAttribute(channel, newDrainCallback(future, channel, future.isKeepAlive(), getPartitionId(future)));
429429
}
430430

providers/netty4/src/main/java/org/asynchttpclient/providers/netty4/handler/HttpProtocol.java

Lines changed: 38 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,14 @@ private Realm.RealmBuilder newRealmBuilder(Realm realm) {
6868
return realm != null ? new Realm.RealmBuilder().clone(realm) : new Realm.RealmBuilder();
6969
}
7070

71-
private Realm kerberosChallenge(Channel channel, List<String> proxyAuth, Request request, ProxyServer proxyServer, FluentCaseInsensitiveStringsMap headers, Realm realm,
72-
NettyResponseFuture<?> future, boolean proxyInd) throws NTLMEngineException {
71+
private Realm kerberosChallenge(Channel channel,//
72+
List<String> proxyAuth,//
73+
Request request,//
74+
ProxyServer proxyServer,//
75+
FluentCaseInsensitiveStringsMap headers,//
76+
Realm realm,//
77+
NettyResponseFuture<?> future,//
78+
boolean proxyInd) throws NTLMEngineException {
7379

7480
Uri uri = request.getUri();
7581
String host = request.getVirtualHost() == null ? uri.getHost() : request.getVirtualHost();
@@ -103,8 +109,13 @@ private void addNTLMAuthorizationHeader(FluentCaseInsensitiveStringsMap headers,
103109
headers.add(authorizationHeaderName(proxyInd), "NTLM " + challengeHeader);
104110
}
105111

106-
private Realm ntlmChallenge(String authenticateHeader, Request request, ProxyServer proxyServer, FluentCaseInsensitiveStringsMap headers, Realm realm,
107-
NettyResponseFuture<?> future, boolean proxyInd) throws NTLMEngineException {
112+
private Realm ntlmChallenge(String authenticateHeader,//
113+
Request request,//
114+
ProxyServer proxyServer,//
115+
FluentCaseInsensitiveStringsMap headers,//
116+
Realm realm,//
117+
NettyResponseFuture<?> future,//
118+
boolean proxyInd) throws NTLMEngineException {
108119

109120
boolean useRealm = proxyServer == null && realm != null;
110121

@@ -137,8 +148,14 @@ private Realm ntlmChallenge(String authenticateHeader, Request request, ProxySer
137148
}
138149
}
139150

140-
private Realm ntlmProxyChallenge(String authenticateHeader, Request request, ProxyServer proxyServer, FluentCaseInsensitiveStringsMap headers, Realm realm,
141-
NettyResponseFuture<?> future, boolean proxyInd) throws NTLMEngineException {
151+
private Realm ntlmProxyChallenge(String authenticateHeader,//
152+
Request request,//
153+
ProxyServer proxyServer,//
154+
FluentCaseInsensitiveStringsMap headers,//
155+
Realm realm,//
156+
NettyResponseFuture<?> future,//
157+
boolean proxyInd) throws NTLMEngineException {
158+
142159
future.getAndSetAuth(false);
143160
headers.remove(HttpHeaders.Names.PROXY_AUTHORIZATION);
144161

@@ -165,7 +182,7 @@ private void finishUpdate(final NettyResponseFuture<?> future, Channel channel,
165182

166183
boolean keepAlive = future.isKeepAlive();
167184
if (expectOtherChunks && keepAlive)
168-
channelManager.drainChannel(channel, future);
185+
channelManager.drainChannelAndOffer(channel, future);
169186
else
170187
channelManager.tryToOfferChannelToPool(channel, keepAlive, channelManager.getPartitionId(future));
171188
markAsDone(future, channel);
@@ -215,17 +232,11 @@ private boolean exitAfterHandling401(//
215232
// NTLM
216233
newRealm = ntlmChallenge(ntlmAuthenticate, request, proxyServer, request.getHeaders(), realm, future, false);
217234

218-
// don't forget to reuse channel: NTLM authenticates a connection
219-
future.setReuseChannel(true);
220-
221235
} else if (negociate) {
222236
newRealm = kerberosChallenge(channel, wwwAuthHeaders, request, proxyServer, request.getHeaders(), realm, future, false);
223237
// SPNEGO KERBEROS
224238
if (newRealm == null)
225239
return true;
226-
else
227-
// don't forget to reuse channel: KERBEROS authenticates a connection
228-
future.setReuseChannel(true);
229240

230241
} else {
231242
newRealm = new Realm.RealmBuilder()//
@@ -242,20 +253,22 @@ private boolean exitAfterHandling401(//
242253
final Request nextRequest = new RequestBuilder(future.getRequest()).setHeaders(request.getHeaders()).setRealm(nr).build();
243254

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

260273
return true;
261274
}

providers/netty4/src/main/java/org/asynchttpclient/providers/netty4/request/NettyRequestSender.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
import org.asynchttpclient.filter.FilterException;
5151
import org.asynchttpclient.filter.IOExceptionFilter;
5252
import org.asynchttpclient.listener.TransferCompletionHandler;
53+
import org.asynchttpclient.providers.netty4.Callback;
5354
import org.asynchttpclient.providers.netty4.NettyAsyncHttpProviderConfig;
5455
import org.asynchttpclient.providers.netty4.channel.ChannelManager;
5556
import org.asynchttpclient.providers.netty4.channel.Channels;
@@ -521,11 +522,25 @@ public void replayRequest(final NettyResponseFuture<?> future, FilterContext fc,
521522
if (future.getAsyncHandler() instanceof AsyncHandlerExtensions)
522523
AsyncHandlerExtensions.class.cast(future.getAsyncHandler()).onRetry();
523524

524-
channelManager.drainChannel(channel, future);
525+
channelManager.drainChannelAndOffer(channel, future);
525526
sendNextRequest(newRequest, future);
526527
}
527528

528529
public boolean isClosed() {
529530
return closed.get();
530531
}
532+
533+
public final Callback newExecuteNextRequestCallback(final NettyResponseFuture<?> future, final Request nextRequest) {
534+
535+
return new Callback(future) {
536+
@Override
537+
public void call() throws IOException {
538+
sendNextRequest(nextRequest, future);
539+
}
540+
};
541+
}
542+
543+
public void drainChannelAndExecuteNextRequest(final Channel channel, final NettyResponseFuture<?> future, Request nextRequest) {
544+
Channels.setAttribute(channel, newExecuteNextRequestCallback(future, nextRequest));
545+
}
531546
}

0 commit comments

Comments
 (0)