This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 11.0.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 0ac68e43a71824b48d1549f76a3ebf81043c8130 Author: opencode <[email protected]> AuthorDate: Mon Oct 19 12:26:56 2026 +0200 Resolve JspC resources whose path contains URI-illegal characters JspCServletContext.getResource() built the resource URI by raw string concatenation of the encoded base URL and the decoded logical resource path. Any path containing characters that are not permitted in a URI, for example a space or a non-ASCII character, produced a URISyntaxException that was swallowed by a catch-all, so the resource was silently reported as not found and JspC failed with a confusing resource not found error, even though the same application resolved and compiled fine at runtime. Percent-encode each segment of the path before appending it to the base URL, matching the behaviour of the runtime container. With that in place, only an IOException (openStream failing or a malformed URL) and a URISyntaxException indicate a missing resource, so the catch-all that previously converted any Throwable, including Errors, into a null result could be narrowed. Add a unit test with a space and a non-ASCII character in the resource name. Found by code review. --- .../apache/jasper/servlet/JspCServletContext.java | 37 ++++++++++++++++-- .../jasper/servlet/TestJspCServletContext.java | 44 ++++++++++++++++++++++ 2 files changed, 78 insertions(+), 3 deletions(-) diff --git a/java/org/apache/jasper/servlet/JspCServletContext.java b/java/org/apache/jasper/servlet/JspCServletContext.java index 6a94a040c0..8663cbec8a 100644 --- a/java/org/apache/jasper/servlet/JspCServletContext.java +++ b/java/org/apache/jasper/servlet/JspCServletContext.java @@ -24,6 +24,8 @@ import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -319,13 +321,18 @@ public class JspCServletContext implements ServletContext { URL url; try { - URI uri = new URI(myResourceBaseURL.toExternalForm() + path); + // The path is a decoded logical path. Percent-encode it before + // appending it to the (already encoded) base URL so that + // resources whose name contains characters that are not + // permitted in a URI, for example a space, can be resolved, as + // they are by the runtime container. + URI uri = new URI(myResourceBaseURL.toExternalForm() + encodePath(path)); url = uri.toURL(); try (@SuppressWarnings("unused") InputStream is = url.openStream()) { } - } catch (Throwable t) { - ExceptionUtils.handleThrowable(t); + } catch (IOException | URISyntaxException e) { + // The resource does not exist or the URL could not be built. url = null; } @@ -347,6 +354,30 @@ public class JspCServletContext implements ServletContext { } + /* + * Percent-encodes each segment of the given decoded resource path so the + * result can be appended to an already encoded base URL. + */ + private static String encodePath(String path) { + StringBuilder result = new StringBuilder(path.length()); + int start = 0; + for (;;) { + int slash = path.indexOf('/', start); + String segment = slash < 0 ? path.substring(start) : path.substring(start, slash); + // URLEncoder implements form encoding. It uses '+' for a space, + // which is not equivalent in a path segment. A literal '+' in the + // input is encoded as "%2B", so any '+' in the output is a space. + result.append(URLEncoder.encode(segment, StandardCharsets.UTF_8).replace("+", "%20")); + if (slash < 0) { + break; + } + result.append('/'); + start = slash + 1; + } + return result.toString(); + } + + @Override public InputStream getResourceAsStream(String path) { try { diff --git a/test/org/apache/jasper/servlet/TestJspCServletContext.java b/test/org/apache/jasper/servlet/TestJspCServletContext.java index 7f24ba47e9..dc6f222e5e 100644 --- a/test/org/apache/jasper/servlet/TestJspCServletContext.java +++ b/test/org/apache/jasper/servlet/TestJspCServletContext.java @@ -17,6 +17,10 @@ package org.apache.jasper.servlet; import java.io.File; +import java.io.InputStream; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.Collection; import java.util.Iterator; import java.util.Set; @@ -163,6 +167,46 @@ public class TestJspCServletContext { } + @Test + public void testGetResourceWithUriIllegalCharacters() throws Exception { + // A resource path is a decoded logical path. Resources whose name + // contains characters that are not permitted in a URI, for example a + // space or a non-ASCII character, resolve correctly at runtime so + // they must also resolve for the offline compiler rather than be + // silently reported as not found. + File docBase = Files.createTempDirectory("jspc-illegal-chars").toFile(); + try { + Files.writeString(new File(docBase, "my file.jsp").toPath(), + "<jsp/>", StandardCharsets.UTF_8); + Files.writeString(new File(docBase, "unicode-\u00fc.jsp").toPath(), + "<jsp/>", StandardCharsets.UTF_8); + JspCServletContext context = new JspCServletContext( + null, docBase.toURI().toURL(), null, false, false); + + URL url = context.getResource("/my file.jsp"); + Assert.assertNotNull(url); + try (InputStream is = url.openStream()) { + Assert.assertEquals("<jsp/>", + new String(is.readAllBytes(), StandardCharsets.UTF_8)); + } + Assert.assertNotNull(context.getResource("/unicode-\u00fc.jsp")); + } finally { + deleteRecursively(docBase); + } + } + + + private static void deleteRecursively(File file) { + File[] children = file.listFiles(); + if (children != null) { + for (File child : children) { + deleteRecursively(child); + } + } + Assert.assertTrue(file.delete()); + } + + @Test public void testResourceJARs() throws Exception { File appDir = new File("test/webapp-fragments"); --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
