Merge branch 'master' into jetty-9.4.x-Feature
This commit is contained in:
commit
f08b84ba0e
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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("\"");
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -423,11 +423,15 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore
|
|||
DBObject o = _dbSessions.findOne(new BasicDBObject("id", id), fields);
|
||||
if (o != null)
|
||||
{
|
||||
Long currentMaxIdle = (Long)o.get(__MAX_IDLE);
|
||||
Long currentExpiry = (Long)o.get(__EXPIRY);
|
||||
if (currentMaxIdle != null && nsqd.getMaxInactiveMs() > 0 && nsqd.getMaxInactiveMs() < currentMaxIdle)
|
||||
Long tmpLong = (Long)o.get(__MAX_IDLE);
|
||||
long currentMaxIdle = (tmpLong == null? 0:tmpLong.longValue());
|
||||
tmpLong = (Long)o.get(__EXPIRY);
|
||||
long currentExpiry = (tmpLong == null? 0 : tmpLong.longValue());
|
||||
|
||||
if (currentMaxIdle != nsqd.getMaxInactiveMs())
|
||||
sets.put(__MAX_IDLE, nsqd.getMaxInactiveMs());
|
||||
if (currentExpiry != null && nsqd.getExpiry() > 0 && nsqd.getExpiry() != currentExpiry)
|
||||
|
||||
if (currentExpiry != nsqd.getExpiry())
|
||||
sets.put(__EXPIRY, nsqd.getExpiry());
|
||||
}
|
||||
else
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
{
|
|
@ -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());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -404,8 +404,10 @@ public class Session implements SessionManager.SessionIf
|
|||
try (Lock lock = _lock.lockIfNotHeld())
|
||||
{
|
||||
_sessionData.setMaxInactiveMs((long)secs*1000L);
|
||||
_sessionData.setExpiry(_sessionData.getMaxInactiveMs() <= 0 ? 0 : (System.currentTimeMillis() + _sessionData.getMaxInactiveMs()*1000L));
|
||||
_sessionData.setExpiry(_sessionData.getMaxInactiveMs() <= 0 ? 0 : (System.currentTimeMillis() + _sessionData.getMaxInactiveMs()));
|
||||
_sessionData.setDirty(true);
|
||||
if (secs <= 0)
|
||||
LOG.warn("Session {} is now immortal (maxInactiveInterval={})", _sessionData.getId(), secs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -76,6 +76,13 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
|
|||
/* ------------------------------------------------------------ */
|
||||
public final static int __distantFuture=60*60*24*7*52*20;
|
||||
|
||||
/**
|
||||
* Web.xml session-timeout is set in minutes, but is stored as an int in seconds by HttpSession and
|
||||
* the sessionmanager. Thus MAX_INT is the max number of seconds that can be set, and MAX_INT/60 is the
|
||||
* max number of minutes that you can set.
|
||||
*/
|
||||
public final static java.math.BigDecimal MAX_INACTIVE_MINUTES = new java.math.BigDecimal(Integer.MAX_VALUE/60);
|
||||
|
||||
static final HttpSessionContext __nullSessionContext=new HttpSessionContext()
|
||||
{
|
||||
@Override
|
||||
|
@ -656,6 +663,9 @@ public class SessionManager extends ContainerLifeCycle implements org.eclipse.je
|
|||
public void setMaxInactiveInterval(int seconds)
|
||||
{
|
||||
_dftMaxIdleSecs=seconds;
|
||||
if (_dftMaxIdleSecs < 0)
|
||||
LOG.warn("Sessions created by this manager are immortal (default maxInactiveInterval={})"+_dftMaxIdleSecs);
|
||||
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -649,9 +649,12 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
{
|
||||
XmlParser.Node tNode = node.get("session-timeout");
|
||||
if (tNode != null)
|
||||
{
|
||||
int timeout = Integer.parseInt(tNode.toString(false, true));
|
||||
context.getSessionHandler().getSessionManager().setMaxInactiveInterval(timeout * 60);
|
||||
{
|
||||
java.math.BigDecimal asDecimal = new java.math.BigDecimal(tNode.toString(false, true));
|
||||
if (asDecimal.compareTo(org.eclipse.jetty.server.session.SessionManager.MAX_INACTIVE_MINUTES) > 0)
|
||||
throw new IllegalStateException ("Max session-timeout in minutes is "+org.eclipse.jetty.server.session.SessionManager.MAX_INACTIVE_MINUTES);
|
||||
|
||||
context.getSessionHandler().getSessionManager().setMaxInactiveInterval(asDecimal.intValueExact() * 60);
|
||||
}
|
||||
|
||||
//Servlet Spec 3.0
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -18,12 +18,39 @@
|
|||
|
||||
package org.eclipse.jetty.nosql.mongodb;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.api.ContentResponse;
|
||||
import org.eclipse.jetty.client.api.Request;
|
||||
import org.eclipse.jetty.server.session.AbstractSessionExpiryTest;
|
||||
import org.eclipse.jetty.server.session.AbstractTestServer;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletHolder;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.DBCollection;
|
||||
import com.mongodb.DBObject;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
|
||||
|
||||
|
||||
|
||||
public class SessionExpiryTest extends AbstractSessionExpiryTest
|
||||
{
|
||||
|
||||
|
@ -58,4 +85,219 @@ public class SessionExpiryTest extends AbstractSessionExpiryTest
|
|||
{
|
||||
super.testSessionExpiry();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBigSessionExpiry() throws Exception
|
||||
{
|
||||
String contextPath = "";
|
||||
String servletMapping = "/server";
|
||||
int inactivePeriod = Integer.MAX_VALUE * 60; //integer overflow
|
||||
int scavengePeriod = 10;
|
||||
int inspectPeriod = 1;
|
||||
int idlePassivatePeriod = 0;
|
||||
AbstractTestServer server1 = createServer(0, inactivePeriod, scavengePeriod, inspectPeriod, idlePassivatePeriod);
|
||||
ChangeTimeoutServlet servlet = new ChangeTimeoutServlet();
|
||||
ServletHolder holder = new ServletHolder(servlet);
|
||||
ServletContextHandler context = server1.addContext(contextPath);
|
||||
context.addServlet(holder, servletMapping);
|
||||
TestHttpSessionListener listener = new TestHttpSessionListener();
|
||||
|
||||
context.getSessionHandler().addEventListener(listener);
|
||||
|
||||
server1.start();
|
||||
int port1 = server1.getPort();
|
||||
|
||||
try
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.start();
|
||||
String url = "http://localhost:" + port1 + contextPath + servletMapping;
|
||||
|
||||
//make a request to set up a session on the server
|
||||
ContentResponse response1 = client.GET(url + "?action=init");
|
||||
assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
|
||||
String sessionCookie = response1.getHeaders().get("Set-Cookie");
|
||||
assertTrue(sessionCookie != null);
|
||||
// Mangle the cookie, replacing Path with $Path, etc.
|
||||
sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
|
||||
|
||||
String sessionId = AbstractTestServer.extractSessionId(sessionCookie);
|
||||
|
||||
DBCollection sessions = ((MongoSessionIdManager)((MongoTestServer)server1).getServer().getSessionIdManager()).getSessions();
|
||||
verifySessionCreated(listener,sessionId);
|
||||
//verify that the session timeout is set in mongo
|
||||
verifySessionTimeout(sessions, sessionId, -1); //SessionManager sets -1 if maxInactive < 0
|
||||
|
||||
//get the session expiry time from mongo
|
||||
long expiry = getSessionExpiry(sessions, sessionId);
|
||||
assertEquals(0, expiry);
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
server1.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void changeSessionTimeout() throws Exception
|
||||
{
|
||||
String contextPath = "";
|
||||
String servletMapping = "/server";
|
||||
int inactivePeriod = 10;
|
||||
int scavengePeriod = 1;
|
||||
int inspectPeriod = 1;
|
||||
int idlePassivatePeriod = 0;
|
||||
AbstractTestServer server1 = createServer(0, inactivePeriod, scavengePeriod, inspectPeriod, idlePassivatePeriod);
|
||||
ChangeTimeoutServlet servlet = new ChangeTimeoutServlet();
|
||||
ServletHolder holder = new ServletHolder(servlet);
|
||||
ServletContextHandler context = server1.addContext(contextPath);
|
||||
context.addServlet(holder, servletMapping);
|
||||
TestHttpSessionListener listener = new TestHttpSessionListener();
|
||||
|
||||
context.getSessionHandler().addEventListener(listener);
|
||||
|
||||
server1.start();
|
||||
int port1 = server1.getPort();
|
||||
|
||||
try
|
||||
{
|
||||
HttpClient client = new HttpClient();
|
||||
client.start();
|
||||
String url = "http://localhost:" + port1 + contextPath + servletMapping;
|
||||
|
||||
//make a request to set up a session on the server
|
||||
ContentResponse response1 = client.GET(url + "?action=init");
|
||||
assertEquals(HttpServletResponse.SC_OK,response1.getStatus());
|
||||
String sessionCookie = response1.getHeaders().get("Set-Cookie");
|
||||
assertTrue(sessionCookie != null);
|
||||
// Mangle the cookie, replacing Path with $Path, etc.
|
||||
sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path=");
|
||||
|
||||
String sessionId = AbstractTestServer.extractSessionId(sessionCookie);
|
||||
|
||||
DBCollection sessions = ((MongoSessionIdManager)((MongoTestServer)server1).getServer().getSessionIdManager()).getSessions();
|
||||
verifySessionCreated(listener,sessionId);
|
||||
//verify that the session timeout is set in mongo
|
||||
verifySessionTimeout(sessions, sessionId, inactivePeriod);
|
||||
|
||||
//get the session expiry time from mongo
|
||||
long expiry = getSessionExpiry(sessions, sessionId);
|
||||
|
||||
//make another request to change the session timeout to a smaller value
|
||||
inactivePeriod = 5;
|
||||
Request request = client.newRequest(url + "?action=change&val="+inactivePeriod);
|
||||
request.getHeaders().add("Cookie", sessionCookie);
|
||||
ContentResponse response2 = request.send();
|
||||
assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
|
||||
|
||||
|
||||
//check the timeout in mongo
|
||||
verifySessionTimeout(sessions, sessionId, inactivePeriod);
|
||||
//check the session expiry time has decreased from previous value
|
||||
assertTrue(getSessionExpiry(sessions, sessionId)<expiry);
|
||||
expiry = getSessionExpiry(sessions, sessionId);
|
||||
|
||||
//increase the session timeout
|
||||
inactivePeriod = 20;
|
||||
request = client.newRequest(url + "?action=change&val="+inactivePeriod);
|
||||
request.getHeaders().add("Cookie", sessionCookie);
|
||||
response2 = request.send();
|
||||
assertEquals(HttpServletResponse.SC_OK,response2.getStatus());
|
||||
//verify that the session timeout is set in mongo
|
||||
verifySessionTimeout(sessions, sessionId, inactivePeriod);
|
||||
assertTrue(getSessionAccessed(sessions, sessionId)+ (1000L*inactivePeriod) == getSessionExpiry(sessions, sessionId));
|
||||
}
|
||||
finally
|
||||
{
|
||||
server1.stop();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void verifySessionTimeout (DBCollection sessions, String id, int sec) throws Exception
|
||||
{
|
||||
long val;
|
||||
|
||||
if (sec > 0)
|
||||
val = sec*1000L;
|
||||
else
|
||||
val = sec;
|
||||
|
||||
assertNotNull(sessions);
|
||||
assertNotNull(id);
|
||||
|
||||
DBObject o = sessions.findOne(new BasicDBObject(MongoSessionDataStore.__ID,id));
|
||||
assertNotNull(o);
|
||||
Long maxIdle = (Long)o.get(MongoSessionDataStore.__MAX_IDLE);
|
||||
assertNotNull(maxIdle);
|
||||
assertEquals(val, maxIdle.longValue());
|
||||
}
|
||||
|
||||
public long getSessionExpiry (DBCollection sessions, String id) throws Exception
|
||||
{
|
||||
assertNotNull(sessions);
|
||||
assertNotNull(id);
|
||||
|
||||
DBObject o = sessions.findOne(new BasicDBObject(MongoSessionDataStore.__ID,id));
|
||||
assertNotNull(o);
|
||||
Long expiry = (Long)o.get(MongoSessionDataStore.__EXPIRY);
|
||||
return (expiry == null? null : expiry.longValue());
|
||||
}
|
||||
|
||||
public long getSessionMaxInactiveInterval (DBCollection sessions, String id) throws Exception
|
||||
{
|
||||
assertNotNull(sessions);
|
||||
assertNotNull(id);
|
||||
|
||||
DBObject o = sessions.findOne(new BasicDBObject(MongoSessionDataStore.__ID,id));
|
||||
assertNotNull(o);
|
||||
Long inactiveInterval = (Long)o.get(MongoSessionDataStore.__MAX_IDLE);
|
||||
return (inactiveInterval == null? null : inactiveInterval.longValue());
|
||||
}
|
||||
|
||||
public long getSessionAccessed (DBCollection sessions, String id) throws Exception
|
||||
{
|
||||
assertNotNull(sessions);
|
||||
assertNotNull(id);
|
||||
|
||||
DBObject o = sessions.findOne(new BasicDBObject(MongoSessionDataStore.__ID,id));
|
||||
assertNotNull(o);
|
||||
Long accessed = (Long)o.get(MongoSessionDataStore.__ACCESSED);
|
||||
return (accessed == null? null : accessed.longValue());
|
||||
}
|
||||
|
||||
public void debugPrint (DBCollection sessions, String id) throws Exception
|
||||
{
|
||||
assertNotNull(sessions);
|
||||
assertNotNull(id);
|
||||
|
||||
DBObject o = sessions.findOne(new BasicDBObject(MongoSessionDataStore.__ID,id));
|
||||
assertNotNull(o);
|
||||
System.err.println(o);
|
||||
}
|
||||
|
||||
public static class ChangeTimeoutServlet extends HttpServlet
|
||||
{
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse httpServletResponse) throws ServletException, IOException
|
||||
{
|
||||
String action = request.getParameter("action");
|
||||
if ("init".equals(action))
|
||||
{
|
||||
HttpSession session = request.getSession(true);
|
||||
session.setAttribute("test", "test");
|
||||
}
|
||||
else if ("change".equals(action))
|
||||
{
|
||||
String tmp = request.getParameter("val");
|
||||
int val = (StringUtil.isBlank(tmp)?0:Integer.valueOf(tmp.trim()));
|
||||
HttpSession session = request.getSession(false);
|
||||
assertNotNull(session);
|
||||
session.setMaxInactiveInterval(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue