Issue #258 (Http request to origin server over https proxy contains absolute URL)
Fixed by sending the request target in origin-form (and not in absolute-form) when request is to a https server.
This commit is contained in:
parent
4039f00bda
commit
2af81781cd
|
@ -35,6 +35,7 @@ import org.eclipse.jetty.http.HttpField;
|
||||||
import org.eclipse.jetty.http.HttpFields;
|
import org.eclipse.jetty.http.HttpFields;
|
||||||
import org.eclipse.jetty.http.HttpHeader;
|
import org.eclipse.jetty.http.HttpHeader;
|
||||||
import org.eclipse.jetty.http.HttpHeaderValue;
|
import org.eclipse.jetty.http.HttpHeaderValue;
|
||||||
|
import org.eclipse.jetty.http.HttpScheme;
|
||||||
import org.eclipse.jetty.http.HttpVersion;
|
import org.eclipse.jetty.http.HttpVersion;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
@ -88,7 +89,6 @@ public abstract class HttpConnection implements Connection
|
||||||
|
|
||||||
protected void normalizeRequest(Request request)
|
protected void normalizeRequest(Request request)
|
||||||
{
|
{
|
||||||
String method = request.getMethod();
|
|
||||||
HttpVersion version = request.getVersion();
|
HttpVersion version = request.getVersion();
|
||||||
HttpFields headers = request.getHeaders();
|
HttpFields headers = request.getHeaders();
|
||||||
ContentProvider content = request.getContent();
|
ContentProvider content = request.getContent();
|
||||||
|
@ -104,7 +104,7 @@ public abstract class HttpConnection implements Connection
|
||||||
|
|
||||||
URI uri = request.getURI();
|
URI uri = request.getURI();
|
||||||
|
|
||||||
if (proxy != null && uri != null)
|
if (proxy != null && !HttpScheme.HTTPS.is(request.getScheme()) && uri != null)
|
||||||
{
|
{
|
||||||
path = uri.toString();
|
path = uri.toString();
|
||||||
request.path(path);
|
request.path(path);
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue