From 3b92e9c62126d2ce372d6085610cc3004a6b35c8 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Thu, 9 Jan 2025 15:32:22 +1100 Subject: [PATCH] Issue #12683 Fix cross context dispatch to root context. --- .../servlet/CrossContextServletContext.java | 2 +- .../servlet/CrossContextDispatcherTest.java | 102 +++++++++++++++++- .../nested/CrossContextServletContext.java | 2 +- .../servlet/CrossContextDispatcherTest.java | 101 ++++++++++++++++- .../src/test/resources/docroot/empty | 0 5 files changed, 197 insertions(+), 10 deletions(-) create mode 100644 jetty-ee9/jetty-ee9-servlet/src/test/resources/docroot/empty diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/CrossContextServletContext.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/CrossContextServletContext.java index f83f4c6b49ff..78db368d1f34 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/CrossContextServletContext.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/CrossContextServletContext.java @@ -141,7 +141,7 @@ public RequestDispatcher getRequestDispatcher(String uriInContext) if (StringUtil.isEmpty(encodedPathInContext)) return null; - if (!StringUtil.isEmpty(contextPath)) + if (!StringUtil.isEmpty(contextPath) && !contextPath.equals("/")) { uri.path(URIUtil.addPaths(contextPath, uri.getPath())); encodedPathInContext = uri.getCanonicalPath().substring(contextPath.length()); diff --git a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/CrossContextDispatcherTest.java b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/CrossContextDispatcherTest.java index 3dc4cb685d68..7ebd4b6f73d1 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/CrossContextDispatcherTest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/CrossContextDispatcherTest.java @@ -115,8 +115,8 @@ public class CrossContextDispatcherTest private Server _server; private LocalConnector _connector; private ServletContextHandler _contextHandler; - private ServletContextHandler _targetServletContextHandler; + private ServletContextHandler _rootContextHandler; @BeforeEach public void init() throws Exception @@ -145,6 +145,13 @@ public void init() throws Exception resourceContextHandler.setHandler(resourceHandler); resourceContextHandler.setCrossContextDispatchSupported(true); contextCollection.addHandler(resourceContextHandler); + + _rootContextHandler = new ServletContextHandler(); + _rootContextHandler.setContextPath("/"); + _rootContextHandler.setBaseResourceAsPath(MavenTestingUtils.getTestResourcePathDir("docroot")); + _rootContextHandler.setCrossContextDispatchSupported(true); + contextCollection.addHandler(_rootContextHandler); + _server.setHandler(contextCollection); _server.addConnector(_connector); @@ -158,6 +165,91 @@ public void destroy() throws Exception _server.join(); } + @Test + public void testForwardToRoot() throws Exception + { + _rootContextHandler.addServlet(VerifyForwardServlet.class, "/verify/*"); + _contextHandler.addServlet(CrossContextDispatchServlet.class, "/dispatch/*"); + + String rawResponse = _connector.getResponse(""" + GET /context/dispatch/?forward=/verify&ctx=/ HTTP/1.1\r + Host: localhost\r + Connection: close\r + \r + """); + + HttpTester.Response response = HttpTester.parseResponse(rawResponse); + + String content = response.getContent(); + String[] contentLines = content.split("\\n"); + + //verify forward attributes + assertThat(content, containsString("Verified!")); + assertThat(content, containsString("jakarta.servlet.forward.context_path=/context")); + assertThat(content, containsString("jakarta.servlet.forward.servlet_path=/dispatch")); + assertThat(content, containsString("jakarta.servlet.forward.path_info=/")); + + String forwardMapping = extractLine(contentLines, "jakarta.servlet.forward.mapping="); + assertNotNull(forwardMapping); + assertThat(forwardMapping, containsString("CrossContextDispatchServlet")); + assertThat(content, containsString("jakarta.servlet.forward.query_string=forward=/verify&ctx=/")); + assertThat(content, containsString("jakarta.servlet.forward.request_uri=/context/dispatch/")); + //verify request values + assertThat(content, containsString("REQUEST_URL=http://localhost/")); + assertThat(content, containsString("CONTEXT_PATH=")); + assertThat(content, containsString("SERVLET_PATH=/verify")); + assertThat(content, containsString("PATH_INFO=/pinfo")); + String mapping = extractLine(contentLines, "MAPPING="); + assertNotNull(mapping); + assertThat(mapping, containsString("VerifyForwardServlet")); + String params = extractLine(contentLines, "PARAMS="); + assertNotNull(params); + params = params.substring(params.indexOf("=") + 1); + params = params.substring(1, params.length() - 1); //dump leading, trailing [ ] + assertThat(Arrays.asList(StringUtil.csvSplit(params)), containsInAnyOrder("a", "forward", "ctx")); + assertThat(content, containsString("REQUEST_URI=/verify/pinfo")); + } + + @Test + public void testIncludeToRoot() throws Exception + { + _rootContextHandler.addServlet(VerifyIncludeServlet.class, "/verify/*"); + _contextHandler.addServlet(CrossContextDispatchServlet.class, "/dispatch/*"); + + String rawResponse = _connector.getResponse(""" + GET /context/dispatch/?include=/verify&ctx=/ HTTP/1.1\r + Host: localhost\r + Connection: close\r + \r + """); + + HttpTester.Response response = HttpTester.parseResponse(rawResponse); + String content = response.getContent(); + String[] contentLines = content.split("\\n"); + + //verify include attributes + assertThat(content, containsString("Verified!")); + assertThat(content, containsString("jakarta.servlet.include.context_path=/")); + assertThat(content, containsString("jakarta.servlet.include.servlet_path=/verify")); + assertThat(content, containsString("jakarta.servlet.include.path_info=/pinfo")); + String includeMapping = extractLine(contentLines, "jakarta.servlet.include.mapping="); + assertThat(includeMapping, containsString("VerifyIncludeServlet")); + assertThat(content, containsString("jakarta.servlet.include.request_uri=/verify/pinfo")); + //verify request values + assertThat(content, containsString("CONTEXT_PATH=/context")); + assertThat(content, containsString("SERVLET_PATH=/dispatch")); + assertThat(content, containsString("PATH_INFO=/")); + String mapping = extractLine(contentLines, "MAPPING="); + assertThat(mapping, containsString("CrossContextDispatchServlet")); + assertThat(content, containsString("QUERY_STRING=include=/verify")); + assertThat(content, containsString("REQUEST_URI=/context/dispatch/")); + String params = extractLine(contentLines, "PARAMS="); + assertNotNull(params); + params = params.substring(params.indexOf("=") + 1); + params = params.substring(1, params.length() - 1); //dump leading, trailing [ ] + assertThat(Arrays.asList(StringUtil.csvSplit(params)), containsInAnyOrder("a", "include", "ctx")); + } + @Test public void testSimpleCrossContextForward() throws Exception { @@ -732,10 +824,12 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { RequestDispatcher dispatcher; - + String ctx = request.getParameter("ctx"); + if (StringUtil.isBlank(ctx)) + ctx = "/foreign"; if (request.getParameter("forward") != null) { - ServletContext foreign = getServletContext().getContext("/foreign"); + ServletContext foreign = getServletContext().getContext(ctx); assertNotNull(foreign); dispatcher = foreign.getRequestDispatcher(request.getParameter("forward") + "/pinfo?a=b"); @@ -746,7 +840,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t } else if (request.getParameter("include") != null) { - ServletContext foreign = getServletContext().getContext("/foreign"); + ServletContext foreign = getServletContext().getContext(ctx); assertNotNull(foreign); dispatcher = foreign.getRequestDispatcher(request.getParameter("include") + "/pinfo?a=b"); diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/CrossContextServletContext.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/CrossContextServletContext.java index aaa6981de4bf..da8359144fc9 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/CrossContextServletContext.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/CrossContextServletContext.java @@ -169,7 +169,7 @@ public RequestDispatcher getRequestDispatcher(String uriInContext) if (StringUtil.isEmpty(encodedPathInContext)) return null; - if (!StringUtil.isEmpty(contextPath)) + if (!StringUtil.isEmpty(contextPath) && !contextPath.equals("/")) { uri.path(URIUtil.addPaths(contextPath, uri.getPath())); encodedPathInContext = uri.getCanonicalPath().substring(contextPath.length()); diff --git a/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/CrossContextDispatcherTest.java b/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/CrossContextDispatcherTest.java index 50379d428506..6155caa40930 100644 --- a/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/CrossContextDispatcherTest.java +++ b/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/CrossContextDispatcherTest.java @@ -116,8 +116,8 @@ public class CrossContextDispatcherTest private Server _server; private LocalConnector _connector; private ServletContextHandler _contextHandler; - private ServletContextHandler _targetServletContextHandler; + private ServletContextHandler _rootContextHandler; @BeforeEach public void init() throws Exception @@ -140,6 +140,12 @@ public void init() throws Exception _targetServletContextHandler.setCrossContextDispatchSupported(true); contextCollection.addHandler(_targetServletContextHandler); + _rootContextHandler = new ServletContextHandler(); + _rootContextHandler.setContextPath("/"); + _rootContextHandler.setBaseResource(ResourceFactory.root().newResource(MavenPaths.findTestResourceDir("docroot"))); + _rootContextHandler.setCrossContextDispatchSupported(true); + contextCollection.addHandler(_rootContextHandler); + ResourceHandler resourceHandler = new ResourceHandler(); resourceHandler.setBaseResource(ResourceFactory.root().newResource(MavenPaths.findTestResourceDir("dispatchResourceTest"))); ContextHandler resourceContextHandler = new ContextHandler("/resource"); @@ -159,6 +165,91 @@ public void destroy() throws Exception _server.join(); } + @Test + public void testForwardToRoot() throws Exception + { + _rootContextHandler.addServlet(VerifyForwardServlet.class, "/verify/*"); + _contextHandler.addServlet(CrossContextDispatchServlet.class, "/dispatch/*"); + + String rawResponse = _connector.getResponse(""" + GET /context/dispatch/?forward=/verify&ctx=/ HTTP/1.1\r + Host: localhost\r + Connection: close\r + \r + """); + + HttpTester.Response response = HttpTester.parseResponse(rawResponse); + + String content = response.getContent(); + String[] contentLines = content.split("\\n"); + + //verify forward attributes + assertThat(content, containsString("Verified!")); + assertThat(content, containsString("jakarta.servlet.forward.context_path=/context")); + assertThat(content, containsString("jakarta.servlet.forward.servlet_path=/dispatch")); + assertThat(content, containsString("jakarta.servlet.forward.path_info=/")); + + String forwardMapping = extractLine(contentLines, "jakarta.servlet.forward.mapping="); + assertNotNull(forwardMapping); + assertThat(forwardMapping, containsString("CrossContextDispatchServlet")); + assertThat(content, containsString("jakarta.servlet.forward.query_string=forward=/verify&ctx=/")); + assertThat(content, containsString("jakarta.servlet.forward.request_uri=/context/dispatch/")); + //verify request values + assertThat(content, containsString("REQUEST_URL=http://localhost/")); + assertThat(content, containsString("CONTEXT_PATH=")); + assertThat(content, containsString("SERVLET_PATH=/verify")); + assertThat(content, containsString("PATH_INFO=/pinfo")); + String mapping = extractLine(contentLines, "MAPPING="); + assertNotNull(mapping); + assertThat(mapping, containsString("VerifyForwardServlet")); + String params = extractLine(contentLines, "PARAMS="); + assertNotNull(params); + params = params.substring(params.indexOf("=") + 1); + params = params.substring(1, params.length() - 1); //dump leading, trailing [ ] + assertThat(Arrays.asList(StringUtil.csvSplit(params)), containsInAnyOrder("a", "forward", "ctx")); + assertThat(content, containsString("REQUEST_URI=/verify/pinfo")); + } + + @Test + public void testIncludeToRoot() throws Exception + { + _rootContextHandler.addServlet(VerifyIncludeServlet.class, "/verify/*"); + _contextHandler.addServlet(CrossContextDispatchServlet.class, "/dispatch/*"); + + String rawResponse = _connector.getResponse(""" + GET /context/dispatch/?include=/verify&ctx=/ HTTP/1.1\r + Host: localhost\r + Connection: close\r + \r + """); + + HttpTester.Response response = HttpTester.parseResponse(rawResponse); + String content = response.getContent(); + String[] contentLines = content.split("\\n"); + + //verify include attributes + assertThat(content, containsString("Verified!")); + assertThat(content, containsString("jakarta.servlet.include.context_path=")); + assertThat(content, containsString("jakarta.servlet.include.servlet_path=/verify")); + assertThat(content, containsString("jakarta.servlet.include.path_info=/pinfo")); + String includeMapping = extractLine(contentLines, "jakarta.servlet.include.mapping="); + assertThat(includeMapping, containsString("VerifyIncludeServlet")); + assertThat(content, containsString("jakarta.servlet.include.request_uri=/verify/pinfo")); + //verify request values + assertThat(content, containsString("CONTEXT_PATH=/context")); + assertThat(content, containsString("SERVLET_PATH=/dispatch")); + assertThat(content, containsString("PATH_INFO=/")); + String mapping = extractLine(contentLines, "MAPPING="); + assertThat(mapping, containsString("CrossContextDispatchServlet")); + assertThat(content, containsString("QUERY_STRING=include=/verify")); + assertThat(content, containsString("REQUEST_URI=/context/dispatch/")); + String params = extractLine(contentLines, "PARAMS="); + assertNotNull(params); + params = params.substring(params.indexOf("=") + 1); + params = params.substring(1, params.length() - 1); //dump leading, trailing [ ] + assertThat(Arrays.asList(StringUtil.csvSplit(params)), containsInAnyOrder("a", "include", "ctx")); + } + @Test public void testSimpleCrossContextForward() throws Exception { @@ -749,10 +840,12 @@ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws S protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { RequestDispatcher dispatcher; - + String ctx = request.getParameter("ctx"); + if (StringUtil.isBlank(ctx)) + ctx = "/foreign"; if (request.getParameter("forward") != null) { - ServletContext foreign = getServletContext().getContext("/foreign"); + ServletContext foreign = getServletContext().getContext(ctx); assertNotNull(foreign); dispatcher = foreign.getRequestDispatcher(request.getParameter("forward") + "/pinfo?a=b"); @@ -768,7 +861,7 @@ protected void doGet(HttpServletRequest request, HttpServletResponse response) t } else if (request.getParameter("include") != null) { - ServletContext foreign = getServletContext().getContext("/foreign"); + ServletContext foreign = getServletContext().getContext(ctx); assertNotNull(foreign); dispatcher = foreign.getRequestDispatcher(request.getParameter("include") + "/pinfo?a=b"); diff --git a/jetty-ee9/jetty-ee9-servlet/src/test/resources/docroot/empty b/jetty-ee9/jetty-ee9-servlet/src/test/resources/docroot/empty new file mode 100644 index 000000000000..e69de29bb2d1