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 e41f8b8127a..4f593509c80 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 @@ -714,6 +714,42 @@ public class URIUtil return buf.toString(); } + /** + * Given a URI, attempt to get the last segment. + *

+ * If this is a {@code jar:file://} style URI, then + * the JAR filename is returned (not the deep {@code !/path} location) + *

+ * + * @param uri the URI to look in + * @return the last segment. + */ + public static String getUriLastPathSegment(URI uri) + { + String ssp = uri.getSchemeSpecificPart(); + // strip off deep jar:file: reference information + int idx = ssp.indexOf("!/"); + if (idx != -1) + { + ssp = ssp.substring(0, idx); + } + + // Strip off trailing '/' if present + if (ssp.endsWith("/")) + { + ssp = ssp.substring(0, ssp.length() - 1); + } + + // Only interested in last segment + idx = ssp.lastIndexOf('/'); + if (idx != -1) + { + ssp = ssp.substring(idx + 1); + } + + return ssp; + } + /** * Return the parent Path. * Treat a URI like a directory path and return the parent directory. 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 d3ba74a0e40..1e4220b1fcd 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 @@ -18,13 +18,31 @@ package org.eclipse.jetty.util; +import java.io.IOException; import java.net.URI; +import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitOption; +import java.nio.file.Files; +import java.nio.file.Path; +import java.text.Normalizer; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.stream.Stream; +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.log.Log; +import org.eclipse.jetty.util.log.Logger; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.OS; +import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; @@ -40,8 +58,12 @@ import static org.junit.jupiter.api.Assertions.assertTrue; * URIUtil Tests. */ @SuppressWarnings("SpellCheckingInspection") +@ExtendWith(WorkDirExtension.class) public class URIUtilTest { + private static final Logger LOG = Log.getLogger(URIUtilTest.class); + public WorkDir workDir; + public static Stream encodePathSource() { return Stream.of( @@ -591,4 +613,97 @@ public class URIUtilTest { assertThat(URIUtil.decodeSpecific(raw, chars), is(expected)); } + + public static Stream resourceUriLastSegmentSource() + { + return Stream.of( + Arguments.of("test.war", "test.war"), + Arguments.of("a/b/c/test.war", "test.war"), + Arguments.of("bar%2Fbaz/test.war", "test.war"), + Arguments.of("fizz buzz/test.war", "test.war"), + Arguments.of("another one/bites the dust/", "bites the dust"), + Arguments.of("another+one/bites+the+dust/", "bites+the+dust"), + Arguments.of("another%20one/bites%20the%20dust/", "bites%20the%20dust"), + Arguments.of("spanish/n\u00FAmero.war", "n\u00FAmero.war"), + Arguments.of("spanish/n%C3%BAmero.war", "n%C3%BAmero.war"), + Arguments.of("a/b!/", "b"), + Arguments.of("a/b!/c/", "b"), + Arguments.of("a/b!/c/d/", "b"), + Arguments.of("a/b%21/", "b%21") + ); + } + + /** + * Using FileSystem provided URIs, attempt to get last URI path segment + */ + @ParameterizedTest + @MethodSource("resourceUriLastSegmentSource") + public void testFileUriGetUriLastPathSegment(String basePath, String expectedName) throws IOException + { + Path root = workDir.getPath(); + Path base = root.resolve(basePath); + if (basePath.endsWith("/")) + { + FS.ensureDirExists(base); + } + else + { + FS.ensureDirExists(base.getParent()); + FS.touch(base); + } + URI uri = base.toUri(); + if (OS.MAC.isCurrentOs()) + { + // Normalize Unicode to NFD form that OSX Path/FileSystem produces + expectedName = Normalizer.normalize(expectedName, Normalizer.Form.NFD); + } + assertThat(URIUtil.getUriLastPathSegment(uri), is(expectedName)); + } + + public static Stream uriLastSegmentSource() throws URISyntaxException, IOException + { + final String TEST_RESOURCE_JAR = "test-base-resource.jar"; + Path testJar = MavenTestingUtils.getTestResourcePathFile(TEST_RESOURCE_JAR); + URI uri = new URI("jar", testJar.toUri().toASCIIString(), null); + + Map env = new HashMap<>(); + env.put("multi-release", "runtime"); + + List arguments = new ArrayList<>(); + arguments.add(Arguments.of(uri, TEST_RESOURCE_JAR)); + try (FileSystem zipFs = FileSystems.newFileSystem(uri, env)) + { + FileVisitOption[] fileVisitOptions = new FileVisitOption[]{}; + + for (Path root : zipFs.getRootDirectories()) + { + Stream entryStream = Files.find(root, 10, (path, attrs) -> true, fileVisitOptions); + entryStream.forEach((path) -> + { + if (path.toString().endsWith("!/")) + { + // skip - JAR entry type not supported by Jetty + // TODO: re-enable once we start to use zipfs + LOG.warn("Skipping Unsupported entry: " + path.toUri()); + } + else + { + arguments.add(Arguments.of(path.toUri(), TEST_RESOURCE_JAR)); + } + }); + } + } + + return arguments.stream(); + } + + /** + * Tests of URIs last segment, including "jar:file:" based URIs. + */ + @ParameterizedTest + @MethodSource("uriLastSegmentSource") + public void testGetUriLastPathSegment(URI uri, String expectedName) + { + assertThat(URIUtil.getUriLastPathSegment(uri), is(expectedName)); + } } diff --git a/jetty-webapp/src/test/resources/test-base-resource.jar b/jetty-util/src/test/resources/test-base-resource.jar similarity index 100% rename from jetty-webapp/src/test/resources/test-base-resource.jar rename to jetty-util/src/test/resources/test-base-resource.jar diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java index 0d3205f5fed..79cd050c84b 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java @@ -844,33 +844,7 @@ public class WebInfConfiguration extends AbstractConfiguration return ""; } - return getUriLastPathSegment(uri); - } - - protected static String getUriLastPathSegment(URI uri) - { - String ssp = uri.getSchemeSpecificPart(); - // strip off deep jar:file: reference information - int idx = ssp.indexOf("!/"); - if (idx != -1) - { - ssp = ssp.substring(0, idx); - } - - // Strip off trailing '/' if present - if (ssp.endsWith("/")) - { - ssp = ssp.substring(0, ssp.length() - 1); - } - - // Only interested in last segment - idx = ssp.lastIndexOf('/'); - if (idx != -1) - { - ssp = ssp.substring(idx + 1); - } - - return ssp; + return URIUtil.getUriLastPathSegment(uri); } protected List findClassDirs(WebAppContext context) diff --git a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebInfConfigurationTest.java b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebInfConfigurationTest.java index 28c1f5a0aa7..446ec9e8180 100644 --- a/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebInfConfigurationTest.java +++ b/jetty-webapp/src/test/java/org/eclipse/jetty/webapp/WebInfConfigurationTest.java @@ -19,27 +19,14 @@ package org.eclipse.jetty.webapp; import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.FileVisitOption; -import java.nio.file.Files; import java.nio.file.Path; -import java.text.Normalizer; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.stream.Stream; 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.JavaVersion; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.util.resource.Resource; import org.junit.jupiter.api.Test; @@ -47,7 +34,6 @@ import org.junit.jupiter.api.condition.DisabledOnJre; import org.junit.jupiter.api.condition.EnabledIfSystemProperty; import org.junit.jupiter.api.condition.EnabledOnJre; import org.junit.jupiter.api.condition.JRE; -import org.junit.jupiter.api.condition.OS; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -66,9 +52,6 @@ import static org.junit.jupiter.api.Assertions.assertEquals; @ExtendWith(WorkDirExtension.class) public class WebInfConfigurationTest { - private static final Logger LOG = Log.getLogger(WebInfConfigurationTest.class); - private static final String TEST_RESOURCE_JAR = "test-base-resource.jar"; - public WorkDir workDir; /** @@ -197,96 +180,4 @@ public class WebInfConfigurationTest Resource resource = new PathResource(base); assertThat(WebInfConfiguration.getResourceBaseName(resource), is(expectedName)); } - - public static Stream fileUriBaseResourceNames() - { - return Stream.of( - Arguments.of("test.war", "test.war"), - Arguments.of("a/b/c/test.war", "test.war"), - Arguments.of("bar%2Fbaz/test.war", "test.war"), - Arguments.of("fizz buzz/test.war", "test.war"), - Arguments.of("another one/bites the dust/", "bites the dust"), - Arguments.of("another+one/bites+the+dust/", "bites+the+dust"), - Arguments.of("another%20one/bites%20the%20dust/", "bites%20the%20dust"), - Arguments.of("spanish/n\u00FAmero.war", "n\u00FAmero.war"), - Arguments.of("spanish/n%C3%BAmero.war", "n%C3%BAmero.war"), - Arguments.of("a/b!/", "b"), - Arguments.of("a/b!/c/", "b"), - Arguments.of("a/b!/c/d/", "b"), - Arguments.of("a/b%21/", "b%21") - ); - } - - /** - * Similar to testPathGetResourceBaseName, but with "file:" URIs - */ - @ParameterizedTest - @MethodSource("fileUriBaseResourceNames") - public void testFileUriGetUriLastPathSegment(String basePath, String expectedName) throws IOException - { - Path root = workDir.getPath(); - Path base = root.resolve(basePath); - if (basePath.endsWith("/")) - { - FS.ensureDirExists(base); - } - else - { - FS.ensureDirExists(base.getParent()); - FS.touch(base); - } - URI uri = base.toUri(); - if (OS.MAC.isCurrentOs()) - { - // Normalize Unicode to NFD form that OSX Path/FileSystem produces - expectedName = Normalizer.normalize(expectedName, Normalizer.Form.NFD); - } - assertThat(WebInfConfiguration.getUriLastPathSegment(uri), is(expectedName)); - } - - public static Stream uriLastSegmentSource() throws URISyntaxException, IOException - { - Path testJar = MavenTestingUtils.getTestResourcePathFile(TEST_RESOURCE_JAR); - URI uri = new URI("jar", testJar.toUri().toASCIIString(), null); - - Map env = new HashMap<>(); - env.put("multi-release", "runtime"); - - List arguments = new ArrayList<>(); - arguments.add(Arguments.of(uri, TEST_RESOURCE_JAR)); - try (FileSystem zipFs = FileSystems.newFileSystem(uri, env)) - { - FileVisitOption[] fileVisitOptions = new FileVisitOption[]{}; - - for (Path root : zipFs.getRootDirectories()) - { - Stream entryStream = Files.find(root, 10, (path, attrs) -> true, fileVisitOptions); - entryStream.forEach((path) -> - { - if (path.toString().endsWith("!/")) - { - // skip - JAR entry type not supported by Jetty - // TODO: re-enable once we start to use zipfs - LOG.warn("Skipping Unsupported entry: " + path.toUri()); - } - else - { - arguments.add(Arguments.of(path.toUri(), TEST_RESOURCE_JAR)); - } - }); - } - } - - return arguments.stream(); - } - - /** - * Tests of URIs last segment, including "jar:file:" based URIs. - */ - @ParameterizedTest - @MethodSource("uriLastSegmentSource") - public void testGetUriLastPathSegment(URI uri, String expectedName) throws IOException - { - assertThat(WebInfConfiguration.getUriLastPathSegment(uri), is(expectedName)); - } }