Issue #381 (HttpClient does not send the Authorization header with authenticating proxy)

Fixed by tracking correctly the conversation attributes for
authentication, and by applying both proxy authentication results and
server authentication results.
This commit is contained in:
Simone Bordet 2016-03-02 18:01:32 +01:00
parent 4e426e9be3
commit 6d9b36c8a4
5 changed files with 108 additions and 21 deletions

View File

@ -40,7 +40,6 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
public static final int DEFAULT_MAX_CONTENT_LENGTH = 16*1024; public static final int DEFAULT_MAX_CONTENT_LENGTH = 16*1024;
public static final Logger LOG = Log.getLogger(AuthenticationProtocolHandler.class); public static final Logger LOG = Log.getLogger(AuthenticationProtocolHandler.class);
private static final Pattern AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\"(.*)", Pattern.CASE_INSENSITIVE); private static final Pattern AUTHENTICATE_PATTERN = Pattern.compile("([^\\s]+)\\s+realm=\"([^\"]+)\"(.*)", Pattern.CASE_INSENSITIVE);
private static final String AUTHENTICATION_ATTRIBUTE = AuthenticationProtocolHandler.class.getName() + ".authentication";
private final HttpClient client; private final HttpClient client;
private final int maxContentLength; private final int maxContentLength;
@ -64,6 +63,8 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
protected abstract URI getAuthenticationURI(Request request); protected abstract URI getAuthenticationURI(Request request);
protected abstract String getAuthenticationAttribute();
@Override @Override
public Response.Listener getResponseListener() public Response.Listener getResponseListener()
{ {
@ -92,8 +93,9 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
return; return;
} }
String authenticationAttribute = getAuthenticationAttribute();
HttpConversation conversation = request.getConversation(); HttpConversation conversation = request.getConversation();
if (conversation.getAttribute(AUTHENTICATION_ATTRIBUTE) != null) if (conversation.getAttribute(authenticationAttribute) != null)
{ {
// We have already tried to authenticate, but we failed again // We have already tried to authenticate, but we failed again
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
@ -146,18 +148,12 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
return; return;
} }
conversation.setAttribute(AUTHENTICATION_ATTRIBUTE, true); conversation.setAttribute(authenticationAttribute, true);
Request newRequest = client.copyRequest(request, request.getURI()); Request newRequest = client.copyRequest(request, request.getURI());
authnResult.apply(newRequest); authnResult.apply(newRequest);
newRequest.onResponseSuccess(new Response.SuccessListener() newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult))
{ .send(null);
@Override
public void onSuccess(Response response)
{
client.getAuthenticationStore().addAuthenticationResult(authnResult);
}
}).send(null);
} }
catch (Throwable x) catch (Throwable x)
{ {

View File

@ -153,14 +153,9 @@ public abstract class HttpConnection implements Connection
request.header(HttpHeader.COOKIE.asString(), cookies.toString()); request.header(HttpHeader.COOKIE.asString(), cookies.toString());
} }
// Authorization // Authentication
URI authenticationURI = proxy != null ? proxy.getURI() : request.getURI(); applyAuthentication(request, proxy != null ? proxy.getURI() : null);
if (authenticationURI != null) applyAuthentication(request, request.getURI());
{
Authentication.Result authnResult = getHttpClient().getAuthenticationStore().findAuthenticationResult(authenticationURI);
if (authnResult != null)
authnResult.apply(request);
}
} }
private StringBuilder convertCookies(List<HttpCookie> cookies, StringBuilder builder) private StringBuilder convertCookies(List<HttpCookie> cookies, StringBuilder builder)
@ -177,6 +172,16 @@ public abstract class HttpConnection implements Connection
return builder; return builder;
} }
private void applyAuthentication(Request request, URI uri)
{
if (uri != null)
{
Authentication.Result result = getHttpClient().getAuthenticationStore().findAuthenticationResult(uri);
if (result != null)
result.apply(request);
}
}
protected SendFailure send(HttpChannel channel, HttpExchange exchange) protected SendFailure send(HttpChannel channel, HttpExchange exchange)
{ {
// Forbid idle timeouts for the time window where // Forbid idle timeouts for the time window where

View File

@ -34,6 +34,7 @@ import org.eclipse.jetty.http.HttpStatus;
public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHandler public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHandler
{ {
public static final String NAME = "proxy-authenticate"; public static final String NAME = "proxy-authenticate";
private static final String ATTRIBUTE = ProxyAuthenticationProtocolHandler.class.getName() + ".attribute";
public ProxyAuthenticationProtocolHandler(HttpClient client) public ProxyAuthenticationProtocolHandler(HttpClient client)
{ {
@ -76,4 +77,10 @@ public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHa
ProxyConfiguration.Proxy proxy = destination.getProxy(); ProxyConfiguration.Proxy proxy = destination.getProxy();
return proxy != null ? proxy.getURI() : request.getURI(); return proxy != null ? proxy.getURI() : request.getURI();
} }
@Override
protected String getAuthenticationAttribute()
{
return ATTRIBUTE;
}
} }

View File

@ -34,6 +34,7 @@ import org.eclipse.jetty.http.HttpStatus;
public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHandler public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHandler
{ {
public static final String NAME = "www-authenticate"; public static final String NAME = "www-authenticate";
private static final String ATTRIBUTE = WWWAuthenticationProtocolHandler.class.getName() + ".attribute";
public WWWAuthenticationProtocolHandler(HttpClient client) public WWWAuthenticationProtocolHandler(HttpClient client)
{ {
@ -74,4 +75,10 @@ public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHand
{ {
return request.getURI(); return request.getURI();
} }
@Override
protected String getAuthenticationAttribute()
{
return ATTRIBUTE;
}
} }

View File

@ -81,7 +81,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
} }
@Test @Test
public void testAuthenticatedProxiedRequest() throws Exception public void testProxyAuthentication() throws Exception
{ {
final String user = "foo"; final String user = "foo";
final String password = "bar"; final String password = "bar";
@ -160,7 +160,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
} }
@Test @Test
public void testAuthenticatedProxiedRequestWithRedirect() throws Exception public void testProxyAuthenticationWithRedirect() throws Exception
{ {
String user = "foo"; String user = "foo";
String password = "bar"; String password = "bar";
@ -254,4 +254,76 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
Assert.assertEquals(status, response3.getStatus()); Assert.assertEquals(status, response3.getStatus());
Assert.assertEquals(1, requests.get()); Assert.assertEquals(1, requests.get());
} }
@Test
public void testProxyAuthenticationWithServerAuthentication() throws Exception
{
String proxyRealm = "proxyRealm";
String serverRealm = "serverRealm";
int status = HttpStatus.NO_CONTENT_204;
start(new AbstractHandler()
{
@Override
public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
String authorization = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString());
if (authorization == null)
{
response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407);
response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + proxyRealm + "\"");
}
else
{
authorization = request.getHeader(HttpHeader.AUTHORIZATION.asString());
if (authorization == null)
{
response.setStatus(HttpStatus.UNAUTHORIZED_401);
response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), "Basic realm=\"" + serverRealm + "\"");
}
else
{
response.setStatus(status);
}
}
}
});
String proxyHost = "localhost";
int proxyPort = connector.getLocalPort();
String serverHost = "server";
int serverPort = proxyPort + 1;
URI proxyURI = URI.create(scheme + "://" + proxyHost + ":" + proxyPort);
client.getAuthenticationStore().addAuthentication(new BasicAuthentication(proxyURI, proxyRealm, "proxyUser", "proxyPassword"));
URI serverURI = URI.create(scheme + "://" + serverHost + ":" + serverPort);
client.getAuthenticationStore().addAuthentication(new BasicAuthentication(serverURI, serverRealm, "serverUser", "serverPassword"));
client.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
final AtomicInteger requests = new AtomicInteger();
client.getRequestListeners().add(new Request.Listener.Adapter()
{
@Override
public void onSuccess(Request request)
{
requests.incrementAndGet();
}
});
// Make a request, expect 407 + 401 + 204.
ContentResponse response1 = client.newRequest(serverHost, serverPort)
.scheme(scheme)
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(status, response1.getStatus());
Assert.assertEquals(3, requests.get());
// Make again the request, authentication is cached, expect 204.
requests.set(0);
ContentResponse response2 = client.newRequest(serverHost, serverPort)
.scheme(scheme)
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(status, response2.getStatus());
Assert.assertEquals(1, requests.get());
}
} }