Skip to content

FileSystemPool fails to mount a jar located under a non-ASCII path ("Bad escape") #15289

@sbernard31

Description

@sbernard31

Jetty version(s)
Tested with 12.0.33
Artemis project seems to face the same with 12.0.15

Jetty Environment
ee10 (in my case)

HTTP version

Java version/vendor (use: java -version)
openjdk 17.0.19 2026-04-21
OpenJDK Runtime Environment Temurin-17.0.19+10 (build 17.0.19+10)
OpenJDK 64-Bit Server VM Temurin-17.0.19+10 (build 17.0.19+10, mixed mode, sharing)

OS type/version
Fedora 44

Description
I have an application which embed a jetty server and which serve statically a folder in a Jar.
When I run this application in a folder where path contains "accent" character, then I face :

java.lang.IllegalArgumentException: Bad escape
	at java.base/sun.nio.fs.UnixUriUtils.fromUri(UnixUriUtils.java:88)
	at java.base/sun.nio.fs.UnixFileSystemProvider.getPath(UnixFileSystemProvider.java:102)
	at java.base/java.nio.file.Path.of(Path.java:203)
	at java.base/java.nio.file.Paths.get(Paths.java:98)
	at org.eclipse.jetty.util.resource.FileSystemPool$Bucket.<init>(FileSystemPool.java:400)
	at org.eclipse.jetty.util.resource.FileSystemPool.retain(FileSystemPool.java:311)
	at org.eclipse.jetty.util.resource.FileSystemPool.mount(FileSystemPool.java:140)
	at org.eclipse.jetty.util.resource.ResourceFactoryInternals$CompositeResourceFactory.mountIfNeeded(ResourceFactoryInternals.java:268)
	at org.eclipse.jetty.util.resource.ResourceFactoryInternals$CompositeResourceFactory.newResource(ResourceFactoryInternals.java:225)
	at org.eclipse.jetty.util.resource.ResourceFactoryInternals$LifeCycle.newResource(ResourceFactoryInternals.java:171)
	at org.eclipse.leshan.demo.server.LeshanServerDemo.createJettyServer(LeshanServerDemo.java:307)
	at org.eclipse.leshan.demo.server.LeshanServerDemo.main(LeshanServerDemo.java:117)

OR

java.lang.IllegalArgumentException: Bad escape
	at java.base/sun.nio.fs.UnixUriUtils.fromUri(UnixUriUtils.java:88)
	at java.base/sun.nio.fs.UnixFileSystemProvider.getPath(UnixFileSystemProvider.java:102)
	at java.base/java.nio.file.Path.of(Path.java:203)
	at java.base/java.nio.file.Paths.get(Paths.java:98)
	at org.eclipse.jetty.util.resource.FileSystemPool$Bucket.<init>(FileSystemPool.java:400)
	at org.eclipse.jetty.util.resource.FileSystemPool.retain(FileSystemPool.java:311)
	at org.eclipse.jetty.util.resource.FileSystemPool.mount(FileSystemPool.java:140)
	at org.eclipse.jetty.util.resource.ResourceFactoryInternals$CompositeResourceFactory.mountIfNeeded(ResourceFactoryInternals.java:268)
	at org.eclipse.jetty.util.resource.ResourceFactoryInternals$CompositeResourceFactory.newResource(ResourceFactoryInternals.java:225)
	at org.eclipse.jetty.util.resource.ResourceFactoryInternals$LifeCycle.newResource(ResourceFactoryInternals.java:171)
	at org.eclipse.jetty.util.resource.ResourceFactory.newResource(ResourceFactory.java:344)
	at org.eclipse.jetty.ee10.servlet.ServletContextHandler.newResource(ServletContextHandler.java:810)
	at org.eclipse.jetty.ee10.servlet.ResourceServlet.init(ResourceServlet.java:223)
	at org.eclipse.jetty.ee10.servlet.DefaultServlet.init(DefaultServlet.java:78)
	at jakarta.servlet.GenericServlet.init(GenericServlet.java:178)
	at jakarta.servlet.http.HttpServlet.init(HttpServlet.java:107)
	at org.eclipse.jetty.ee10.servlet.ServletHolder.initServlet(ServletHolder.java:639)
	at org.eclipse.jetty.ee10.servlet.ServletHolder.initialize(ServletHolder.java:426)
	at org.eclipse.jetty.ee10.servlet.ServletHandler.lambda$initialize$1(ServletHandler.java:666)
	at org.eclipse.jetty.util.ExceptionUtil.call(ExceptionUtil.java:369)
	at org.eclipse.jetty.util.ExceptionUtil$MultiException.callAndCatch(ExceptionUtil.java:305)
	at org.eclipse.jetty.ee10.servlet.ServletHandler.lambda$initialize$0(ServletHandler.java:663)
	at java.base/java.util.stream.SortedOps$SizedRefSortingSink.end(SortedOps.java:357)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:510)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310)
	at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735)
	at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762)
	at org.eclipse.jetty.ee10.servlet.ServletHandler.initialize(ServletHandler.java:686)
	at org.eclipse.jetty.ee10.servlet.ServletContextHandler.startContext(ServletContextHandler.java:1325)
	at org.eclipse.jetty.ee10.servlet.ServletContextHandler.lambda$doStart$0(ServletContextHandler.java:1051)
	at org.eclipse.jetty.server.handler.ContextHandler$ScopedContext.call(ContextHandler.java:1494)
	at org.eclipse.jetty.ee10.servlet.ServletContextHandler.doStart(ServletContextHandler.java:1048)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:169)
	at org.eclipse.jetty.server.Server.start(Server.java:643)
	at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:120)
	at org.eclipse.jetty.server.Handler$Abstract.doStart(Handler.java:491)
	at org.eclipse.jetty.server.Server.doStart(Server.java:584)
	at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93)
	at org.eclipse.leshan.demo.server.LeshanServerDemo.main(LeshanServerDemo.java:132)

I'm not 100% sure but It could be a bug ?

Searching on the web I see that another project faced that : https://issues.apache.org/jira/browse/ARTEMIS-5461

How to reproduce?

I'm able to reproduce with

package org.eclipse.leshan.demo.server;

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;

import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;

/**
 * Minimal reproducer for: IllegalArgumentException "Bad escape" thrown from FileSystemPool$Bucket -> Paths.get(URI) ->
 * UnixUriUtils.fromUri when mounting a jar that lives in a directory whose name contains non-ASCII characters.
 *
 * Build/run (adjust the jetty-util version/path to match your setup):
 *
 * JETTY_UTIL=~/.m2/repository/org/eclipse/jetty/jetty-util/12.0.33/jetty-util-12.0.33.jar javac -cp "$JETTY_UTIL"
 * JettyFileSystemPoolReproduction.java java -cp ".:$JETTY_UTIL" JettyFileSystemPoolReproduction
 *
 * Expected: prints "OK ...". Actual (12.0.33, Linux, OpenJDK 21): IllegalArgumentException: Bad escape.
 *
 * Sanity check: change the directory name on line ~31 to an ASCII-only name (e.g. "plain-ascii-dir") and the mount
 * succeeds — confirming the trigger is the non-ASCII characters in the container path, not the jar contents.
 */
public class JettyFileSystemPoolReproduction {

    public static void main(String[] args) throws Exception {
        // 1. A directory whose name contains spaces AND a non-ASCII character.
        Path base = Files.createTempDirectory("jetty-repro-");
        Path dir = Files.createDirectory(base.resolve("un dossier avec dés accents"));
        Path jar = dir.resolve("app.jar");

        // 2. A trivial jar containing a "webapp/" directory entry.
        try (JarOutputStream out = new JarOutputStream(Files.newOutputStream(jar))) {
            out.putNextEntry(new JarEntry("webapp/"));
            out.closeEntry();
            out.putNextEntry(new JarEntry("webapp/index.html"));
            out.write("<html>ok</html>".getBytes(StandardCharsets.UTF_8));
            out.closeEntry();
        }

        // 3. The jar: URI for the bundled "webapp" directory — exactly the form
        // ClassLoader.getResource("webapp") yields (path is percent-encoded).
        URI webappUri = URI.create("jar:" + jar.toUri() + "!/webapp");
        System.out.println("jar.toUri()  = " + jar.toUri());
        System.out.println("mounting     = " + webappUri);

        // 4. Hand the (correctly percent-encoded) URI to Jetty.
        // FileSystemPool mounts the zip filesystem via Paths.get(<container URI>),
        // which throws "Bad escape" when the container path has non-ASCII chars.
        try (ResourceFactory.Closeable factory = ResourceFactory.closeable()) {
            Resource r = factory.newResource(webappUri);
            System.out.println("OK: " + r + " (exists=" + r.exists() + ")");
        }
    }
}

Metadata

Metadata

Assignees

Labels

BugFor general bugs on Jetty side

Type

No type
No fields configured for issues without a type.

Projects

Status
✅ Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions