This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch 10.1.x in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit dad31835dbb9947a6800c22f2be941e4248e473f 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 e875855b48..0f967958ed 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,12 +321,17 @@ 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 (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; } @@ -346,6 +353,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 cc0032e099..a61d82d484 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; @@ -153,6 +157,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]
