Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimone Bordet2015-06-05 10:01:23 +0000
committerSimone Bordet2015-06-05 10:01:23 +0000
commit9306477f5bdfad31a649f42206c6ecee46a6c3cd (patch)
tree4ef6ccab510f979c61f25a68d2a58bfad17fba82
parentbfe6c2638edaa97777d88d4b82cefe4f4821cb05 (diff)
downloadorg.eclipse.jetty.project-9306477f5bdfad31a649f42206c6ecee46a6c3cd.tar.gz
org.eclipse.jetty.project-9306477f5bdfad31a649f42206c6ecee46a6c3cd.tar.xz
org.eclipse.jetty.project-9306477f5bdfad31a649f42206c6ecee46a6c3cd.zip
469414 - Proxied redirects expose upstream server name.
Introduced "preserveHost" init-parameter similar to what Apache and Nginx have.
-rw-r--r--jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java37
-rw-r--r--jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java10
-rw-r--r--jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncProxyServlet.java9
-rw-r--r--jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java26
-rw-r--r--jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ReverseProxyTest.java70
5 files changed, 104 insertions, 48 deletions
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java
index 08506b989b..8f1a416c58 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java
@@ -33,6 +33,7 @@ import java.util.concurrent.TimeoutException;
import javax.servlet.AsyncContext;
import javax.servlet.ServletConfig;
+import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServlet;
@@ -50,6 +51,32 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
+/**
+ * <p>Abstract base class for proxy servlets.</p>
+ * <p>Forwards requests to another server either as a standard web reverse
+ * proxy or as a transparent reverse proxy (as defined by RFC 7230).</p>
+ * <p>To facilitate JMX monitoring, the {@link HttpClient} instance is set
+ * as ServletContext attribute, prefixed with this servlet's name and
+ * exposed by the mechanism provided by
+ * {@link ServletContext#setAttribute(String, Object)}.</p>
+ * <p>The following init parameters may be used to configure the servlet:</p>
+ * <ul>
+ * <li>preserveHost - the host header specified by the client is forwarded to the server</li>
+ * <li>hostHeader - forces the host header to a particular value</li>
+ * <li>viaHost - the name to use in the Via header: Via: http/1.1 &lt;viaHost&gt;</li>
+ * <li>whiteList - comma-separated list of allowed proxy hosts</li>
+ * <li>blackList - comma-separated list of forbidden proxy hosts</li>
+ * </ul>
+ * <p>In addition, see {@link #createHttpClient()} for init parameters
+ * used to configure the {@link HttpClient} instance.</p>
+ * <p>NOTE: By default the Host header sent to the server by this proxy
+ * servlet is the server's host name. However, this breaks redirects.
+ * Set {@code preserveHost} to {@code true} to make redirects working,
+ * although this may break server's virtual host selection.</p>
+ * <p>The default behavior of not preserving the Host header mimics
+ * the default behavior of Apache httpd and Nginx, which both have
+ * a way to be configured to preserve the Host header.</p>
+ */
public abstract class AbstractProxyServlet extends HttpServlet
{
protected static final Set<String> HOP_HEADERS;
@@ -71,6 +98,7 @@ public abstract class AbstractProxyServlet extends HttpServlet
private final Set<String> _whiteList = new HashSet<>();
private final Set<String> _blackList = new HashSet<>();
protected Logger _log;
+ private boolean _preserveHost;
private String _hostHeader;
private String _viaHost;
private HttpClient _client;
@@ -83,6 +111,8 @@ public abstract class AbstractProxyServlet extends HttpServlet
ServletConfig config = getServletConfig();
+ _preserveHost = Boolean.parseBoolean(config.getInitParameter("preserveHost"));
+
_hostHeader = config.getInitParameter("hostHeader");
_viaHost = config.getInitParameter("viaHost");
@@ -181,9 +211,8 @@ public abstract class AbstractProxyServlet extends HttpServlet
}
/**
- * Creates a {@link HttpClient} instance, configured with init parameters of this servlet.
- * <p/>
- * The init parameters used to configure the {@link HttpClient} instance are:
+ * <p>Creates a {@link HttpClient} instance, configured with init parameters of this servlet.</p>
+ * <p>The init parameters used to configure the {@link HttpClient} instance are:</p>
* <table>
* <thead>
* <tr>
@@ -408,7 +437,7 @@ public abstract class AbstractProxyServlet extends HttpServlet
String headerName = headerNames.nextElement();
String lowerHeaderName = headerName.toLowerCase(Locale.ENGLISH);
- if (HttpHeader.HOST.is(headerName))
+ if (HttpHeader.HOST.is(headerName) && !_preserveHost)
continue;
// Remove hop-by-hop headers.
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java
index f011bb2412..8f7dceb768 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncMiddleManServlet.java
@@ -55,7 +55,15 @@ import org.eclipse.jetty.util.CountingCallback;
import org.eclipse.jetty.util.IteratingCallback;
import org.eclipse.jetty.util.component.Destroyable;
-@SuppressWarnings("serial")
+/**
+ * <p>Servlet 3.1 asynchronous proxy servlet with capability
+ * to intercept and modify request/response content.</p>
+ * <p>Both the request processing and the I/O are asynchronous.</p>
+ *
+ * @see ProxyServlet
+ * @see AsyncProxyServlet
+ * @see ConnectHandler
+ */
public class AsyncMiddleManServlet extends AbstractProxyServlet
{
private static final String CLIENT_TRANSFORMER = AsyncMiddleManServlet.class.getName() + ".clientTransformer";
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncProxyServlet.java
index e3f83b128e..1ca252c706 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncProxyServlet.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AsyncProxyServlet.java
@@ -22,6 +22,7 @@ import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.channels.WritePendingException;
+
import javax.servlet.ReadListener;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
@@ -38,6 +39,14 @@ import org.eclipse.jetty.client.util.DeferredContentProvider;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.IteratingCallback;
+/**
+ * <p>Servlet 3.1 asynchronous proxy servlet.</p>
+ * <p>Both the request processing and the I/O are asynchronous.</p>
+ *
+ * @see ProxyServlet
+ * @see AsyncMiddleManServlet
+ * @see ConnectHandler
+ */
public class AsyncProxyServlet extends ProxyServlet
{
private static final String WRITE_LISTENER_ATTRIBUTE = AsyncProxyServlet.class.getName() + ".writeListener";
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
index bb30d8a361..d374c35763 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
@@ -23,14 +23,13 @@ import java.io.InputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;
+
import javax.servlet.AsyncContext;
import javax.servlet.ServletConfig;
-import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
-import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentProvider;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
@@ -40,26 +39,11 @@ import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.util.Callback;
/**
- * Asynchronous ProxyServlet.
- * <p/>
- * Forwards requests to another server either as a standard web reverse proxy
- * (as defined by RFC2616) or as a transparent reverse proxy.
- * <p/>
- * To facilitate JMX monitoring, the {@link HttpClient} instance is set as context attribute,
- * prefixed with the servlet's name and exposed by the mechanism provided by
- * {@link ServletContext#setAttribute(String, Object)}.
- * <p/>
- * The following init parameters may be used to configure the servlet:
- * <ul>
- * <li>hostHeader - forces the host header to a particular value</li>
- * <li>viaHost - the name to use in the Via header: Via: http/1.1 &lt;viaHost&gt;</li>
- * <li>whiteList - comma-separated list of allowed proxy hosts</li>
- * <li>blackList - comma-separated list of forbidden proxy hosts</li>
- * </ul>
- * <p/>
- * In addition, see {@link #createHttpClient()} for init parameters used to configure
- * the {@link HttpClient} instance.
+ * <p>Servlet 3.0 asynchronous proxy servlet.</p>
+ * <p>The request processing is asynchronous, but the I/O is blocking.</p>
*
+ * @see AsyncProxyServlet
+ * @see AsyncMiddleManServlet
* @see ConnectHandler
*/
public class ProxyServlet extends AbstractProxyServlet
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ReverseProxyTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ReverseProxyTest.java
index 57d85307e9..0c4261cab8 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ReverseProxyTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ReverseProxyTest.java
@@ -19,6 +19,9 @@
package org.eclipse.jetty.proxy;
import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
@@ -42,13 +45,27 @@ public class ReverseProxyTest
{
@Rule
public final TestTracker tracker = new TestTracker();
- private HttpClient client;
- private Server proxy;
- private ServerConnector proxyConnector;
private Server server;
private ServerConnector serverConnector;
+ private Server proxy;
+ private ServerConnector proxyConnector;
+ private HttpClient client;
+
+ private void startServer(HttpServlet servlet) throws Exception
+ {
+ server = new Server();
+
+ serverConnector = new ServerConnector(server);
+ server.addConnector(serverConnector);
- private void startProxy() throws Exception
+ ServletContextHandler appCtx = new ServletContextHandler(server, "/", true, false);
+ ServletHolder appServletHolder = new ServletHolder(servlet);
+ appCtx.addServlet(appServletHolder, "/*");
+
+ server.start();
+ }
+
+ private void startProxy(Map<String, String> params) throws Exception
{
proxy = new Server();
@@ -65,7 +82,7 @@ public class ReverseProxyTest
protected String rewriteTarget(HttpServletRequest clientRequest)
{
StringBuilder builder = new StringBuilder();
- builder.append(clientRequest.getScheme()).append("://localhost:");
+ builder.append(clientRequest.getScheme()).append("://127.0.0.1:");
builder.append(serverConnector.getLocalPort());
builder.append(clientRequest.getRequestURI());
String query = clientRequest.getQueryString();
@@ -74,25 +91,14 @@ public class ReverseProxyTest
return builder.toString();
}
});
+ if (params != null)
+ proxyServletHolder.setInitParameters(params);
+
proxyContext.addServlet(proxyServletHolder, "/*");
proxy.start();
}
- private void startServer(HttpServlet servlet) throws Exception
- {
- server = new Server();
-
- serverConnector = new ServerConnector(server);
- server.addConnector(serverConnector);
-
- ServletContextHandler appCtx = new ServletContextHandler(server, "/", true, false);
- ServletHolder appServletHolder = new ServletHolder(servlet);
- appCtx.addServlet(appServletHolder, "/*");
-
- server.start();
- }
-
private void startClient() throws Exception
{
client = new HttpClient();
@@ -108,17 +114,37 @@ public class ReverseProxyTest
}
@Test
- public void testClientHostHeaderUpdatedWhenSentToServer() throws Exception
+ public void testHostHeaderUpdatedWhenSentToServer() throws Exception
+ {
+ startServer(new HttpServlet()
+ {
+ @Override
+ protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
+ {
+ Assert.assertEquals("127.0.0.1", request.getServerName());
+ Assert.assertEquals(serverConnector.getLocalPort(), request.getServerPort());
+ }
+ });
+ startProxy(null);
+ startClient();
+
+ ContentResponse response = client.newRequest("localhost", proxyConnector.getLocalPort()).send();
+ Assert.assertEquals(200, response.getStatus());
+ }
+
+ @Test
+ public void testHostHeaderPreserved() throws Exception
{
startServer(new HttpServlet()
{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
- Assert.assertEquals(request.getServerPort(), serverConnector.getLocalPort());
+ Assert.assertEquals("localhost", request.getServerName());
+ Assert.assertEquals(proxyConnector.getLocalPort(), request.getServerPort());
}
});
- startProxy();
+ startProxy(new HashMap<String, String>() {{ put("preserveHost", "true"); }});
startClient();
ContentResponse response = client.newRequest("localhost", proxyConnector.getLocalPort()).send();

Back to the top