diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java index 9d7c297774e..860cdd9e26f 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java @@ -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) { diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java index aa50e988138..4c9d4ed3590 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java @@ -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 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 diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java index 4099974417c..85f2c30919d 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java @@ -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; + } } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java index 5f023a95d07..3d0ab9d849d 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/WWWAuthenticationProtocolHandler.java @@ -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; + } } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java index 8a8866c2d1e..2efc6c6bf25 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java @@ -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()); + } }