diff --git a/jetty-proxy/pom.xml b/jetty-proxy/pom.xml
index e21421bb70e0..4cff18ace882 100644
--- a/jetty-proxy/pom.xml
+++ b/jetty-proxy/pom.xml
@@ -47,6 +47,12 @@
tests
test
+
+ org.eclipse.jetty
+ jetty-rewrite
+ ${project.version}
+ test
+
org.eclipse.jetty.toolchain
jetty-test-helper
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/BalancerServletTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/BalancerServletTest.java
index 06a89e7c7cb1..6941298ff09b 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/BalancerServletTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/BalancerServletTest.java
@@ -31,6 +31,8 @@
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.rewrite.handler.RewriteHandler;
+import org.eclipse.jetty.rewrite.handler.VirtualHostRuleContainer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.session.DefaultSessionIdManager;
@@ -40,6 +42,9 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class BalancerServletTest
@@ -111,12 +116,17 @@ private int getServerPort(Server server)
return server.getURI().getPort();
}
- protected byte[] sendRequestToBalancer(String path) throws Exception
+ protected ContentResponse getBalancedResponse(String path) throws Exception
{
- ContentResponse response = client.newRequest("localhost", getServerPort(balancer))
+ return client.newRequest("localhost", getServerPort(balancer))
.path(CONTEXT_PATH + SERVLET_PATH + path)
.timeout(5, TimeUnit.SECONDS)
.send();
+ }
+
+ protected byte[] sendRequestToBalancer(String path) throws Exception
+ {
+ ContentResponse response = getBalancedResponse(path);
return response.getContent();
}
@@ -160,12 +170,44 @@ public void testProxyPassReverse() throws Exception
assertEquals("success", msg);
}
+ @Test
+ public void testRewrittenBalancerWithEncodedURI() throws Exception
+ {
+ startBalancer(DumpServlet.class);
+ balancer.stop();
+ RewriteHandler rewrite = new RewriteHandler();
+ rewrite.setHandler(balancer.getHandler());
+ balancer.setHandler(rewrite);
+ rewrite.setRewriteRequestURI(true);
+ rewrite.addRule(new VirtualHostRuleContainer());
+ balancer.start();
+
+ ContentResponse response = getBalancedResponse("/test/%0A");
+ assertThat(response.getStatus(), is(200));
+ assertThat(response.getContentAsString(), containsString("requestURI='/context/mapping/test/%0A'"));
+ assertThat(response.getContentAsString(), containsString("servletPath='/mapping'"));
+ assertThat(response.getContentAsString(), containsString("pathInfo='/test/\n'"));
+ }
+
private String readFirstLine(byte[] responseBytes) throws IOException
{
BufferedReader reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(responseBytes)));
return reader.readLine();
}
+ public static final class DumpServlet extends HttpServlet
+ {
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ {
+ resp.setContentType("text/plain");
+ resp.getWriter().printf("requestURI='%s'%n", req.getRequestURI());
+ resp.getWriter().printf("servletPath='%s'%n", req.getServletPath());
+ resp.getWriter().printf("pathInfo='%s'%n", req.getPathInfo());
+ resp.getWriter().flush();
+ }
+ }
+
public static final class CounterServlet extends HttpServlet
{
private final AtomicInteger counter = new AtomicInteger();
diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
index c078fc135061..227db9815b5d 100644
--- a/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
+++ b/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java
@@ -120,7 +120,7 @@ private static StringBuilder encodePath(StringBuilder buf, String path, int offs
buf = new StringBuilder(path.length() * 2);
break loop;
default:
- if (c > 127)
+ if (c < 0x20 || c >= 0x7f)
{
bytes = path.getBytes(URIUtil.__CHARSET);
buf = new StringBuilder(path.length() * 2);
@@ -193,7 +193,7 @@ private static StringBuilder encodePath(StringBuilder buf, String path, int offs
continue;
default:
- if (c > 127)
+ if (c < 0x20 || c >= 0x7f)
{
bytes = path.getBytes(URIUtil.__CHARSET);
break loop;
@@ -261,7 +261,7 @@ private static StringBuilder encodePath(StringBuilder buf, String path, int offs
buf.append("%7D");
continue;
default:
- if (c < 0)
+ if (c < 0x20 || c >= 0x7f)
{
buf.append('%');
TypeUtil.toHex(c, buf);
diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java
index 13eb24dc6c61..fea195ab472b 100644
--- a/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java
+++ b/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java
@@ -69,6 +69,7 @@ public static Stream encodePathSource()
{
// @checkstyle-disable-check : AvoidEscapedUnicodeCharactersCheck
return Stream.of(
+ Arguments.of("/foo/\n/bar", "/foo/%0A/bar"),
Arguments.of("/foo%23+;,:=/b a r/?info ", "/foo%2523+%3B,:=/b%20a%20r/%3Finfo%20"),
Arguments.of("/context/'list'/\"me\"/;",
"/context/%27list%27/%22me%22/%3B%3Cscript%3Ewindow.alert(%27xss%27)%3B%3C/script%3E"),
@@ -732,4 +733,25 @@ public void testAddQueryParam(String param1, String param2, Matcher matc
{
assertThat(URIUtil.addQueries(param1, param2), matcher);
}
+
+ @Test
+ public void testEncodeDecodeVisibleOnly()
+ {
+ StringBuilder builder = new StringBuilder();
+ builder.append('/');
+ for (char i = 0; i < 0x7FFF; i++)
+ builder.append(i);
+ String path = builder.toString();
+ String encoded = URIUtil.encodePath(path);
+ // Check endoded is visible
+ for (char c : encoded.toCharArray())
+ {
+ assertTrue(c > 0x20 && c < 0x80);
+ assertFalse(Character.isWhitespace(c));
+ assertFalse(Character.isISOControl(c));
+ }
+ // check decode to original
+ String decoded = URIUtil.decodePath(encoded);
+ assertEquals(path, decoded);
+ }
}