diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletHandler.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletHandler.java index 879644047a5e..b57be1db2b5b 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletHandler.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletHandler.java @@ -1161,10 +1161,10 @@ protected void updateNameMappings() } } - protected ServletPathSpec asPathSpec(String pathSpec) + protected PathSpec asPathSpec(String pathSpec) { return new ServletPathSpec(pathSpec); - } +} protected void updateMappings() { @@ -1274,7 +1274,7 @@ else if (isAllowDuplicateMappings()) finalMapping.getServletName(), getServlet(finalMapping.getServletName()).getSource()); - ServletPathSpec servletPathSpec = asPathSpec(pathSpec); + PathSpec servletPathSpec = asPathSpec(pathSpec); MappedServlet mappedServlet = new MappedServlet(servletPathSpec, getServlet(finalMapping.getServletName())); pm.put(servletPathSpec, mappedServlet); } diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletPathMapping.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletPathMapping.java index bb4f85194136..79c41a18b429 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletPathMapping.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletPathMapping.java @@ -95,6 +95,13 @@ public ServletPathMapping(PathSpec pathSpec, String servletName, String pathInCo throw new IllegalStateException(); } } + else if (pathSpec != null) + { + _mappingMatch = null; + _servletPath = pathSpec.getPathMatch(pathInContext); + _matchValue = _servletPath.startsWith("/") ? _servletPath.substring(1) : _servletPath; + _pathInfo = pathSpec.getPathInfo(pathInContext); + } else { _mappingMatch = null; diff --git a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/RegexServletTest.java b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/RegexServletTest.java new file mode 100644 index 000000000000..575c697cc951 --- /dev/null +++ b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/RegexServletTest.java @@ -0,0 +1,153 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee10.servlet; + +import java.io.IOException; +import java.io.PrintWriter; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.pathmap.PathMappings; +import org.eclipse.jetty.http.pathmap.PathSpec; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; + +public class RegexServletTest +{ + private Server _server; + private LocalConnector _connector; + private ServletContextHandler _servletContextHandler; + + @BeforeEach + public void beforeEach() + { + _server = new Server(); + _connector = new LocalConnector(_server); + + _servletContextHandler = new ServletContextHandler(_server, "/ctx"); + _servletContextHandler.setServletHandler(new ServletHandler() + { + @Override + protected PathSpec asPathSpec(String pathSpec) + { + return PathMappings.asPathSpec(pathSpec); + } + }); + + _server.setHandler(_servletContextHandler); + _server.addConnector(_connector); + } + + @Test + public void testHello() throws Exception + { + _servletContextHandler.addServlet(new ServletHolder(new ServletContextHandlerTest.HelloServlet()), "^/[Hh]ello"); + _server.start(); + + assertThat(_connector.getResponse("GET /ctx/hello HTTP/1.0\r\n\r\n"), containsString("Hello World")); + assertThat(_connector.getResponse("GET /ctx/Hello HTTP/1.0\r\n\r\n"), containsString("Hello World")); + assertThat(_connector.getResponse("GET /ctx/HELLO HTTP/1.0\r\n\r\n"), containsString(" 404")); + } + + @Test + public void testMapping() throws Exception + { + _servletContextHandler.addServlet(new ServletHolder(new TestServlet()), "^/test/.*$"); + _server.start(); + + String response = _connector.getResponse("GET /ctx/test/info HTTP/1.0\r\n\r\n"); + assertThat(response, containsString(" 200 OK")); + assertThat(response, containsString("contextPath='/ctx'")); + assertThat(response, containsString("servletPath='/test/info'")); + assertThat(response, containsString("pathInfo='null'")); + assertThat(response, containsString("mapping.mappingMatch='null'")); + assertThat(response, containsString("mapping.matchValue='test/info'")); + assertThat(response, containsString("mapping.pattern='^/test/.*$'")); + } + + @Test + public void testForward() throws Exception + { + _servletContextHandler.addServlet(new ServletHolder(new ForwardServlet()), "^/forward(/.*)?"); + _servletContextHandler.addServlet(new ServletHolder(new TestServlet()), "^/[Tt]est(/.*)?"); + _server.start(); + + String response = _connector.getResponse("GET /ctx/forward/ignore HTTP/1.0\r\n\r\n"); + assertThat(response, containsString(" 200 OK")); + assertThat(response, containsString("contextPath='/ctx'")); + assertThat(response, containsString("servletPath='/Test/info'")); + assertThat(response, containsString("pathInfo='null'")); + assertThat(response, containsString("mapping.mappingMatch='null'")); + assertThat(response, containsString("mapping.matchValue='Test/info'")); + assertThat(response, containsString("mapping.pattern='^/[Tt]est(/.*)?'")); + } + + @Test + public void testInclude() throws Exception + { + _servletContextHandler.addServlet(new ServletHolder(new IncludeServlet()), "^/include$"); + _servletContextHandler.addServlet(new ServletHolder(new TestServlet()), "^/[Tt]est(/.*)?"); + _server.start(); + + String response = _connector.getResponse("GET /ctx/include HTTP/1.0\r\n\r\n"); + assertThat(response, containsString(" 200 OK")); + assertThat(response, containsString("contextPath='/ctx'")); + assertThat(response, containsString("servletPath='/include'")); + assertThat(response, containsString("pathInfo='null'")); + assertThat(response, containsString("mapping.mappingMatch='null'")); + assertThat(response, containsString("mapping.matchValue='include'")); + assertThat(response, containsString("mapping.pattern='^/include$'")); + } + + static class TestServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + resp.setStatus(200); + PrintWriter out = resp.getWriter(); + out.printf("contextPath='%s'%n", req.getContextPath()); + out.printf("servletPath='%s'%n", req.getServletPath()); + out.printf("pathInfo='%s'%n", req.getPathInfo()); + out.printf("mapping.mappingMatch='%s'%n", req.getHttpServletMapping().getMappingMatch()); + out.printf("mapping.matchValue='%s'%n", req.getHttpServletMapping().getMatchValue()); + out.printf("mapping.pattern='%s'%n", req.getHttpServletMapping().getPattern()); + } + } + + static class ForwardServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + req.getServletContext().getRequestDispatcher("/Test/info").forward(req, resp); + } + } + + static class IncludeServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + req.getServletContext().getRequestDispatcher("/Test/info").include(req, resp); + } + } +} diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ServletPathMapping.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ServletPathMapping.java index a0734e57a10e..74eea74140c8 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ServletPathMapping.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ServletPathMapping.java @@ -95,6 +95,13 @@ public ServletPathMapping(PathSpec pathSpec, String servletName, String pathInCo throw new IllegalStateException(); } } + else if (pathSpec != null) + { + _mappingMatch = null; + _servletPath = pathSpec.getPathMatch(pathInContext); + _matchValue = _servletPath.startsWith("/") ? _servletPath.substring(1) : _servletPath; + _pathInfo = pathSpec.getPathInfo(pathInContext); + } else { _mappingMatch = null; diff --git a/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/RequestTest.java b/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/RequestTest.java index 4ca17e2eff8e..b6262bf5c39b 100644 --- a/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/RequestTest.java +++ b/jetty-ee9/jetty-ee9-nested/src/test/java/org/eclipse/jetty/ee9/nested/RequestTest.java @@ -62,6 +62,7 @@ import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.UriCompliance; +import org.eclipse.jetty.http.pathmap.RegexPathSpec; import org.eclipse.jetty.http.pathmap.ServletPathSpec; import org.eclipse.jetty.logging.StacklessLogging; import org.eclipse.jetty.server.Components; @@ -1891,6 +1892,39 @@ public void testServletPathMapping() assertThat(m.getPathInfo(), is(spec.getPathInfo(uri))); } + @Test + public void testRegexPathMapping() + { + RegexPathSpec spec; + ServletPathMapping m; + + spec = new RegexPathSpec("^/.*$"); + m = new ServletPathMapping(spec, "Something", "/some/path"); + assertThat(m.getMappingMatch(), nullValue()); + assertThat(m.getPattern(), is(spec.getDeclaration())); + assertThat(m.getServletName(), is("Something")); + assertThat(m.getServletPath(), is("/some/path")); + assertThat(m.getPathInfo(), nullValue()); + assertThat(m.getMatchValue(), is("some/path")); + + spec = new RegexPathSpec("^/some(/.*)?$"); + m = new ServletPathMapping(spec, "Something", "/some/path"); + assertThat(m.getMappingMatch(), nullValue()); + assertThat(m.getPattern(), is(spec.getDeclaration())); + assertThat(m.getServletName(), is("Something")); + assertThat(m.getServletPath(), is("/some")); + assertThat(m.getPathInfo(), is("/path")); + assertThat(m.getMatchValue(), is("some")); + + m = new ServletPathMapping(spec, "Something", "/some"); + assertThat(m.getMappingMatch(), nullValue()); + assertThat(m.getPattern(), is(spec.getDeclaration())); + assertThat(m.getServletName(), is("Something")); + assertThat(m.getServletPath(), is("/some")); + assertThat(m.getPathInfo(), nullValue()); + assertThat(m.getMatchValue(), is("some")); + } + private static long getFileCount(Path path) { try (Stream s = Files.list(path)) diff --git a/jetty-ee9/jetty-ee9-servlet/src/main/java/org/eclipse/jetty/ee9/servlet/ServletHandler.java b/jetty-ee9/jetty-ee9-servlet/src/main/java/org/eclipse/jetty/ee9/servlet/ServletHandler.java index 68bc6e9f7adb..97c2670aa8b2 100644 --- a/jetty-ee9/jetty-ee9-servlet/src/main/java/org/eclipse/jetty/ee9/servlet/ServletHandler.java +++ b/jetty-ee9/jetty-ee9-servlet/src/main/java/org/eclipse/jetty/ee9/servlet/ServletHandler.java @@ -1246,7 +1246,7 @@ protected void updateNameMappings() } } - protected ServletPathSpec asPathSpec(String pathSpec) + protected PathSpec asPathSpec(String pathSpec) { return new ServletPathSpec(pathSpec); } @@ -1359,7 +1359,7 @@ else if (isAllowDuplicateMappings()) finalMapping.getServletName(), getServlet(finalMapping.getServletName()).getSource()); - ServletPathSpec servletPathSpec = asPathSpec(pathSpec); + PathSpec servletPathSpec = asPathSpec(pathSpec); MappedServlet mappedServlet = new MappedServlet(servletPathSpec, getServlet(finalMapping.getServletName())); pm.put(servletPathSpec, mappedServlet); } diff --git a/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/RegexServletTest.java b/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/RegexServletTest.java new file mode 100644 index 000000000000..68e035362e6a --- /dev/null +++ b/jetty-ee9/jetty-ee9-servlet/src/test/java/org/eclipse/jetty/ee9/servlet/RegexServletTest.java @@ -0,0 +1,153 @@ +// +// ======================================================================== +// Copyright (c) 1995-2022 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.ee9.servlet; + +import java.io.IOException; +import java.io.PrintWriter; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.pathmap.PathMappings; +import org.eclipse.jetty.http.pathmap.PathSpec; +import org.eclipse.jetty.server.LocalConnector; +import org.eclipse.jetty.server.Server; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.containsString; + +public class RegexServletTest +{ + private Server _server; + private LocalConnector _connector; + private ServletContextHandler _servletContextHandler; + + @BeforeEach + public void beforeEach() + { + _server = new Server(); + _connector = new LocalConnector(_server); + + _servletContextHandler = new ServletContextHandler(_server, "/ctx"); + _servletContextHandler.setServletHandler(new ServletHandler() + { + @Override + protected PathSpec asPathSpec(String pathSpec) + { + return PathMappings.asPathSpec(pathSpec); + } + }); + + _server.setHandler(_servletContextHandler); + _server.addConnector(_connector); + } + + @Test + public void testHello() throws Exception + { + _servletContextHandler.addServlet(new ServletHolder(new ServletContextHandlerTest.HelloServlet()), "^/[Hh]ello"); + _server.start(); + + assertThat(_connector.getResponse("GET /ctx/hello HTTP/1.0\r\n\r\n"), containsString("Hello World")); + assertThat(_connector.getResponse("GET /ctx/Hello HTTP/1.0\r\n\r\n"), containsString("Hello World")); + assertThat(_connector.getResponse("GET /ctx/HELLO HTTP/1.0\r\n\r\n"), containsString(" 404")); + } + + @Test + public void testMapping() throws Exception + { + _servletContextHandler.addServlet(new ServletHolder(new TestServlet()), "^/test/.*$"); + _server.start(); + + String response = _connector.getResponse("GET /ctx/test/info HTTP/1.0\r\n\r\n"); + assertThat(response, containsString(" 200 OK")); + assertThat(response, containsString("contextPath='/ctx'")); + assertThat(response, containsString("servletPath='/test/info'")); + assertThat(response, containsString("pathInfo='null'")); + assertThat(response, containsString("mapping.mappingMatch='null'")); + assertThat(response, containsString("mapping.matchValue='test/info'")); + assertThat(response, containsString("mapping.pattern='^/test/.*$'")); + } + + @Test + public void testForward() throws Exception + { + _servletContextHandler.addServlet(new ServletHolder(new ForwardServlet()), "^/forward(/.*)?"); + _servletContextHandler.addServlet(new ServletHolder(new TestServlet()), "^/[Tt]est(/.*)?"); + _server.start(); + + String response = _connector.getResponse("GET /ctx/forward/ignore HTTP/1.0\r\n\r\n"); + assertThat(response, containsString(" 200 OK")); + assertThat(response, containsString("contextPath='/ctx'")); + assertThat(response, containsString("servletPath='/Test/info'")); + assertThat(response, containsString("pathInfo='null'")); + assertThat(response, containsString("mapping.mappingMatch='null'")); + assertThat(response, containsString("mapping.matchValue='Test/info'")); + assertThat(response, containsString("mapping.pattern='^/[Tt]est(/.*)?'")); + } + + @Test + public void testInclude() throws Exception + { + _servletContextHandler.addServlet(new ServletHolder(new IncludeServlet()), "^/include$"); + _servletContextHandler.addServlet(new ServletHolder(new TestServlet()), "^/[Tt]est(/.*)?"); + _server.start(); + + String response = _connector.getResponse("GET /ctx/include HTTP/1.0\r\n\r\n"); + assertThat(response, containsString(" 200 OK")); + assertThat(response, containsString("contextPath='/ctx'")); + assertThat(response, containsString("servletPath='/include'")); + assertThat(response, containsString("pathInfo='null'")); + assertThat(response, containsString("mapping.mappingMatch='null'")); + assertThat(response, containsString("mapping.matchValue='include'")); + assertThat(response, containsString("mapping.pattern='^/include$'")); + } + + static class TestServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + resp.setStatus(200); + PrintWriter out = resp.getWriter(); + out.printf("contextPath='%s'%n", req.getContextPath()); + out.printf("servletPath='%s'%n", req.getServletPath()); + out.printf("pathInfo='%s'%n", req.getPathInfo()); + out.printf("mapping.mappingMatch='%s'%n", req.getHttpServletMapping().getMappingMatch()); + out.printf("mapping.matchValue='%s'%n", req.getHttpServletMapping().getMatchValue()); + out.printf("mapping.pattern='%s'%n", req.getHttpServletMapping().getPattern()); + } + } + + static class ForwardServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + req.getServletContext().getRequestDispatcher("/Test/info").forward(req, resp); + } + } + + static class IncludeServlet extends HttpServlet + { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException + { + req.getServletContext().getRequestDispatcher("/Test/info").include(req, resp); + } + } +}