diff --git a/jetty-proxy/pom.xml b/jetty-proxy/pom.xml index 0910ea51418b..e85683e30e61 100644 --- a/jetty-proxy/pom.xml +++ b/jetty-proxy/pom.xml @@ -58,6 +58,12 @@ jetty-slf4j-impl 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 2d17ef65cefd..eef0e824aabc 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 @@ -26,6 +26,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; @@ -35,6 +37,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 @@ -106,12 +111,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(); } @@ -155,12 +165,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 1a8b65aad919..6230b27bc3b5 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 @@ -115,7 +115,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); @@ -188,7 +188,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; @@ -256,7 +256,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 32d1db7664b0..c69ecb574116 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 @@ -64,6 +64,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"), @@ -727,4 +728,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); + } }