Skip to content

Commit 16a03a4

Browse files
jfarcandrlubke
authored andcommitted
Add NTLM support, patch provider by Giovanni Mels
1 parent 97f38e9 commit 16a03a4

File tree

2 files changed

+82
-8
lines changed

2 files changed

+82
-8
lines changed

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

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,11 @@ private static HttpRequest construct(AsyncHttpClientConfig config,
554554
if (config.isCompressionEnabled()) {
555555
nettyRequest.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP);
556556
}
557+
} else {
558+
List<String> auth = request.getHeaders().get(HttpHeaders.Names.PROXY_AUTHORIZATION);
559+
if (auth != null && auth.size() > 0 && auth.get(0).startsWith("NTLM")) {
560+
nettyRequest.addHeader(HttpHeaders.Names.PROXY_AUTHORIZATION, auth.get(0));
561+
}
557562
}
558563
ProxyServer proxyServer = request.getProxyServer() != null ? request.getProxyServer() : config.getProxyServer();
559564
Realm realm = request.getRealm() != null ? request.getRealm() : config.getRealm();
@@ -626,8 +631,24 @@ private static HttpRequest construct(AsyncHttpClientConfig config,
626631
}
627632

628633
if (proxyServer.getPrincipal() != null) {
629-
nettyRequest.setHeader(HttpHeaders.Names.PROXY_AUTHORIZATION,
634+
if (proxyServer.getNtlmDomain() != null) {
635+
636+
List<String> auth = request.getHeaders().get(HttpHeaders.Names.PROXY_AUTHORIZATION);
637+
if (!(auth != null && auth.size() > 0 && auth.get(0).startsWith("NTLM"))) {
638+
try {
639+
String msg = ntlmEngine.generateType1Msg(proxyServer.getNtlmDomain(),
640+
proxyServer.getHost());
641+
nettyRequest.setHeader(HttpHeaders.Names.PROXY_AUTHORIZATION, "NTLM " + msg);
642+
} catch (NTLMEngineException e) {
643+
IOException ie = new IOException();
644+
ie.initCause(e);
645+
throw ie;
646+
}
647+
}
648+
} else {
649+
nettyRequest.setHeader(HttpHeaders.Names.PROXY_AUTHORIZATION,
630650
AuthenticatorUtils.computeBasicAuthentication(proxyServer));
651+
}
631652
}
632653
}
633654

@@ -824,11 +845,12 @@ private <T> ListenableFuture<T> doConnect(final Request request, final AsyncHand
824845

825846
boolean useSSl = uri.getScheme().compareToIgnoreCase(HTTPS) == 0 && proxyServer == null;
826847
if (channel != null && channel.isOpen() && channel.isConnected()) {
827-
HttpRequest nettyRequest = buildRequest(config, request, uri, false, bufferedBytes);
848+
HttpRequest nettyRequest = buildRequest(config, request, uri, f == null ? false : f.isConnectAllowed(), bufferedBytes);
828849

829850
if (f == null) {
830851
f = newFuture(uri, request, asyncHandler, nettyRequest, config, this);
831852
} else {
853+
nettyRequest = buildRequest(config, request, uri, f.isConnectAllowed(), bufferedBytes);
832854
f.setNettyRequest(nettyRequest);
833855
}
834856
f.setState(NettyResponseFuture.STATE.POOLED);
@@ -1159,8 +1181,9 @@ public Object call() throws Exception {
11591181
log.debug("Sending proxy authentication to {}", request.getUrl());
11601182

11611183
future.setState(NettyResponseFuture.STATE.NEW);
1162-
if (!proxyAuth.contains("Kerberos") && (proxyAuth.contains("NTLM") || (proxyAuth.contains("Negotiate")))) {
1163-
newRealm = ntlmChallenge(proxyAuth, request, proxyServer, headers, realm, future);
1184+
1185+
if (!proxyAuth.contains("Kerberos") && (proxyAuth.get(0).contains("NTLM") || (proxyAuth.contains("Negotiate")))) {
1186+
newRealm = ntlmProxyChallenge(proxyAuth, request, proxyServer, headers, realm, future);
11641187
// SPNEGO KERBEROS
11651188
} else if (proxyAuth.contains("Negotiate")) {
11661189
newRealm = kerberosChallenge(proxyAuth, request, proxyServer, headers, realm, future);
@@ -1169,7 +1192,10 @@ public Object call() throws Exception {
11691192
newRealm = future.getRequest().getRealm();
11701193
}
11711194

1172-
nextRequest(builder.setHeaders(headers).setRealm(newRealm).build(), future);
1195+
Request req = builder.setHeaders(headers).setRealm(newRealm).build();
1196+
future.setReuseChannel(true);
1197+
future.setConnectAllowed(true);
1198+
nextRequest(req, future);
11731199
return;
11741200
}
11751201

@@ -1188,7 +1214,10 @@ public Object call() throws Exception {
11881214
} catch (Throwable ex) {
11891215
abort(future, ex);
11901216
}
1191-
nextRequest(builder.build(), future);
1217+
Request req = builder.build();
1218+
future.setReuseChannel(true);
1219+
future.setConnectAllowed(false);
1220+
nextRequest(req, future);
11921221
return;
11931222
}
11941223

@@ -1380,10 +1409,43 @@ private Realm ntlmChallenge(List<String> wwwAuth,
13801409
realmBuilder = new Realm.RealmBuilder();
13811410
}
13821411
newRealm = realmBuilder.setScheme(realm.getAuthScheme())
1412+
.setUri(URI.create(request.getUrl()).getPath())
1413+
.setMethodName(request.getMethod())
1414+
.build();
1415+
}
1416+
1417+
return newRealm;
1418+
}
1419+
1420+
private Realm ntlmProxyChallenge(List<String> wwwAuth,
1421+
Request request,
1422+
ProxyServer proxyServer,
1423+
FluentCaseInsensitiveStringsMap headers,
1424+
Realm realm,
1425+
NettyResponseFuture<?> future) throws NTLMEngineException {
1426+
future.getAndSetAuth(false);
1427+
headers.remove(HttpHeaders.Names.PROXY_AUTHORIZATION);
1428+
1429+
if (wwwAuth.get(0).startsWith("NTLM ")) {
1430+
String serverChallenge = wwwAuth.get(0).trim().substring("NTLM ".length());
1431+
String challengeHeader = ntlmEngine.generateType3Msg(proxyServer.getPrincipal(),
1432+
proxyServer.getPassword(),
1433+
proxyServer.getNtlmDomain(),
1434+
proxyServer.getHost(),
1435+
serverChallenge);
1436+
headers.add(HttpHeaders.Names.PROXY_AUTHORIZATION, "NTLM " + challengeHeader);
1437+
}
1438+
Realm newRealm;
1439+
Realm.RealmBuilder realmBuilder;
1440+
if (realm != null) {
1441+
realmBuilder = new Realm.RealmBuilder().clone(realm);
1442+
} else {
1443+
realmBuilder = new Realm.RealmBuilder();
1444+
}
1445+
newRealm = realmBuilder//.setScheme(realm.getAuthScheme())
13831446
.setUri(URI.create(request.getUrl()).getPath())
13841447
.setMethodName(request.getMethod())
13851448
.build();
1386-
}
13871449

13881450
return newRealm;
13891451
}

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

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ enum STATE {
8282
private boolean writeHeaders;
8383
private boolean writeBody;
8484
private final AtomicBoolean throwableCalled = new AtomicBoolean(false);
85+
private boolean allowConnect = false;
8586

8687
public NettyResponseFuture(URI uri,
8788
Request request,
@@ -405,6 +406,18 @@ protected void attachChannel(Channel channel) {
405406
this.channel = channel;
406407
}
407408

409+
public void setReuseChannel(boolean reuseChannel) {
410+
this.reuseChannel = reuseChannel;
411+
}
412+
413+
public boolean isConnectAllowed() {
414+
return allowConnect;
415+
}
416+
417+
public void setConnectAllowed(boolean allowConnect) {
418+
this.allowConnect = allowConnect;
419+
}
420+
408421
protected void attachChannel(Channel channel, boolean reuseChannel) {
409422
this.channel = channel;
410423
this.reuseChannel = reuseChannel;
@@ -429,7 +442,6 @@ public void setRequest(Request request) {
429442
this.request = request;
430443
}
431444

432-
433445
/**
434446
* Return true if the {@link Future} cannot be recovered. There is some scenario where a connection can be
435447
* closed by an unexpected IOException, and in some situation we can recover from that exception.

0 commit comments

Comments
 (0)