Merged branch 'jetty-9.3.x' into 'master'.
This commit is contained in:
commit
bede684cac
|
@ -31,6 +31,7 @@ import org.eclipse.jetty.client.api.Request;
|
||||||
import org.eclipse.jetty.client.api.Response;
|
import org.eclipse.jetty.client.api.Response;
|
||||||
import org.eclipse.jetty.client.api.Result;
|
import org.eclipse.jetty.client.api.Result;
|
||||||
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
import org.eclipse.jetty.client.util.BufferingResponseListener;
|
||||||
|
import org.eclipse.jetty.http.HttpField;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
@ -40,7 +41,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 +64,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 +94,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 +149,16 @@ 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()
|
// Copy existing, explicitly set, authorization headers.
|
||||||
{
|
copyIfAbsent(request, newRequest, HttpHeader.AUTHORIZATION);
|
||||||
@Override
|
copyIfAbsent(request, newRequest, HttpHeader.PROXY_AUTHORIZATION);
|
||||||
public void onSuccess(Response response)
|
|
||||||
{
|
newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult))
|
||||||
client.getAuthenticationStore().addAuthenticationResult(authnResult);
|
.send(null);
|
||||||
}
|
|
||||||
}).send(null);
|
|
||||||
}
|
}
|
||||||
catch (Throwable x)
|
catch (Throwable x)
|
||||||
{
|
{
|
||||||
|
@ -167,6 +168,13 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void copyIfAbsent(HttpRequest oldRequest, Request newRequest, HttpHeader header)
|
||||||
|
{
|
||||||
|
HttpField field = oldRequest.getHeaders().getField(header);
|
||||||
|
if (field != null && !newRequest.getHeaders().contains(header))
|
||||||
|
newRequest.getHeaders().put(field);
|
||||||
|
}
|
||||||
|
|
||||||
private void forwardSuccessComplete(HttpRequest request, Response response)
|
private void forwardSuccessComplete(HttpRequest request, Response response)
|
||||||
{
|
{
|
||||||
HttpConversation conversation = request.getConversation();
|
HttpConversation conversation = request.getConversation();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,148 @@ 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());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProxyAuthenticationWithExplicitAuthorizationHeader() 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"));
|
||||||
|
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 + 204.
|
||||||
|
ContentResponse response1 = client.newRequest(serverHost, serverPort)
|
||||||
|
.scheme(scheme)
|
||||||
|
.header(HttpHeader.AUTHORIZATION, "Basic foobar")
|
||||||
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertEquals(status, response1.getStatus());
|
||||||
|
Assert.assertEquals(2, requests.get());
|
||||||
|
|
||||||
|
// Make again the request, authentication is cached, expect 204.
|
||||||
|
requests.set(0);
|
||||||
|
ContentResponse response2 = client.newRequest(serverHost, serverPort)
|
||||||
|
.scheme(scheme)
|
||||||
|
.header(HttpHeader.AUTHORIZATION, "Basic foobar")
|
||||||
|
.timeout(5, TimeUnit.SECONDS)
|
||||||
|
.send();
|
||||||
|
|
||||||
|
Assert.assertEquals(status, response2.getStatus());
|
||||||
|
Assert.assertEquals(1, requests.get());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,6 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.server.handler;
|
package org.eclipse.jetty.server.handler;
|
||||||
|
|
||||||
import static org.hamcrest.Matchers.is;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -38,7 +36,9 @@ import org.junit.BeforeClass;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@Ignore("Unfixed range bug")
|
import static org.hamcrest.Matchers.is;
|
||||||
|
|
||||||
|
@Ignore("Unfixed range bug - Issue #107")
|
||||||
public class ResourceHandlerRangeTest
|
public class ResourceHandlerRangeTest
|
||||||
{
|
{
|
||||||
private static Server server;
|
private static Server server;
|
||||||
|
|
Loading…
Reference in New Issue