diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java index a237c8734dd5..b531b35486e6 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/FileID.java @@ -74,7 +74,29 @@ public static String getFileName(String path) return ""; int idx = path.lastIndexOf('/'); if (idx >= 0) + { + if (idx == path.length() - 1) + { + // we found the trailing slash + // eg: file:/path/to/dir/ + // we want to return the "dir" segment here + int previousSlash = path.lastIndexOf('/', idx - 1); + if (previousSlash >= 0) + { + // we have a previous slash + // so return the segment without the trailing slash + return path.substring(previousSlash + 1, idx); + } + else + { + // we have no previous slash + // this input string is something like "foo/" + // so return the segment without trailing slash + return path.substring(0, idx); + } + } return path.substring(idx + 1); + } return path; } diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java index a98a12f0dcf0..0305d60b22ae 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/Resource.java @@ -143,7 +143,8 @@ public long length() * *

This is the last segment of the path.

* - * @return the filename of the resource, or "" if there are no path segments (eg: path of "/"), or null if not backed by a Path + * @return the filename of the resource, or "" if there are no path segments (eg: path of "/"), or null if resource has no path. + * @see Path#getFileName() */ public abstract String getFileName(); diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/FileIDTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/FileIDTest.java index 326130eef583..e6293cda3a33 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/FileIDTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/FileIDTest.java @@ -101,13 +101,14 @@ public static Stream fileNameSource() { return Stream.of( Arguments.of(null, ""), + Arguments.of(URI.create("foo:bar"), "bar"), Arguments.of(URI.create("file:/"), ""), Arguments.of(URI.create("file:///"), ""), - Arguments.of(URI.create("file:zed/"), ""), + Arguments.of(URI.create("file:zed/"), "zed"), Arguments.of(URI.create("file:///path/to/test.txt"), "test.txt"), - Arguments.of(URI.create("file:///path/to/dir/"), ""), + Arguments.of(URI.create("file:///path/to/dir/"), "dir"), Arguments.of(URI.create("jar:file:///home/user/libs/jetty-server-12.jar!/org/eclipse/jetty/server/jetty-dir.css"), "jetty-dir.css"), - Arguments.of(URI.create("http://eclipse.org/jetty/"), ""), + Arguments.of(URI.create("http://eclipse.org/jetty/"), "jetty"), Arguments.of(URI.create("http://eclipse.org/jetty/index.html"), "index.html"), Arguments.of(URI.create("http://eclipse.org/jetty/docs.html?query=val#anchor"), "docs.html") ); diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/PathResourceTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/PathResourceTest.java index 429404ef9e31..8043d769c191 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/PathResourceTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/PathResourceTest.java @@ -27,6 +27,7 @@ import java.util.HashMap; import java.util.Map; +import org.eclipse.jetty.toolchain.test.FS; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; @@ -353,6 +354,25 @@ public void testNullCharEndingFilename(WorkDir workDir) throws Exception } } + @Test + public void testGetFileName(WorkDir workDir) throws IOException + { + Path tmpPath = workDir.getEmptyPathDir(); + Path dir = tmpPath.resolve("foo-dir"); + FS.ensureDirExists(dir); + Path file = dir.resolve("bar.txt"); + Files.writeString(file, "This is bar.txt", StandardCharsets.UTF_8); + + Resource baseResource = new PathResource(tmpPath); + assertThat(baseResource.getFileName(), is(tmpPath.getFileName().toString())); + + Resource dirResource = baseResource.resolve("foo-dir"); + assertThat(dirResource.getFileName(), is(dir.getFileName().toString())); + + Resource fileResource = dirResource.resolve("bar.txt"); + assertThat(fileResource.getFileName(), is(file.getFileName().toString())); + } + @Test public void testSymlink(WorkDir workDir) throws Exception { diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/UrlResourceFactoryTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/UrlResourceFactoryTest.java index fdd81844f4b4..36037417d46e 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/UrlResourceFactoryTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/resource/UrlResourceFactoryTest.java @@ -27,15 +27,20 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import org.eclipse.jetty.toolchain.test.FS; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.URIUtil; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.notNullValue; @@ -44,6 +49,7 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +@ExtendWith(WorkDirExtension.class) public class UrlResourceFactoryTest { @Test @@ -77,7 +83,7 @@ public void testHttps() throws IOException assertThat(blogs.lastModified().toEpochMilli(), not(Instant.EPOCH)); assertThat(blogs.length(), not(-1)); assertTrue(blogs.isDirectory()); - assertThat(blogs.getFileName(), is("")); + assertThat(blogs.getFileName(), is("blog")); Resource favicon = resource.resolve("favicon.ico"); assertThat(favicon, notNullValue()); @@ -93,6 +99,27 @@ public void testHttps() throws IOException } } + @Test + public void testGetFileName(WorkDir workDir) throws IOException + { + Path tmpPath = workDir.getEmptyPathDir(); + Path dir = tmpPath.resolve("foo-dir"); + FS.ensureDirExists(dir); + Path file = dir.resolve("bar.txt"); + Files.writeString(file, "This is bar.txt", StandardCharsets.UTF_8); + + URLResourceFactory urlResourceFactory = new URLResourceFactory(); + + Resource baseResource = urlResourceFactory.newResource(tmpPath); + assertThat(baseResource.getFileName(), endsWith("")); + + Resource dirResource = baseResource.resolve("foo-dir/"); + assertThat(dirResource.getFileName(), endsWith("foo-dir")); + + Resource fileResource = dirResource.resolve("bar.txt"); + assertThat(fileResource.getFileName(), endsWith("bar.txt")); + } + @Test public void testInputStreamCleanedUp() throws Exception { diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextHandler.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextHandler.java index f5b18df21409..45f4637c2f3f 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextHandler.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/ServletContextHandler.java @@ -20,6 +20,7 @@ import java.net.URI; import java.net.URL; import java.nio.charset.Charset; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; @@ -810,12 +811,15 @@ public Set getResourcePaths(String path) Resource resource = getResource(path); if (!path.endsWith("/")) - path = path + "/"; + path = path + '/'; HashSet set = new HashSet<>(); for (Resource item: resource.list()) { - set.add(path + item.getFileName()); + String entry = path + item.getFileName(); + if (item.isDirectory()) + entry = entry + '/'; + set.add(entry); } return set; } @@ -2768,7 +2772,12 @@ else if (path.charAt(0) != '/') { Path resourcePath = r.getPath(); if (resourcePath != null) - return resourcePath.normalize().toString(); + { + String realPath = resourcePath.normalize().toString(); + if (Files.isDirectory(resourcePath)) + realPath = realPath + "/"; + return realPath; + } } } diff --git a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ServletRequestListenerTest.java b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ServletRequestListenerTest.java index 1beae0705bf0..db9ab23eaec8 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ServletRequestListenerTest.java +++ b/jetty-ee10/jetty-ee10-servlet/src/test/java/org/eclipse/jetty/ee10/servlet/ServletRequestListenerTest.java @@ -15,7 +15,6 @@ import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.EnumSet; import java.util.List; import java.util.function.Consumer; @@ -46,6 +45,7 @@ import org.junit.jupiter.api.Test; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.equalTo; public class ServletRequestListenerTest @@ -316,7 +316,7 @@ private void assertEventsEmpty() private void assertEvents(String... events) { - assertThat(_events, equalTo(Arrays.asList(events))); + assertThat(_events, contains(events)); _events.clear(); } } diff --git a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppContextTest.java b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppContextTest.java index 14238ec39952..ad0b0f4937a7 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppContextTest.java +++ b/jetty-ee10/jetty-ee10-webapp/src/test/java/org/eclipse/jetty/ee10/webapp/WebAppContextTest.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -512,8 +513,8 @@ public void testGetResourceFromCollection() throws Exception WebAppContext context = new WebAppContext(); context.setContextPath("/"); context.setBaseResource(ResourceFactory.combine( - ResourceFactory.root().newResource(MavenTestingUtils.getTestResourcePath("wars/layer0/")), - ResourceFactory.root().newResource(MavenTestingUtils.getTestResourcePath("wars/layer1/")))); + context.getResourceFactory().newResource(MavenTestingUtils.getTestResourcePath("wars/layer0/")), + context.getResourceFactory().newResource(MavenTestingUtils.getTestResourcePath("wars/layer1/")))); server.setHandler(context); server.start(); @@ -530,13 +531,39 @@ public void testGetResourcePathsFromCollection() throws Exception WebAppContext context = new WebAppContext(); context.setContextPath("/"); context.setBaseResource(ResourceFactory.combine( - ResourceFactory.root().newResource(MavenTestingUtils.getTestResourcePath("wars/layer0/")), - ResourceFactory.root().newResource(MavenTestingUtils.getTestResourcePath("wars/layer1/")))); + context.getResourceFactory().newResource(MavenTestingUtils.getTestResourcePath("wars/layer0/")), + context.getResourceFactory().newResource(MavenTestingUtils.getTestResourcePath("wars/layer1/")))); server.setHandler(context); server.start(); ServletContext servletContext = context.getServletContext(); - assertThat(servletContext.getResourcePaths("/WEB-INF"), containsInAnyOrder("/WEB-INF/zero.xml", "/WEB-INF/one.xml")); + assertThat(servletContext.getResourcePaths("/WEB-INF/"), containsInAnyOrder("/WEB-INF/zero.xml", "/WEB-INF/one.xml")); + } + + @Test + public void testGetResourcePathsWithDirsFromCollection() throws Exception + { + Server server = newServer(); + + WebAppContext context = new WebAppContext(); + context.setContextPath("/"); + context.setBaseResource(ResourceFactory.combine( + context.getResourceFactory().newResource(MavenTestingUtils.getTestResourcePath("wars/layer0/")), + context.getResourceFactory().newResource(MavenTestingUtils.getTestResourcePath("wars/layer1/")), + context.getResourceFactory().newResource(MavenTestingUtils.getTestResourcePath("wars/with_dirs/")) + )); + server.setHandler(context); + server.start(); + + ServletContext servletContext = context.getServletContext(); + Set results = servletContext.getResourcePaths("/WEB-INF/"); + String[] expected = { + "/WEB-INF/zero.xml", + "/WEB-INF/one.xml", + "/WEB-INF/bar/", + "/WEB-INF/foo/" + }; + assertThat(results, containsInAnyOrder(expected)); } @Test @@ -552,18 +579,21 @@ public void testGetResourcePaths() throws Exception ServletContext servletContext = context.getServletContext(); - List resourcePaths = List.copyOf(servletContext.getResourcePaths("/")); + Set resourcePaths = servletContext.getResourcePaths("/"); + String[] expected = { + "/WEB-INF/", + "/nested-reserved-!#\\\\$%&()*+,:=?@[]-meta-inf-resource.txt", + }; assertThat(resourcePaths.size(), is(2)); - assertThat(resourcePaths.get(0), is("/WEB-INF")); - assertThat(resourcePaths.get(1), is("/nested-reserved-!#\\\\$%&()*+,:=?@[]-meta-inf-resource.txt")); + assertThat(resourcePaths, containsInAnyOrder(expected)); String realPath = servletContext.getRealPath("/"); assertThat(realPath, notNullValue()); - assertThat(servletContext.getRealPath(resourcePaths.get(0)), endsWith("/WEB-INF")); + assertThat(servletContext.getRealPath("/WEB-INF/"), endsWith("/WEB-INF/")); // TODO the following assertion fails because of a bug in the JDK (see JDK-8311079 and MountedPathResourceTest.testJarFileResourceAccessBackSlash()) //assertThat(servletContext.getRealPath(resourcePaths.get(1)), endsWith("/nested-reserved-!#\\\\$%&()*+,:=?@[]-meta-inf-resource.txt")); - assertThat(servletContext.getResource("/WEB-INF"), notNullValue()); + assertThat(servletContext.getResource("/WEB-INF/"), notNullValue()); // TODO the following assertion fails because of a bug in the JDK (see JDK-8311079 and MountedPathResourceTest.testJarFileResourceAccessBackSlash()) //assertThat(servletContext.getResource("/nested-reserved-!#\\\\$%&()*+,:=?@[]-meta-inf-resource.txt"), notNullValue()); diff --git a/jetty-ee10/jetty-ee10-webapp/src/test/resources/wars/with_dirs/WEB-INF/bar/main.txt b/jetty-ee10/jetty-ee10-webapp/src/test/resources/wars/with_dirs/WEB-INF/bar/main.txt new file mode 100644 index 000000000000..2af19ef08b60 --- /dev/null +++ b/jetty-ee10/jetty-ee10-webapp/src/test/resources/wars/with_dirs/WEB-INF/bar/main.txt @@ -0,0 +1 @@ +This is the "main.txt" in the /bar/ directory for war "with_dirs" \ No newline at end of file diff --git a/jetty-ee10/jetty-ee10-webapp/src/test/resources/wars/with_dirs/WEB-INF/foo/alt.txt b/jetty-ee10/jetty-ee10-webapp/src/test/resources/wars/with_dirs/WEB-INF/foo/alt.txt new file mode 100644 index 000000000000..d2302efab927 --- /dev/null +++ b/jetty-ee10/jetty-ee10-webapp/src/test/resources/wars/with_dirs/WEB-INF/foo/alt.txt @@ -0,0 +1 @@ +This is the "alt.txt" in the /foo/ directory for war "with_dirs" \ No newline at end of file diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ContextHandler.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ContextHandler.java index db70e7398dcf..1871df9d5513 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ContextHandler.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ContextHandler.java @@ -18,6 +18,7 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URL; +import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; @@ -1568,16 +1569,16 @@ public Set getResourcePaths(String path) Resource resource = getResource(path); if (!path.endsWith("/")) - path = path + "/"; + path = path + '/'; HashSet set = new HashSet<>(); - for (Resource r: resource) + for (Resource item: resource.list()) { - for (Resource item: r.list()) - { - set.add(path + item.getFileName()); - } + String entry = path + item.getFileName(); + if (item.isDirectory()) + entry = entry + '/'; + set.add(entry); } return set; } @@ -1907,9 +1908,24 @@ else if (path.charAt(0) != '/') Resource resource = ContextHandler.this.getResource(path); if (resource != null) { - Path resourcePath = resource.getPath(); - if (resourcePath != null) - return resourcePath.toAbsolutePath().normalize().toString(); + for (Resource r : resource) + { + // return first + if (Resources.exists(r)) + { + Path resourcePath = r.getPath(); + if (resourcePath != null) + { + String realPath = resourcePath.normalize().toString(); + if (Files.isDirectory(resourcePath)) + realPath = realPath + "/"; + return realPath; + } + } + } + + // A Resource was returned, but did not exist + return null; } } catch (Exception e) diff --git a/jetty-ee9/jetty-ee9-webapp/pom.xml b/jetty-ee9/jetty-ee9-webapp/pom.xml index 0dee5f1711ed..24b7f3244b09 100644 --- a/jetty-ee9/jetty-ee9-webapp/pom.xml +++ b/jetty-ee9/jetty-ee9-webapp/pom.xml @@ -37,6 +37,10 @@ src/test/webapp webapp + + src/test/webapp-with-resources + webapp-with-resources + src/test/webapp-alt-jsp webapp-alt-jsp @@ -50,6 +54,8 @@ @{argLine} ${jetty.surefire.argLine} + --add-exports org.eclipse.jetty.ee9.webapp/org.acme.webapp=org.eclipse.jetty.ee9.servlet + --add-exports org.eclipse.jetty.ee9.webapp/org.acme.webapp=org.eclipse.jetty.ee9.nested false diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/acme/webapp/GetRealPathsServlet.java b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/acme/webapp/GetRealPathsServlet.java new file mode 100644 index 000000000000..a99fabdbf90d --- /dev/null +++ b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/acme/webapp/GetRealPathsServlet.java @@ -0,0 +1,49 @@ +// +// ======================================================================== +// Copyright (c) 1995 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.acme.webapp; + +import java.io.IOException; +import java.util.LinkedHashSet; +import java.util.Set; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class GetRealPathsServlet extends HttpServlet +{ + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + collectResourcePaths("/").stream() + .map(p -> getServletContext().getRealPath(p)) + .forEach(resp.getWriter()::println); + resp.getWriter().flush(); + } + + private Set collectResourcePaths(String path) + { + Set allResourcePaths = new LinkedHashSet<>(); + Set pathsForPath = getServletContext().getResourcePaths(path); + if (pathsForPath != null) + { + for (String resourcePath : pathsForPath) + { + allResourcePaths.add(resourcePath); + allResourcePaths.addAll(collectResourcePaths(resourcePath)); + } + } + return allResourcePaths; + } +} diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/acme/webapp/GetResourcePathsServlet.java b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/acme/webapp/GetResourcePathsServlet.java new file mode 100644 index 000000000000..2c7aebf9049b --- /dev/null +++ b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/acme/webapp/GetResourcePathsServlet.java @@ -0,0 +1,47 @@ +// +// ======================================================================== +// Copyright (c) 1995 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.acme.webapp; + +import java.io.IOException; +import java.util.LinkedHashSet; +import java.util.Set; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +public class GetResourcePathsServlet extends HttpServlet +{ + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException + { + collectResourcePaths("/").forEach(resp.getWriter()::println); + resp.getWriter().flush(); + } + + private Set collectResourcePaths(String path) + { + Set allResourcePaths = new LinkedHashSet<>(); + Set pathsForPath = getServletContext().getResourcePaths(path); + if (pathsForPath != null) + { + for (String resourcePath : pathsForPath) + { + allResourcePaths.add(resourcePath); + allResourcePaths.addAll(collectResourcePaths(resourcePath)); + } + } + return allResourcePaths; + } +} diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppContextTest.java b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppContextTest.java index 62679431c8c8..99f9c2b81485 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppContextTest.java +++ b/jetty-ee9/jetty-ee9-webapp/src/test/java/org/eclipse/jetty/ee9/webapp/WebAppContextTest.java @@ -22,6 +22,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.List; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -49,6 +50,7 @@ import org.eclipse.jetty.util.FileID; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.component.LifeCycle; +import org.eclipse.jetty.util.resource.FileSystemPool; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceFactory; import org.eclipse.jetty.util.resource.Resources; @@ -68,8 +70,13 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.containsInAnyOrder; +import static org.hamcrest.Matchers.containsString; +import static org.hamcrest.Matchers.empty; +import static org.hamcrest.Matchers.endsWith; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -86,7 +93,7 @@ public class WebAppContextTest @BeforeEach public void beforeEach() { - //assertThat(FileSystemPool.INSTANCE.mounts(), empty()); + assertThat(FileSystemPool.INSTANCE.mounts(), empty()); } @AfterEach @@ -94,7 +101,7 @@ public void tearDown() { lifeCycles.forEach(LifeCycle::stop); Configurations.cleanKnown(); - //assertThat(FileSystemPool.INSTANCE.mounts(), empty()); + assertThat(FileSystemPool.INSTANCE.mounts(), empty()); } private Server newServer() @@ -349,7 +356,7 @@ public void testAlias(WorkDir workDir) throws Exception FS.touch(someClass); WebAppContext context = new WebAppContext(); - context.setBaseResource(ResourceFactory.root().newResource(tempDir)); + context.setBaseResource(context.getResourceFactory().newResource(tempDir)); context.setResourceAlias("/WEB-INF/classes/", "/classes/"); @@ -380,7 +387,7 @@ public void testProtectedTarget() throws Exception WebAppContext context = new WebAppContext(); Path testWebapp = MavenTestingUtils.getTargetPath("test-classes/webapp"); - context.setBaseResource(ResourceFactory.root().newResource(testWebapp)); + context.setBaseResource(context.getResourceFactory().newResource(testWebapp)); context.setContextPath("/"); contexts.addHandler(context); @@ -441,7 +448,7 @@ public void testProtectedTargetFailure(String path) throws Exception WebAppContext context = new WebAppContext(); Path testWebapp = MavenTestingUtils.getTargetPath("test-classes/webapp"); - context.setBaseResource(ResourceFactory.root().newResource(testWebapp)); + context.setBaseResource(context.getResourceFactory().newResource(testWebapp)); context.setContextPath("/"); contexts.addHandler(context); @@ -495,7 +502,7 @@ public void testNullSessionAndSecurityHandler() throws Exception ServletContextHandler.NO_SESSIONS | ServletContextHandler.NO_SECURITY); context.setContextPath("/"); Path testWebapp = MavenTestingUtils.getTargetPath("test-classes/webapp"); - context.setBaseResource(ResourceFactory.root().newResource(testWebapp)); + context.setBaseResource(context.getResourceFactory().newResource(testWebapp)); contexts.addHandler(context); LocalConnector connector = new LocalConnector(server); @@ -546,6 +553,132 @@ public void testBaseResourceAbsolutePath() throws Exception assertTrue(context.isAvailable(), "WebAppContext should be available"); } + @Test + public void testGetResourceFromCollection() throws Exception + { + Server server = newServer(); + + WebAppContext context = new WebAppContext(); + context.setContextPath("/"); + context.setBaseResource(ResourceFactory.combine( + context.getResourceFactory().newResource(MavenPaths.findTestResourceDir("wars/layer0/")), + context.getResourceFactory().newResource(MavenPaths.findTestResourceDir("wars/layer1/")))); + server.setHandler(context); + server.start(); + + ServletContext servletContext = context.getServletContext(); + assertThat(servletContext.getResource("/WEB-INF/zero.xml"), notNullValue()); + assertThat(servletContext.getResource("/WEB-INF/one.xml"), notNullValue()); + } + + @Test + public void testGetResourcePathsFromCollection() throws Exception + { + Server server = newServer(); + + WebAppContext context = new WebAppContext(); + context.setContextPath("/"); + context.setBaseResource(ResourceFactory.combine( + context.getResourceFactory().newResource(MavenPaths.findTestResourceDir("wars/layer0/")), + context.getResourceFactory().newResource(MavenPaths.findTestResourceDir("wars/layer1/")))); + server.setHandler(context); + server.start(); + + ServletContext servletContext = context.getServletContext(); + assertThat(servletContext.getResourcePaths("/WEB-INF/"), containsInAnyOrder("/WEB-INF/zero.xml", "/WEB-INF/one.xml")); + } + + @Test + public void testGetResourcePathsWithDirsFromCollection() throws Exception + { + Server server = newServer(); + + WebAppContext context = new WebAppContext(); + context.setContextPath("/"); + context.setBaseResource(ResourceFactory.combine( + context.getResourceFactory().newResource(MavenPaths.findTestResourceDir("wars/layer0/")), + context.getResourceFactory().newResource(MavenPaths.findTestResourceDir("wars/layer1/")), + context.getResourceFactory().newResource(MavenPaths.findTestResourceDir("wars/with_dirs/")) + )); + server.setHandler(context); + server.start(); + + ServletContext servletContext = context.getServletContext(); + Set results = servletContext.getResourcePaths("/WEB-INF/"); + String[] expected = { + "/WEB-INF/zero.xml", + "/WEB-INF/one.xml", + "/WEB-INF/bar/", + "/WEB-INF/foo/" + }; + assertThat(results, containsInAnyOrder(expected)); + } + + @Test + @Disabled("There is extra decoding of the nested-reserved paths that is getting in the way") + public void testGetResourcePaths() throws Exception + { + Server server = newServer(); + LocalConnector connector = new LocalConnector(server); + server.addConnector(connector); + + Path warRoot = MavenPaths.findTestResourceDir("webapp-with-resources"); + assertTrue(Files.isDirectory(warRoot), "Unable to find directory: " + warRoot); + WebAppContext context = new WebAppContext(); + Resource warResource = context.getResourceFactory().newResource(warRoot); + context.setWarResource(warResource); + context.setContextPath("/"); + server.setHandler(context); + server.start(); + + ServletContext servletContext = context.getServletContext(); + + Set resourcePaths = servletContext.getResourcePaths("/"); + String[] expected = { + "/WEB-INF/", + "/nested-reserved-!#\\\\$%&()*+,:=?@[]-meta-inf-resource.txt", + }; + assertThat(resourcePaths.size(), is(2)); + assertThat(resourcePaths, containsInAnyOrder(expected)); + + String realPath = servletContext.getRealPath("/"); + assertThat(realPath, notNullValue()); + assertThat(servletContext.getRealPath("/WEB-INF/"), endsWith("/WEB-INF/")); + // TODO the following assertion fails because of a bug in the JDK (see JDK-8311079 and MountedPathResourceTest.testJarFileResourceAccessBackSlash()) + //assertThat(servletContext.getRealPath(resourcePaths.get(1)), endsWith("/nested-reserved-!#\\\\$%&()*+,:=?@[]-meta-inf-resource.txt")); + + assertThat(servletContext.getResource("/WEB-INF/"), notNullValue()); + // TODO the following assertion fails because of a bug in the JDK (see JDK-8311079 and MountedPathResourceTest.testJarFileResourceAccessBackSlash()) + //assertThat(servletContext.getResource("/nested-reserved-!#\\\\$%&()*+,:=?@[]-meta-inf-resource.txt"), notNullValue()); + + HttpTester.Response response1 = HttpTester.parseResponse(connector.getResponse(""" + GET /resource HTTP/1.1\r + Host: local\r + Connection: close\r + \r + """)); + + assertThat(response1.getStatus(), is(HttpStatus.OK_200)); + assertThat(response1.getContent(), containsString("/WEB-INF")); + assertThat(response1.getContent(), containsString("/WEB-INF/lib")); + assertThat(response1.getContent(), containsString("/WEB-INF/lib/odd-resource.jar")); + assertThat(response1.getContent(), containsString("/nested-reserved-!#\\\\$%&()*+,:=?@[]-meta-inf-resource.txt")); + + HttpTester.Response response2 = HttpTester.parseResponse(connector.getResponse(""" + GET /real HTTP/1.1\r + Host: local\r + Connection: close\r + \r + """)); + + assertThat(response2.getStatus(), is(HttpStatus.OK_200)); + assertThat(response2.getContent(), containsString("/WEB-INF")); + assertThat(response2.getContent(), containsString("/WEB-INF/lib")); + assertThat(response2.getContent(), containsString("/WEB-INF/lib/odd-resource.jar")); + // TODO the following assertion fails because of a bug in the JDK (see JDK-8311079 and MountedPathResourceTest.testJarFileResourceAccessBackSlash()) + //assertThat(response2.getContent(), containsString("/nested-reserved-!#\\\\$%&()*+,:=?@[]-meta-inf-resource.txt")); + } + public static Stream extraClasspathGlob() { List references = new ArrayList<>(); diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/resources/wars/layer0/WEB-INF/zero.xml b/jetty-ee9/jetty-ee9-webapp/src/test/resources/wars/layer0/WEB-INF/zero.xml new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/resources/wars/layer1/WEB-INF/one.xml b/jetty-ee9/jetty-ee9-webapp/src/test/resources/wars/layer1/WEB-INF/one.xml new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/resources/wars/with_dirs/WEB-INF/bar/main.txt b/jetty-ee9/jetty-ee9-webapp/src/test/resources/wars/with_dirs/WEB-INF/bar/main.txt new file mode 100644 index 000000000000..2af19ef08b60 --- /dev/null +++ b/jetty-ee9/jetty-ee9-webapp/src/test/resources/wars/with_dirs/WEB-INF/bar/main.txt @@ -0,0 +1 @@ +This is the "main.txt" in the /bar/ directory for war "with_dirs" \ No newline at end of file diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/resources/wars/with_dirs/WEB-INF/foo/alt.txt b/jetty-ee9/jetty-ee9-webapp/src/test/resources/wars/with_dirs/WEB-INF/foo/alt.txt new file mode 100644 index 000000000000..d2302efab927 --- /dev/null +++ b/jetty-ee9/jetty-ee9-webapp/src/test/resources/wars/with_dirs/WEB-INF/foo/alt.txt @@ -0,0 +1 @@ +This is the "alt.txt" in the /foo/ directory for war "with_dirs" \ No newline at end of file diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/webapp-with-resources/WEB-INF/lib/odd-resource.jar b/jetty-ee9/jetty-ee9-webapp/src/test/webapp-with-resources/WEB-INF/lib/odd-resource.jar new file mode 100644 index 000000000000..5c78785fd6a3 Binary files /dev/null and b/jetty-ee9/jetty-ee9-webapp/src/test/webapp-with-resources/WEB-INF/lib/odd-resource.jar differ diff --git a/jetty-ee9/jetty-ee9-webapp/src/test/webapp-with-resources/WEB-INF/web.xml b/jetty-ee9/jetty-ee9-webapp/src/test/webapp-with-resources/WEB-INF/web.xml new file mode 100644 index 000000000000..612b39d55f58 --- /dev/null +++ b/jetty-ee9/jetty-ee9-webapp/src/test/webapp-with-resources/WEB-INF/web.xml @@ -0,0 +1,30 @@ + + + + + GetResourcePathsServlet + org.acme.webapp.GetResourcePathsServlet + 1 + + + GetRealPathsServlet + org.acme.webapp.GetRealPathsServlet + 1 + + + + GetResourcePathsServlet + /resource/* + + + GetRealPathsServlet + /real/* + + + +