Merged branch 'jetty-9.3.x' into 'master'.

This commit is contained in:
Simone Bordet 2016-03-10 22:32:24 +01:00
commit 14c985802e
18 changed files with 537 additions and 124 deletions

View File

@ -26,6 +26,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.jetty.client.api.Authentication;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
@ -117,12 +118,12 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
Authentication authentication = null;
Authentication.HeaderInfo headerInfo = null;
URI uri = getAuthenticationURI(request);
if (uri != null)
URI authURI = getAuthenticationURI(request);
if (authURI != null)
{
for (Authentication.HeaderInfo element : headerInfos)
{
authentication = client.getAuthenticationStore().findAuthentication(element.getType(), uri, element.getRealm());
authentication = client.getAuthenticationStore().findAuthentication(element.getType(), authURI, element.getRealm());
if (authentication != null)
{
headerInfo = element;
@ -151,14 +152,33 @@ public abstract class AuthenticationProtocolHandler implements ProtocolHandler
conversation.setAttribute(authenticationAttribute, true);
Request newRequest = client.copyRequest(request, request.getURI());
URI requestURI = request.getURI();
String path = null;
if (requestURI == null)
{
String uri = request.getScheme() + "://" + request.getHost();
int port = request.getPort();
if (port > 0)
uri += ":" + port;
requestURI = URI.create(uri);
path = request.getPath();
}
Request newRequest = client.copyRequest(request, requestURI);
if (path != null)
newRequest.path(path);
authnResult.apply(newRequest);
// Copy existing, explicitly set, authorization headers.
copyIfAbsent(request, newRequest, HttpHeader.AUTHORIZATION);
copyIfAbsent(request, newRequest, HttpHeader.PROXY_AUTHORIZATION);
newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult))
.send(null);
newRequest.onResponseSuccess(r -> client.getAuthenticationStore().addAuthenticationResult(authnResult));
Connection connection = (Connection)request.getAttributes().get(HttpRequest.CONNECTION_ATTRIBUTE);
if (connection != null)
connection.send(newRequest, null);
else
newRequest.send(null);
}
catch (Throwable x)
{

View File

@ -408,9 +408,9 @@ public class HttpClient extends ContainerLifeCycle
}
/**
* Creates a new request with the specified URI.
* Creates a new request with the specified absolute URI in string format.
*
* @param uri the URI to request
* @param uri the request absolute URI
* @return the request just created
*/
public Request newRequest(String uri)
@ -419,9 +419,9 @@ public class HttpClient extends ContainerLifeCycle
}
/**
* Creates a new request with the specified URI.
* Creates a new request with the specified absolute URI.
*
* @param uri the URI to request
* @param uri the request absolute URI
* @return the request just created
*/
public Request newRequest(URI uri)

View File

@ -35,7 +35,6 @@ import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -89,7 +88,6 @@ public abstract class HttpConnection implements Connection
protected void normalizeRequest(Request request)
{
String method = request.getMethod();
HttpVersion version = request.getVersion();
HttpFields headers = request.getHeaders();
ContentProvider content = request.getContent();
@ -102,9 +100,12 @@ public abstract class HttpConnection implements Connection
path = "/";
request.path(path);
}
if (proxy != null && !HttpMethod.CONNECT.is(method))
URI uri = request.getURI();
if (proxy != null && !HttpClient.isSchemeSecure(request.getScheme()) && uri != null)
{
path = request.getURI().toString();
path = uri.toString();
request.path(path);
}
@ -144,7 +145,6 @@ public abstract class HttpConnection implements Connection
CookieStore cookieStore = getHttpClient().getCookieStore();
if (cookieStore != null)
{
URI uri = request.getURI();
StringBuilder cookies = null;
if (uri != null)
cookies = convertCookies(cookieStore.get(uri), null);
@ -155,7 +155,7 @@ public abstract class HttpConnection implements Connection
// Authentication
applyAuthentication(request, proxy != null ? proxy.getURI() : null);
applyAuthentication(request, request.getURI());
applyAuthentication(request, uri);
}
private StringBuilder convertCookies(List<HttpCookie> cookies, StringBuilder builder)

View File

@ -77,6 +77,8 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
if (proxy != null)
{
connectionFactory = proxy.newClientConnectionFactory(connectionFactory);
if (proxy.isSecure())
connectionFactory = newSslClientConnectionFactory(connectionFactory);
}
else
{

View File

@ -63,7 +63,7 @@ public class HttpProxy extends ProxyConfiguration.Proxy
return URI.create(new Origin(scheme, getAddress()).asString());
}
public static class HttpProxyClientConnectionFactory implements ClientConnectionFactory
private static class HttpProxyClientConnectionFactory implements ClientConnectionFactory
{
private static final Logger LOG = Log.getLogger(HttpProxyClientConnectionFactory.class);
private final ClientConnectionFactory connectionFactory;
@ -140,13 +140,19 @@ public class HttpProxy extends ProxyConfiguration.Proxy
HttpClient httpClient = destination.getHttpClient();
long connectTimeout = httpClient.getConnectTimeout();
Request connect = httpClient.newRequest(proxyAddress.getHost(), proxyAddress.getPort())
.scheme(HttpScheme.HTTP.asString())
.method(HttpMethod.CONNECT)
.path(target)
.header(HttpHeader.HOST, target)
.idleTimeout(2 * connectTimeout, TimeUnit.MILLISECONDS)
.timeout(connectTimeout, TimeUnit.MILLISECONDS);
// In case the proxy replies with a 407, we want
// to use the same connection for resending the
// request (this time with the Proxy-Authorization
// header), so we save it as an attribute to be
// used to send the next request.
connect.attribute(HttpRequest.CONNECTION_ATTRIBUTE, connection);
connection.send(connect, result ->
{
if (result.isFailed())

View File

@ -184,7 +184,9 @@ public abstract class HttpReceiver
case SET_COOKIE:
case SET_COOKIE2:
{
storeCookie(exchange.getRequest().getURI(), field);
URI uri = exchange.getRequest().getURI();
if (uri != null)
storeCookie(uri, field);
break;
}
default:

View File

@ -229,7 +229,18 @@ public class HttpRedirector
private Request redirect(Request request, Response response, Response.CompleteListener listener, URI newURI)
{
if (!newURI.isAbsolute())
newURI = request.getURI().resolve(newURI);
{
URI requestURI = request.getURI();
if (requestURI == null)
{
String uri = request.getScheme() + "://" + request.getHost();
int port = request.getPort();
if (port > 0)
uri += ":" + port;
requestURI = URI.create(uri);
}
newURI = requestURI.resolve(newURI);
}
int status = response.getStatus();
switch (status)

View File

@ -59,6 +59,7 @@ import org.eclipse.jetty.util.Fields;
public class HttpRequest implements Request
{
private static final URI NULL_URI = URI.create("null:0");
static final String CONNECTION_ATTRIBUTE = HttpRequest.class.getName() + ".connection";
private final HttpFields headers = new HttpFields();
private final Fields params = new Fields(true);
@ -92,6 +93,7 @@ public class HttpRequest implements Request
path = uri.getRawPath();
query = uri.getRawQuery();
extractParams(query);
followRedirects(client.isFollowRedirects());
idleTimeout = client.getIdleTimeout();
HttpField acceptEncodingField = client.getAcceptEncodingField();
@ -170,8 +172,6 @@ public class HttpRequest implements Request
else
{
String rawPath = uri.getRawPath();
if (uri.isOpaque())
rawPath = path;
if (rawPath == null)
rawPath = "";
this.path = rawPath;
@ -788,7 +788,7 @@ public class HttpRequest implements Request
URI result = newURI(path);
if (result == null)
return NULL_URI;
if (!result.isAbsolute() && !result.isOpaque())
if (!result.isAbsolute())
result = URI.create(new Origin(getScheme(), getHost(), getPort()).asString() + path);
return result;
}
@ -797,12 +797,16 @@ public class HttpRequest implements Request
{
try
{
return new URI(uri);
// Handle specially the "OPTIONS *" case, since it is possible to create a URI from "*" (!).
if ("*".equals(uri))
return null;
URI result = new URI(uri);
return result.isOpaque() ? null : result;
}
catch (URISyntaxException x)
{
// The "path" of a HTTP request may not be a URI,
// for example for CONNECT 127.0.0.1:8080 or OPTIONS *.
// for example for CONNECT 127.0.0.1:8080.
return null;
}
}

View File

@ -208,7 +208,8 @@ public class DigestAuthentication extends AbstractAuthentication
String A1 = user + ":" + realm + ":" + password;
String hashA1 = toHexString(digester.digest(A1.getBytes(StandardCharsets.ISO_8859_1)));
String A2 = request.getMethod() + ":" + request.getURI();
URI uri = request.getURI();
String A2 = request.getMethod() + ":" + uri;
if ("auth-int".equals(qop))
A2 += ":" + toHexString(digester.digest(content));
String hashA2 = toHexString(digester.digest(A2.getBytes(StandardCharsets.ISO_8859_1)));
@ -237,7 +238,7 @@ public class DigestAuthentication extends AbstractAuthentication
if (opaque != null)
value.append(", opaque=\"").append(opaque).append("\"");
value.append(", algorithm=\"").append(algorithm).append("\"");
value.append(", uri=\"").append(request.getURI()).append("\"");
value.append(", uri=\"").append(uri).append("\"");
if (qop != null)
{
value.append(", qop=\"").append(qop).append("\"");

View File

@ -29,6 +29,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.Fields;
@ -472,4 +473,35 @@ public class HttpClientURITest extends AbstractHttpClientServerTest
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
@Test
public void testAsteriskFormTarget() throws Exception
{
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);
Assert.assertEquals("*", target);
Assert.assertEquals("*", request.getPathInfo());
}
});
Request request = client.newRequest("localhost", connector.getLocalPort())
.method(HttpMethod.OPTIONS)
.scheme(scheme)
.path("*")
.timeout(5, TimeUnit.SECONDS);
Assert.assertEquals("*", request.getPath());
Assert.assertNull(request.getQuery());
Fields params = request.getParams();
Assert.assertEquals(0, params.getSize());
Assert.assertNull(request.getURI());
ContentResponse response = request.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
}

View File

@ -64,9 +64,9 @@ public class HttpSenderOverFCGI extends HttpSender
// FastCGI headers based on the URI
URI uri = request.getURI();
String path = uri.getRawPath();
String path = uri == null ? request.getPath() : uri.getRawPath();
fcgiHeaders.put(FCGI.Headers.DOCUMENT_URI, path);
String query = uri.getRawQuery();
String query = uri == null ? null : uri.getRawQuery();
fcgiHeaders.put(FCGI.Headers.QUERY_STRING, query == null ? "" : query);
// FastCGI headers based on HTTP headers

View File

@ -188,8 +188,8 @@ public class FastCGIProxyServlet extends AsyncProxyServlet.Transparent
fastCGIHeaders.put(FCGI.Headers.HTTPS, "on");
URI proxyRequestURI = proxyRequest.getURI();
String rawPath = proxyRequestURI.getRawPath();
String rawQuery = proxyRequestURI.getRawQuery();
String rawPath = proxyRequestURI == null ? proxyRequest.getPath() : proxyRequestURI.getRawPath();
String rawQuery = proxyRequestURI == null ? null : proxyRequestURI.getRawQuery();
String requestURI = (String)proxyRequest.getAttributes().get(REQUEST_URI_ATTRIBUTE);
if (requestURI == null)

View File

@ -0,0 +1,221 @@
//
// ========================================================================
// Copyright (c) 1995-2016 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.proxy;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpProxy;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.AbstractConnectionFactory;
import org.eclipse.jetty.server.ConnectionFactory;
import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.TestTracker;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Utf8StringBuilder;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class ForwardProxyServerTest
{
@Parameterized.Parameters
public static Object[] parameters()
{
return new Object[]{null, newSslContextFactory()};
}
@Rule
public final TestTracker tracker = new TestTracker();
private final SslContextFactory serverSslContextFactory;
private Server server;
private ServerConnector serverConnector;
private Server proxy;
private ServerConnector proxyConnector;
public ForwardProxyServerTest(SslContextFactory serverSslContextFactory)
{
this.serverSslContextFactory = serverSslContextFactory;
}
protected void startServer(ConnectionFactory connectionFactory) throws Exception
{
QueuedThreadPool serverThreads = new QueuedThreadPool();
serverThreads.setName("server");
server = new Server(serverThreads);
serverConnector = new ServerConnector(server, serverSslContextFactory, connectionFactory);
server.addConnector(serverConnector);
server.start();
}
protected void startProxy() throws Exception
{
QueuedThreadPool proxyThreads = new QueuedThreadPool();
proxyThreads.setName("proxy");
proxy = new Server(proxyThreads);
proxyConnector = new ServerConnector(proxy);
proxy.addConnector(proxyConnector);
// Under Windows, it takes a while to detect that a connection
// attempt fails, so use an explicit timeout
ConnectHandler connectHandler = new ConnectHandler();
connectHandler.setConnectTimeout(1000);
proxy.setHandler(connectHandler);
ServletContextHandler proxyHandler = new ServletContextHandler(connectHandler, "/");
proxyHandler.addServlet(ProxyServlet.class, "/*");
proxy.start();
}
protected HttpProxy newHttpProxy()
{
return new HttpProxy("localhost", proxyConnector.getLocalPort());
}
private static SslContextFactory newSslContextFactory()
{
SslContextFactory sslContextFactory = new SslContextFactory();
String keyStorePath = MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath();
sslContextFactory.setKeyStorePath(keyStorePath);
sslContextFactory.setKeyStorePassword("storepwd");
sslContextFactory.setKeyManagerPassword("keypwd");
return sslContextFactory;
}
@After
public void stop() throws Exception
{
stopProxy();
stopServer();
}
protected void stopServer() throws Exception
{
if (server != null)
{
server.stop();
server.join();
}
}
protected void stopProxy() throws Exception
{
if (proxy != null)
{
proxy.stop();
proxy.join();
}
}
@Test
public void testRequestTarget() throws Exception
{
startServer(new AbstractConnectionFactory("http/1.1")
{
@Override
public Connection newConnection(Connector connector, EndPoint endPoint)
{
return new AbstractConnection(endPoint, connector.getExecutor())
{
@Override
public void onOpen()
{
super.onOpen();
fillInterested();
}
@Override
public void onFillable()
{
try
{
// When using TLS, multiple reads are required.
ByteBuffer buffer = BufferUtil.allocate(1024);
int filled = 0;
while (filled == 0)
filled = getEndPoint().fill(buffer);
Utf8StringBuilder builder = new Utf8StringBuilder();
builder.append(buffer);
String request = builder.toString();
// ProxyServlet will receive an absolute URI from
// the client, and convert it to a relative URI.
// The ConnectHandler won't modify what the client
// sent, which must be a relative URI.
Assert.assertThat(request.length(), Matchers.greaterThan(0));
if (serverSslContextFactory == null)
Assert.assertFalse(request.contains("http://"));
else
Assert.assertFalse(request.contains("https://"));
String response = "" +
"HTTP/1.1 200 OK\r\n" +
"Content-Length: 0\r\n" +
"\r\n";
getEndPoint().write(Callback.NOOP, ByteBuffer.wrap(response.getBytes(StandardCharsets.UTF_8)));
}
catch (Throwable x)
{
x.printStackTrace();
close();
}
}
};
}
});
startProxy();
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
httpClient.start();
try
{
ContentResponse response = httpClient.newRequest("localhost", serverConnector.getLocalPort())
.scheme(serverSslContextFactory == null ? "http" : "https")
.method(HttpMethod.GET)
.path("/test")
.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
finally
{
httpClient.stop();
}
}
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.proxy;
import java.io.IOException;
import java.net.ConnectException;
import java.net.Socket;
import java.net.URI;
import java.net.URLEncoder;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
@ -34,9 +35,11 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpProxy;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.util.BasicAuthentication;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader;
@ -62,37 +65,37 @@ import org.junit.Assume;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import static org.junit.Assert.assertEquals;
public class ProxyTunnellingTest
@RunWith(Parameterized.class)
public class ForwardProxyTLSServerTest
{
@Rule
public TestTracker tracker = new TestTracker();
@Parameterized.Parameters
public static Object[] parameters()
{
return new Object[]{null, newSslContextFactory()};
}
private SslContextFactory sslContextFactory;
@Rule
public final TestTracker tracker = new TestTracker();
private final SslContextFactory proxySslContextFactory;
private Server server;
private ServerConnector serverConnector;
private Server proxy;
private ServerConnector proxyConnector;
protected int proxyPort()
public ForwardProxyTLSServerTest(SslContextFactory proxySslContextFactory)
{
return proxyConnector.getLocalPort();
this.proxySslContextFactory = proxySslContextFactory;
}
protected void startSSLServer(Handler handler) throws Exception
protected void startTLSServer(Handler handler) throws Exception
{
sslContextFactory = new SslContextFactory();
String keyStorePath = MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath();
sslContextFactory.setKeyStorePath(keyStorePath);
sslContextFactory.setKeyStorePassword("storepwd");
sslContextFactory.setKeyManagerPassword("keypwd");
QueuedThreadPool serverThreads = new QueuedThreadPool();
serverThreads.setName("server");
server = new Server(serverThreads);
serverConnector = new ServerConnector(server, sslContextFactory);
serverConnector = new ServerConnector(server, newSslContextFactory());
server.addConnector(serverConnector);
server.setHandler(handler);
server.start();
@ -108,7 +111,7 @@ public class ProxyTunnellingTest
QueuedThreadPool proxyThreads = new QueuedThreadPool();
proxyThreads.setName("proxy");
proxy = new Server(proxyThreads);
proxyConnector = new ServerConnector(proxy);
proxyConnector = new ServerConnector(proxy, proxySslContextFactory);
proxy.addConnector(proxyConnector);
// Under Windows, it takes a while to detect that a connection
// attempt fails, so use an explicit timeout
@ -117,6 +120,21 @@ public class ProxyTunnellingTest
proxy.start();
}
protected HttpProxy newHttpProxy()
{
return new HttpProxy(new Origin.Address("localhost", proxyConnector.getLocalPort()), proxySslContextFactory != null);
}
private static SslContextFactory newSslContextFactory()
{
SslContextFactory sslContextFactory = new SslContextFactory();
String keyStorePath = MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath();
sslContextFactory.setKeyStorePath(keyStorePath);
sslContextFactory.setKeyStorePassword("storepwd");
sslContextFactory.setKeyManagerPassword("keypwd");
return sslContextFactory;
}
@After
public void stop() throws Exception
{
@ -142,19 +160,21 @@ public class ProxyTunnellingTest
}
}
@Test(timeout=60000)
public void testOneExchangeViaSSL() throws Exception
@Test
public void testOneExchange() throws Exception
{
startSSLServer(new ServerHandler());
startTLSServer(new ServerHandler());
startProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
httpClient.start();
try
{
// Use a numeric host to test the URI of the CONNECT request.
// URIs such as host:80 may interpret "host" as the scheme,
// but when the host is numeric it is not a valid URI.
String host = "127.0.0.1";
String body = "BODY";
ContentResponse response = httpClient.newRequest(host, serverConnector.getLocalPort())
@ -163,9 +183,9 @@ public class ProxyTunnellingTest
.path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
.send();
assertEquals(HttpStatus.OK_200, response.getStatus());
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
String content = response.getContentAsString();
assertEquals(body, content);
Assert.assertEquals(body, content);
}
finally
{
@ -173,14 +193,14 @@ public class ProxyTunnellingTest
}
}
@Test(timeout=60000)
public void testTwoExchangesViaSSL() throws Exception
@Test
public void testTwoExchanges() throws Exception
{
startSSLServer(new ServerHandler());
startTLSServer(new ServerHandler());
startProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
httpClient.start();
try
@ -192,9 +212,9 @@ public class ProxyTunnellingTest
.path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
.send();
assertEquals(HttpStatus.OK_200, response1.getStatus());
Assert.assertEquals(HttpStatus.OK_200, response1.getStatus());
String content = response1.getContentAsString();
assertEquals(body, content);
Assert.assertEquals(body, content);
content = "body=" + body;
ContentResponse response2 = httpClient.newRequest("localhost", serverConnector.getLocalPort())
@ -206,9 +226,9 @@ public class ProxyTunnellingTest
.content(new StringContentProvider(content))
.send();
assertEquals(HttpStatus.OK_200, response2.getStatus());
Assert.assertEquals(HttpStatus.OK_200, response2.getStatus());
content = response2.getContentAsString();
assertEquals(body, content);
Assert.assertEquals(body, content);
}
finally
{
@ -216,14 +236,14 @@ public class ProxyTunnellingTest
}
}
@Test(timeout=60000)
public void testTwoConcurrentExchangesViaSSL() throws Exception
@Test
public void testTwoConcurrentExchanges() throws Exception
{
startSSLServer(new ServerHandler());
startTLSServer(new ServerHandler());
startProxy();
final HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
final HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
httpClient.start();
try
@ -235,28 +255,24 @@ public class ProxyTunnellingTest
.scheme(HttpScheme.HTTPS.asString())
.method(HttpMethod.GET)
.path("/echo?body=" + URLEncoder.encode(body1, "UTF-8"))
.onRequestCommit(new org.eclipse.jetty.client.api.Request.CommitListener()
.onRequestCommit(request ->
{
@Override
public void onCommit(org.eclipse.jetty.client.api.Request request)
Destination destination = httpClient.getDestination(HttpScheme.HTTPS.asString(), "localhost", serverConnector.getLocalPort());
destination.newConnection(new Promise.Adapter<Connection>()
{
Destination destination = httpClient.getDestination(HttpScheme.HTTPS.asString(), "localhost", serverConnector.getLocalPort());
destination.newConnection(new Promise.Adapter<Connection>()
@Override
public void succeeded(Connection result)
{
@Override
public void succeeded(Connection result)
{
connection.set(result);
connectionLatch.countDown();
}
});
}
connection.set(result);
connectionLatch.countDown();
}
});
})
.send();
assertEquals(HttpStatus.OK_200, response1.getStatus());
Assert.assertEquals(HttpStatus.OK_200, response1.getStatus());
String content = response1.getContentAsString();
assertEquals(body1, content);
Assert.assertEquals(body1, content);
Assert.assertTrue(connectionLatch.await(5, TimeUnit.SECONDS));
@ -274,9 +290,9 @@ public class ProxyTunnellingTest
connection.get().send(request2, listener2);
ContentResponse response2 = listener2.get(5, TimeUnit.SECONDS);
assertEquals(HttpStatus.OK_200, response2.getStatus());
Assert.assertEquals(HttpStatus.OK_200, response2.getStatus());
String content2 = response1.getContentAsString();
assertEquals(body1, content2);
Assert.assertEquals(body1, content2);
}
finally
{
@ -284,13 +300,13 @@ public class ProxyTunnellingTest
}
}
@Test(timeout=60000)
@Test
public void testShortIdleTimeoutOverriddenByRequest() throws Exception
{
// Short idle timeout for HttpClient.
long idleTimeout = 500;
startSSLServer(new ServerHandler());
startTLSServer(new ServerHandler());
startProxy(new ConnectHandler()
{
@Override
@ -309,8 +325,8 @@ public class ProxyTunnellingTest
}
});
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
// Short idle timeout for HttpClient.
httpClient.setIdleTimeout(idleTimeout);
httpClient.start();
@ -327,9 +343,9 @@ public class ProxyTunnellingTest
.idleTimeout(10 * idleTimeout, TimeUnit.MILLISECONDS)
.send();
assertEquals(HttpStatus.OK_200, response.getStatus());
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
String content = response.getContentAsString();
assertEquals(body, content);
Assert.assertEquals(body, content);
}
finally
{
@ -337,16 +353,16 @@ public class ProxyTunnellingTest
}
}
@Test(timeout=60000)
@Test
public void testProxyDown() throws Exception
{
startSSLServer(new ServerHandler());
startTLSServer(new ServerHandler());
startProxy();
int proxyPort = proxyPort();
int proxyPort = proxyConnector.getLocalPort();
stopProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort));
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy(new Origin.Address("localhost", proxyPort), proxySslContextFactory != null));
httpClient.start();
try
@ -369,16 +385,16 @@ public class ProxyTunnellingTest
}
}
@Test(timeout=60000)
@Test
public void testServerDown() throws Exception
{
startSSLServer(new ServerHandler());
startTLSServer(new ServerHandler());
int serverPort = serverConnector.getLocalPort();
stopServer();
startProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
httpClient.start();
try
@ -401,10 +417,10 @@ public class ProxyTunnellingTest
}
}
@Test(timeout=60000)
@Test
public void testProxyClosesConnection() throws Exception
{
startSSLServer(new ServerHandler());
startTLSServer(new ServerHandler());
startProxy(new ConnectHandler()
{
@Override
@ -414,8 +430,8 @@ public class ProxyTunnellingTest
}
});
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
httpClient.start();
try
@ -435,7 +451,55 @@ public class ProxyTunnellingTest
}
}
@Test(timeout=60000)
@Test
public void testProxyAuthentication() throws Exception
{
startTLSServer(new ServerHandler());
String proxyRealm = "ProxyRealm";
startProxy(new ConnectHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
String proxyAuth = request.getHeader(HttpHeader.PROXY_AUTHORIZATION.asString());
if (proxyAuth == null)
{
baseRequest.setHandled(true);
response.setStatus(HttpStatus.PROXY_AUTHENTICATION_REQUIRED_407);
response.setHeader(HttpHeader.PROXY_AUTHENTICATE.asString(), "Basic realm=\"" + proxyRealm + "\"");
return;
}
super.handle(target, baseRequest, request, response);
}
});
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
URI proxyURI = URI.create("https://localhost:" + proxyConnector.getLocalPort());
httpClient.getAuthenticationStore().addAuthentication(new BasicAuthentication(proxyURI, proxyRealm, "proxyUser", "proxyPassword"));
httpClient.start();
try
{
String body = "BODY";
ContentResponse response = httpClient.newRequest("localhost", serverConnector.getLocalPort())
.scheme(HttpScheme.HTTPS.asString())
.method(HttpMethod.GET)
.path("/echo")
.param("body", body)
.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
String content = response.getContentAsString();
Assert.assertEquals(body, content);
}
finally
{
httpClient.stop();
}
}
@Test
@Ignore("External Proxy Server no longer stable enough for testing")
public void testExternalProxy() throws Exception
{
@ -454,7 +518,7 @@ public class ProxyTunnellingTest
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.start();
HttpClient httpClient = new HttpClient(sslContextFactory);
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
httpClient.start();
@ -464,7 +528,7 @@ public class ProxyTunnellingTest
// Use a longer timeout, sometimes the proxy takes a while to answer
.timeout(20, TimeUnit.SECONDS)
.send();
assertEquals(HttpStatus.OK_200, response.getStatus());
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
finally
{

View File

@ -440,7 +440,7 @@ public class Server extends HandlerWrapper implements Attributes
if (LOG.isDebugEnabled())
LOG.debug("doStop {}",this);
MultiException mex=new MultiException();
// list if graceful futures
@ -529,7 +529,8 @@ public class Server extends HandlerWrapper implements Attributes
final Response response=connection.getResponse();
if (LOG.isDebugEnabled())
LOG.debug("{} on {}{}",request.getDispatcherType(),connection,"\n"+request.getMethod()+" "+request.getHttpURI()+"\n"+request.getHttpFields());
LOG.debug("{} on {}{}{} {} {}{}{}", request.getDispatcherType(), connection, System.lineSeparator(),
request.getMethod(), target, request.getProtocol(), System.lineSeparator(), request.getHttpFields());
if (HttpMethod.OPTIONS.is(request.getMethod()) || "*".equals(target))
{
@ -543,7 +544,8 @@ public class Server extends HandlerWrapper implements Attributes
handle(target, request, request, response);
if (LOG.isDebugEnabled())
LOG.debug("RESPONSE for {} h={}{}",target,request.isHandled(),"\n"+response.getStatus()+" "+response.getReason()+"\n"+response.getHttpFields());
LOG.debug("RESPONSE for {} h={}{}{} {}{}{}", target, request.isHandled(), System.lineSeparator(),
response.getStatus(), response.getReason(), System.lineSeparator(), response.getHttpFields());
}
/* ------------------------------------------------------------ */

View File

@ -199,20 +199,14 @@ public abstract class AbstractTest
return http2Client;
}
protected String getScheme()
{
return isTransportSecure() ? "https" : "http";
}
protected String newURI()
{
switch (transport)
{
case HTTP:
case H2C:
case FCGI:
return "http://localhost:" + connector.getLocalPort();
case HTTPS:
case H2:
return "https://localhost:" + connector.getLocalPort();
default:
throw new IllegalArgumentException();
}
return getScheme() + "://localhost:" + connector.getLocalPort();
}
protected boolean isTransportSecure()

View File

@ -263,7 +263,7 @@ public class HttpClientLoadTest extends AbstractTest
int maxContentLength = 64 * 1024;
int contentLength = random.nextInt(maxContentLength) + 1;
test(ssl ? "https" : "http", host, method.asString(), clientClose, serverClose, contentLength, true, latch, failures);
test(getScheme(), host, method.asString(), clientClose, serverClose, contentLength, true, latch, failures);
}
private void test(String scheme, String host, String method, boolean clientClose, boolean serverClose, int contentLength, final boolean checkContentLength, final CountDownLatch latch, final List<String> failures)

View File

@ -316,6 +316,60 @@ public class HttpClientTest extends AbstractTest
.send();
}
@Test
public void testOPTIONS() throws Exception
{
start(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
Assert.assertTrue(HttpMethod.OPTIONS.is(request.getMethod()));
Assert.assertEquals("*", target);
Assert.assertEquals("*", request.getPathInfo());
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(getScheme())
.method(HttpMethod.OPTIONS)
.path("*")
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
@Test
public void testOPTIONSWithRelativeRedirect() throws Exception
{
start(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
baseRequest.setHandled(true);
if ("*".equals(target))
{
// Be nasty and send a relative redirect.
// Code 303 will change the method to GET.
response.setStatus(HttpStatus.SEE_OTHER_303);
response.setHeader("Location", "/");
}
}
});
ContentResponse response = client.newRequest("localhost", connector.getLocalPort())
.scheme(getScheme())
.method(HttpMethod.OPTIONS)
.path("*")
.timeout(5, TimeUnit.SECONDS)
.send();
Assert.assertEquals(HttpStatus.OK_200, response.getStatus());
}
private void sleep(long time) throws IOException
{
try