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:
parent
4e426e9be3
commit
6d9b36c8a4
|
@ -40,7 +40,6 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
public static final int DEFAULT_MAX_CONTENT_LENGTH = 16*1024;
|
||||
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 String AUTHENTICATION_ATTRIBUTE = AuthenticationProtocolHandler.class.getName() + ".authentication";
|
||||
|
||||
private final HttpClient client;
|
||||
private final int maxContentLength;
|
||||
|
@ -64,6 +63,8 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
|
||||
protected abstract URI getAuthenticationURI(Request request);
|
||||
|
||||
protected abstract String getAuthenticationAttribute();
|
||||
|
||||
@Override
|
||||
public Response.Listener getResponseListener()
|
||||
{
|
||||
|
@ -92,8 +93,9 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
return;
|
||||
}
|
||||
|
||||
String authenticationAttribute = getAuthenticationAttribute();
|
||||
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
|
||||
if (LOG.isDebugEnabled())
|
||||
|
@ -146,18 +148,12 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
|||
return;
|
||||
}
|
||||
|
||||
conversation.setAttribute(AUTHENTICATION_ATTRIBUTE, true);
|
||||
conversation.setAttribute(authenticationAttribute, true);
|
||||
|
||||
Request newRequest = client.copyRequest(request, request.getURI());
|
||||
authnResult.apply(newRequest);
|
||||
newRequest.onResponseSuccess(new Response.SuccessListener()
|
||||
{
|
||||
@Override
|
||||
public void onSuccess(Response response)
|
||||
{
|
||||
client.getAuthenticationStore().addAuthenticationResult(authnResult);
|
||||
}
|
||||
}).send(null);
|
||||
newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult))
|
||||
.send(null);
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
|
|
|
@ -153,14 +153,9 @@ public abstract class HttpConnection implements Connection
|
|||
request.header(HttpHeader.COOKIE.asString(), cookies.toString());
|
||||
}
|
||||
|
||||
// Authorization
|
||||
URI authenticationURI = proxy != null ? proxy.getURI() : request.getURI();
|
||||
if (authenticationURI != null)
|
||||
{
|
||||
Authentication.Result authnResult = getHttpClient().getAuthenticationStore().findAuthenticationResult(authenticationURI);
|
||||
if (authnResult != null)
|
||||
authnResult.apply(request);
|
||||
}
|
||||
// Authentication
|
||||
applyAuthentication(request, proxy != null ? proxy.getURI() : null);
|
||||
applyAuthentication(request, request.getURI());
|
||||
}
|
||||
|
||||
private StringBuilder convertCookies(List<HttpCookie> cookies, StringBuilder builder)
|
||||
|
@ -177,6 +172,16 @@ public abstract class HttpConnection implements Connection
|
|||
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)
|
||||
{
|
||||
// Forbid idle timeouts for the time window where
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.eclipse.jetty.http.HttpStatus;
|
|||
public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHandler
|
||||
{
|
||||
public static final String NAME = "proxy-authenticate";
|
||||
private static final String ATTRIBUTE = ProxyAuthenticationProtocolHandler.class.getName() + ".attribute";
|
||||
|
||||
public ProxyAuthenticationProtocolHandler(HttpClient client)
|
||||
{
|
||||
|
@ -76,4 +77,10 @@ public class ProxyAuthenticationProtocolHandler extends AuthenticationProtocolHa
|
|||
ProxyConfiguration.Proxy proxy = destination.getProxy();
|
||||
return proxy != null ? proxy.getURI() : request.getURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAuthenticationAttribute()
|
||||
{
|
||||
return ATTRIBUTE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,6 +34,7 @@ import org.eclipse.jetty.http.HttpStatus;
|
|||
public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHandler
|
||||
{
|
||||
public static final String NAME = "www-authenticate";
|
||||
private static final String ATTRIBUTE = WWWAuthenticationProtocolHandler.class.getName() + ".attribute";
|
||||
|
||||
public WWWAuthenticationProtocolHandler(HttpClient client)
|
||||
{
|
||||
|
@ -74,4 +75,10 @@ public class WWWAuthenticationProtocolHandler extends AuthenticationProtocolHand
|
|||
{
|
||||
return request.getURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getAuthenticationAttribute()
|
||||
{
|
||||
return ATTRIBUTE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testAuthenticatedProxiedRequest() throws Exception
|
||||
public void testProxyAuthentication() throws Exception
|
||||
{
|
||||
final String user = "foo";
|
||||
final String password = "bar";
|
||||
|
@ -160,7 +160,7 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testAuthenticatedProxiedRequestWithRedirect() throws Exception
|
||||
public void testProxyAuthenticationWithRedirect() throws Exception
|
||||
{
|
||||
String user = "foo";
|
||||
String password = "bar";
|
||||
|
@ -254,4 +254,76 @@ public class HttpClientProxyTest extends AbstractHttpClientServerTest
|
|||
Assert.assertEquals(status, response3.getStatus());
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue