Skip to content

Commit fd17c4e

Browse files
committed
Merge pull request 269.
1 parent 4c7de7a commit fd17c4e

File tree

3 files changed

+207
-32
lines changed

3 files changed

+207
-32
lines changed

api/src/main/java/com/ning/http/client/ProxyServer.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,13 @@ public String toString() {
5858
private String ntlmDomain = System.getProperty("http.auth.ntlm.domain", "");
5959

6060
private boolean isBasic = true;
61-
61+
62+
@Deprecated
6263
public boolean isBasic() {
6364
return isBasic;
6465
}
6566

67+
@Deprecated
6668
public void setBasic(boolean isBasic) {
6769
this.isBasic = isBasic;
6870
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.ning.http.client.providers.grizzly;
2+
3+
import org.ietf.jgss.GSSContext;
4+
import org.ietf.jgss.GSSException;
5+
import org.ietf.jgss.GSSManager;
6+
import org.ietf.jgss.GSSName;
7+
import org.ietf.jgss.Oid;
8+
9+
import com.ning.http.util.Base64;
10+
11+
public class GSSSPNEGOWrapper {
12+
13+
private static final String KERBEROS_OID = "1.2.840.113554.1.2.2";
14+
15+
static GSSManager getManager() {
16+
return GSSManager.getInstance();
17+
}
18+
19+
static byte[] generateGSSToken(
20+
final byte[] input, final Oid oid, final String authServer) throws GSSException {
21+
byte[] token = input;
22+
if (token == null) {
23+
token = new byte[0];
24+
}
25+
GSSManager manager = getManager();
26+
GSSName serverName = manager.createName("HTTP@" + authServer, GSSName.NT_HOSTBASED_SERVICE);
27+
GSSContext gssContext = manager.createContext(
28+
serverName.canonicalize(oid), oid, null, GSSContext.DEFAULT_LIFETIME);
29+
gssContext.requestMutualAuth(true);
30+
gssContext.requestCredDeleg(true);
31+
return gssContext.initSecContext(token, 0, token.length);
32+
}
33+
34+
public static String generateToken(String authServer)
35+
{
36+
String returnVal = "";
37+
Oid oid;
38+
try {
39+
oid = new Oid(KERBEROS_OID);
40+
byte[] token = GSSSPNEGOWrapper.generateGSSToken(null, oid, authServer);
41+
returnVal = new String(Base64.encode(token));
42+
} catch (GSSException e) {
43+
// TODO Auto-generated catch block
44+
e.printStackTrace();
45+
}
46+
47+
return returnVal;
48+
}
49+
}

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

Lines changed: 155 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012-2013 Sonatype, Inc. All rights reserved.
2+
* Copyright (c) 2012 Sonatype, Inc. All rights reserved.
33
*
44
* This program is licensed to you under the Apache License Version 2.0,
55
* and you may not use this file except in compliance with the Apache License Version 2.0.
@@ -39,6 +39,7 @@
3939
import com.ning.http.client.filter.FilterContext;
4040
import com.ning.http.client.filter.ResponseFilter;
4141
import com.ning.http.client.listener.TransferCompletionHandler;
42+
import com.ning.http.client.ntlm.NTLMEngine;
4243
import com.ning.http.client.websocket.WebSocket;
4344
import com.ning.http.client.websocket.WebSocketByteListener;
4445
import com.ning.http.client.websocket.WebSocketCloseCodeReasonListener;
@@ -172,8 +173,7 @@ public class GrizzlyAsyncHttpProvider implements AsyncHttpProvider {
172173
DelayedExecutor.Resolver<Connection> resolver;
173174
private DelayedExecutor timeoutExecutor;
174175

175-
176-
176+
private final static NTLMEngine ntlmEngine = new NTLMEngine();
177177

178178
// ------------------------------------------------------------ Constructors
179179

@@ -222,7 +222,9 @@ public void completed(final Connection c) {
222222
try {
223223
execute(c, request, handler, future);
224224
} catch (Exception e) {
225-
if (e instanceof IOException) {
225+
if (e instanceof RuntimeException) {
226+
failed(e);
227+
} else if (e instanceof IOException) {
226228
failed(e);
227229
}
228230
if (LOGGER.isWarnEnabled()) {
@@ -399,7 +401,7 @@ public void onTimeout(Connection connection) {
399401
}
400402
fcb.add(eventFilter);
401403
fcb.add(clientFilter);
402-
404+
403405
if (providerConfig != null) {
404406
final TransportCustomizer customizer = (TransportCustomizer)
405407
providerConfig.getProperty(TRANSPORT_CUSTOMIZER);
@@ -435,6 +437,7 @@ void touchConnection(final Connection c, final Request request) {
435437

436438
// --------------------------------------------------------- Private Methods
437439

440+
438441
private static boolean configSendFileSupport() {
439442

440443
return !((System.getProperty("os.name").equalsIgnoreCase("linux")
@@ -933,6 +936,18 @@ private void convertToUpgradeRequest(final HttpTransactionContext ctx) {
933936
ctx.requestUrl = sb.toString();
934937
}
935938

939+
940+
/* private ProxyServer getProxyServer(Request request) {
941+
942+
ProxyServer proxyServer = request.getProxyServer();
943+
if (proxyServer == null) {
944+
proxyServer = config.getProxyServer();
945+
}
946+
return proxyServer;
947+
948+
}*/
949+
950+
936951
private void addHeaders(final Request request,
937952
final HttpRequestPacket requestPacket,
938953
final ProxyServer proxy) throws IOException {
@@ -970,8 +985,9 @@ private void addHeaders(final Request request,
970985
requestPacket.setHeader(Header.ProxyConnection, "keep-alive");
971986
}
972987

973-
if (proxy.getPrincipal() != null && proxy.isBasic()) {
974-
requestPacket.setHeader(Header.ProxyAuthorization, AuthenticatorUtils.computeBasicAuthentication(proxy));
988+
if(null == requestPacket.getHeader(Header.ProxyAuthorization) )
989+
{
990+
requestPacket.setHeader(Header.ProxyAuthorization, AuthenticatorUtils.computeBasicAuthentication(proxy));
975991
}
976992

977993
}
@@ -1367,10 +1383,18 @@ protected void onHttpHeadersParsed(HttpHeader httpHeader,
13671383
protected boolean onHttpPacketParsed(HttpHeader httpHeader, FilterChainContext ctx) {
13681384

13691385
boolean result;
1370-
if (httpHeader.isSkipRemainder()) {
1371-
clearResponse(ctx.getConnection());
1372-
cleanup(ctx, provider);
1373-
return false;
1386+
final String proxy_auth = httpHeader.getHeader(Header.ProxyAuthenticate);
1387+
1388+
if (httpHeader.isSkipRemainder() ) {
1389+
if (!ProxyAuthorizationHandler.isNTLMSecondHandShake(proxy_auth)) {
1390+
clearResponse(ctx.getConnection());
1391+
cleanup(ctx, provider);
1392+
return false;
1393+
} else {
1394+
super.onHttpPacketParsed(httpHeader, ctx);
1395+
httpHeader.getProcessingState().setKeepAlive(true);
1396+
return false;
1397+
}
13741398
}
13751399

13761400
result = super.onHttpPacketParsed(httpHeader, ctx);
@@ -1453,6 +1477,14 @@ private static HttpTransactionContext cleanup(final FilterChainContext ctx,
14531477

14541478
}
14551479

1480+
1481+
private static URI getURI(String url) {
1482+
1483+
return AsyncHttpProviderUtils.createUri(url);
1484+
1485+
}
1486+
1487+
14561488
private static boolean redirectCountExceeded(final HttpTransactionContext context) {
14571489

14581490
return (context.redirectCount.get() > context.maxRedirectCount);
@@ -1590,7 +1622,7 @@ public boolean handleStatus(final HttpResponsePacket responsePacket,
15901622
String principal = proxyServer.getPrincipal();
15911623
String password = proxyServer.getPassword();
15921624
Realm realm = new Realm.RealmBuilder().setPrincipal(principal)
1593-
.setPassword(password)
1625+
.setPassword(password)
15941626
.setUri("/")
15951627
.setMethodName("CONNECT")
15961628
.setUsePreemptiveAuth(true)
@@ -1615,36 +1647,123 @@ public boolean handleStatus(final HttpResponsePacket responsePacket,
16151647
} catch (UnsupportedEncodingException e) {
16161648
throw new IllegalStateException("Unsupported encoding.", e);
16171649
}
1618-
} else {
1650+
}else if (proxy_auth.toLowerCase().startsWith("ntlm")) {
1651+
1652+
req.getHeaders().remove(Header.ProxyAuthenticate.toString());
1653+
req.getHeaders().remove(Header.ProxyAuthorization.toString());
1654+
1655+
String msg = null;
1656+
try {
1657+
1658+
if(isNTLMFirstHandShake(proxy_auth))
1659+
{
1660+
msg = ntlmEngine.generateType1Msg(proxyServer.getNtlmDomain(), "");
1661+
}else {
1662+
String serverChallenge = proxy_auth.trim().substring("NTLM ".length());
1663+
msg = ntlmEngine.generateType3Msg(principal, password, proxyServer.getNtlmDomain(), proxyServer.getHost(), serverChallenge);
1664+
}
1665+
1666+
req.getHeaders().add(Header.ProxyAuthorization.toString(), "NTLM " + msg);
1667+
} catch (Exception e1) {
1668+
e1.printStackTrace();
1669+
}
1670+
} else if (proxy_auth.toLowerCase().startsWith("negotiate")){
1671+
//this is for kerberos
1672+
req.getHeaders().remove(Header.ProxyAuthenticate.toString());
1673+
req.getHeaders().remove(Header.ProxyAuthorization.toString());
1674+
1675+
}else {
16191676
throw new IllegalStateException("Unsupported authorization method: " + proxy_auth);
16201677
}
16211678

16221679
final ConnectionManager m = httpTransactionContext.provider.connectionManager;
1680+
InvocationStatus tempInvocationStatus = InvocationStatus.STOP;
1681+
16231682
try {
1624-
final Connection c = m.obtainConnection(req,
1625-
httpTransactionContext.future);
1626-
final HttpTransactionContext newContext =
1627-
httpTransactionContext.copy();
1628-
httpTransactionContext.future = null;
1629-
httpTransactionContext.provider.setHttpTransactionContext(c, newContext);
1630-
newContext.invocationStatus = InvocationStatus.STOP;
1631-
try {
1632-
httpTransactionContext.provider.execute(c,
1633-
req,
1634-
httpTransactionContext.handler,
1635-
httpTransactionContext.future);
1636-
return false;
1637-
} catch (IOException ioe) {
1638-
newContext.abort(ioe);
1639-
return false;
1683+
1684+
if(isNTLMFirstHandShake(proxy_auth))
1685+
{
1686+
tempInvocationStatus = InvocationStatus.CONTINUE;
1687+
1688+
}
1689+
1690+
if(proxy_auth.toLowerCase().startsWith("negotiate"))
1691+
{
1692+
final Connection c = m.obtainConnection(req, httpTransactionContext.future);
1693+
final HttpTransactionContext newContext = httpTransactionContext.copy();
1694+
httpTransactionContext.future = null;
1695+
httpTransactionContext.provider.setHttpTransactionContext(c, newContext);
1696+
1697+
newContext.invocationStatus = tempInvocationStatus;
1698+
1699+
String challengeHeader = null;
1700+
String server = proxyServer.getHost();
1701+
1702+
challengeHeader = GSSSPNEGOWrapper.generateToken(server);
1703+
1704+
req.getHeaders().add(Header.ProxyAuthorization.toString(), "Negotiate " + challengeHeader);
1705+
1706+
1707+
return exceuteRequest(httpTransactionContext, req, c,
1708+
newContext);
1709+
}else if(isNTLMSecondHandShake(proxy_auth))
1710+
{
1711+
final Connection c = ctx.getConnection();
1712+
final HttpTransactionContext newContext = httpTransactionContext.copy();
1713+
1714+
httpTransactionContext.future = null;
1715+
httpTransactionContext.provider.setHttpTransactionContext(c, newContext);
1716+
1717+
newContext.invocationStatus = tempInvocationStatus;
1718+
httpTransactionContext.establishingTunnel = true;
1719+
1720+
return exceuteRequest(httpTransactionContext, req, c,
1721+
newContext);
1722+
1723+
}
1724+
else{
1725+
final Connection c = m.obtainConnection(req, httpTransactionContext.future);
1726+
final HttpTransactionContext newContext = httpTransactionContext.copy();
1727+
httpTransactionContext.future = null;
1728+
httpTransactionContext.provider.setHttpTransactionContext(c, newContext);
1729+
1730+
newContext.invocationStatus = tempInvocationStatus;
1731+
1732+
//NTLM needs the same connection to be used for exchange of tokens
1733+
return exceuteRequest(httpTransactionContext, req, c,
1734+
newContext);
16401735
}
16411736
} catch (Exception e) {
16421737
httpTransactionContext.abort(e);
16431738
}
1644-
httpTransactionContext.invocationStatus = InvocationStatus.STOP;
1739+
httpTransactionContext.invocationStatus = tempInvocationStatus;
16451740
return false;
16461741
}
16471742

1743+
private boolean exceuteRequest(
1744+
final HttpTransactionContext httpTransactionContext,
1745+
final Request req, final Connection c,
1746+
final HttpTransactionContext newContext) {
1747+
try {
1748+
httpTransactionContext.provider.execute(c,
1749+
req,
1750+
httpTransactionContext.handler,
1751+
httpTransactionContext.future);
1752+
return false;
1753+
} catch (IOException ioe) {
1754+
newContext.abort(ioe);
1755+
return false;
1756+
}
1757+
}
1758+
1759+
public static boolean isNTLMSecondHandShake(final String proxy_auth) {
1760+
return (proxy_auth.toLowerCase().startsWith("ntlm") && !proxy_auth.equalsIgnoreCase("ntlm"));
1761+
}
1762+
public static boolean isNTLMFirstHandShake(final String proxy_auth) {
1763+
return (proxy_auth.equalsIgnoreCase("ntlm"));
1764+
}
1765+
1766+
16481767
} // END AuthorizationHandler
16491768

16501769

@@ -2418,6 +2537,7 @@ Connection obtainConnection(final Request request,
24182537
final Connection c = obtainConnection0(request, requestFuture, requestFuture.getProxyServer());
24192538
markConnectionAsDoNotCache(c);
24202539
return c;
2540+
24212541
}
24222542

24232543
void doAsyncConnect(final Request request,
@@ -2472,6 +2592,7 @@ boolean returnConnection(final Request request, final Connection c) {
24722592
}
24732593
}
24742594
return result;
2595+
24752596
}
24762597

24772598

@@ -2948,4 +3069,7 @@ public int hashCode() {
29483069
}
29493070
} // END AHCWebSocketListenerAdapter
29503071

2951-
}
3072+
}
3073+
3074+
3075+

0 commit comments

Comments
 (0)