From eeeb6acf88cde62fb93f3a8f67ec86057aab343e Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 3 Nov 2022 10:21:09 -0500 Subject: [PATCH 1/9] General cleanup of URIUtil + Removal of __CHARSET + Removal of unused methods + Using new switch/case concepts --- .../java/org/eclipse/jetty/util/URIUtil.java | 228 +++++++----------- 1 file changed, 89 insertions(+), 139 deletions(-) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java index ce4540d0aa60..f449b2073858 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java @@ -18,7 +18,6 @@ import java.net.URI; import java.net.URL; import java.net.URLClassLoader; -import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; @@ -59,9 +58,6 @@ public final class URIUtil public static final String HTTP = "http"; public static final String HTTPS = "https"; - // Use UTF-8 as per http://www.w3.org/TR/html40/appendix/notes.html#non-ascii-chars - public static final Charset __CHARSET = StandardCharsets.UTF_8; - /** * The characters that are supported by the URI class and that can be decoded by {@link #canonicalPath(String)} */ @@ -206,7 +202,7 @@ private URIUtil() * This is the same encoding offered by URLEncoder, except that * the '/' character is not encoded. * - * @param path The path the encode + * @param path The path to encode * @return The encoded path */ public static String encodePath(String path) @@ -214,36 +210,24 @@ public static String encodePath(String path) if (path == null || path.length() == 0) return path; - StringBuilder buf = encodePath(null, path, 0); + StringBuilder buf = encodePath(null, path); return buf == null ? path : buf.toString(); } /** * Encode a URI path. * - * @param path The path the encode + * @param path The path to encode * @param buf StringBuilder to encode path into (or null) * @return The StringBuilder or null if no substitutions required. */ public static StringBuilder encodePath(StringBuilder buf, String path) - { - return encodePath(buf, path, 0); - } - - /** - * Encode a URI path. - * - * @param path The path the encode - * @param buf StringBuilder to encode path into (or null) - * @return The StringBuilder or null if no substitutions required. - */ - private static StringBuilder encodePath(StringBuilder buf, String path, int offset) { byte[] bytes = null; if (buf == null) { loop: - for (int i = offset; i < path.length(); i++) + for (int i = 0; i < path.length(); i++) { char c = path.charAt(i); switch (c) @@ -270,7 +254,7 @@ private static StringBuilder encodePath(StringBuilder buf, String path, int offs default: if (c < 0x20 || c >= 0x7f) { - bytes = path.getBytes(URIUtil.__CHARSET); + bytes = path.getBytes(StandardCharsets.UTF_8); buf = new StringBuilder(path.length() * 2); break loop; } @@ -283,70 +267,37 @@ private static StringBuilder encodePath(StringBuilder buf, String path, int offs int i; loop: - for (i = offset; i < path.length(); i++) + for (i = 0; i < path.length(); i++) { char c = path.charAt(i); switch (c) { - case '%': - buf.append("%25"); - continue; - case '?': - buf.append("%3F"); - continue; - case ';': - buf.append("%3B"); - continue; - case '#': - buf.append("%23"); - continue; - case '"': - buf.append("%22"); - continue; - case '\'': - buf.append("%27"); - continue; - case '<': - buf.append("%3C"); - continue; - case '>': - buf.append("%3E"); - continue; - case ' ': - buf.append("%20"); - continue; - case '[': - buf.append("%5B"); - continue; - case '\\': - buf.append("%5C"); - continue; - case ']': - buf.append("%5D"); - continue; - case '^': - buf.append("%5E"); - continue; - case '`': - buf.append("%60"); - continue; - case '{': - buf.append("%7B"); - continue; - case '|': - buf.append("%7C"); - continue; - case '}': - buf.append("%7D"); - continue; - - default: + case '%' -> buf.append("%25"); + case '?' -> buf.append("%3F"); + case ';' -> buf.append("%3B"); + case '#' -> buf.append("%23"); + case '"' -> buf.append("%22"); + case '\'' -> buf.append("%27"); + case '<' -> buf.append("%3C"); + case '>' -> buf.append("%3E"); + case ' ' -> buf.append("%20"); + case '[' -> buf.append("%5B"); + case '\\' -> buf.append("%5C"); + case ']' -> buf.append("%5D"); + case '^' -> buf.append("%5E"); + case '`' -> buf.append("%60"); + case '{' -> buf.append("%7B"); + case '|' -> buf.append("%7C"); + case '}' -> buf.append("%7D"); + default -> + { if (c < 0x20 || c >= 0x7f) { - bytes = path.getBytes(URIUtil.__CHARSET); + bytes = path.getBytes(StandardCharsets.UTF_8); break loop; } buf.append(c); + } } } @@ -510,41 +461,40 @@ public static String decodeSpecific(String str, String charsToDecode) for (int i = idx; i < len; i++) { char c = str.charAt(i); - switch (c) + if (c == '%') { - case '%': - if ((i + 2) < len) + if ((i + 2) < len) + { + char u = str.charAt(i + 1); + char l = str.charAt(i + 2); + char result = (char)(0xff & (TypeUtil.convertHexDigit(u) * 16 + TypeUtil.convertHexDigit(l))); + boolean decoded = false; + for (char f : find) { - char u = str.charAt(i + 1); - char l = str.charAt(i + 2); - char result = (char)(0xff & (TypeUtil.convertHexDigit(u) * 16 + TypeUtil.convertHexDigit(l))); - boolean decoded = false; - for (char f : find) - { - if (f == result) - { - ret.append(result); - decoded = true; - break; - } - } - if (decoded) + if (f == result) { - i += 2; - } - else - { - ret.append(c); + ret.append(result); + decoded = true; + break; } } + if (decoded) + { + i += 2; + } else { - throw new IllegalArgumentException("Bad URI % encoding"); + ret.append(c); } - break; - default: - ret.append(c); - break; + } + else + { + throw new IllegalArgumentException("Bad URI % encoding"); + } + } + else + { + ret.append(c); } } return ret.toString(); @@ -1189,7 +1139,6 @@ public static String normalizePathQuery(String pathQuery) case '.': if (slash) break loop; - slash = false; break; case '?': @@ -1306,18 +1255,13 @@ public static String normalizePath(String path) char c = path.charAt(i); switch (c) { - case '/': - slash = true; - break; - - case '.': + case '/' -> slash = true; + case '.' -> + { if (slash) break loop; - slash = false; - break; - - default: - slash = false; + } + default -> slash = false; } i++; @@ -1339,14 +1283,15 @@ public static String normalizePath(String path) char c = path.charAt(i); switch (c) { - case '/': + case '/' -> + { if (doDotsSlash(canonical, dots)) return null; slash = true; dots = 0; - break; - - case '.': + } + case '.' -> + { // Count dots only if they are leading in the segment if (dots > 0) dots++; @@ -1355,15 +1300,16 @@ else if (slash) else canonical.append('.'); slash = false; - break; - - default: + } + default -> + { // Add leading dots to the path while (dots-- > 0) canonical.append('.'); canonical.append(c); dots = 0; slash = false; + } } i++; } @@ -1582,27 +1528,24 @@ public static void appendSchemeHostPort(StringBuilder url, String scheme, String */ public static void appendSchemeHostPort(StringBuffer url, String scheme, String server, int port) { - synchronized (url) - { - url.append(scheme).append("://").append(HostPort.normalizeHost(server)); + url.append(scheme).append("://").append(HostPort.normalizeHost(server)); - if (port > 0) + if (port > 0) + { + switch (scheme) { - switch (scheme) - { - case "http": - if (port != 80) - url.append(':').append(port); - break; - - case "https": - if (port != 443) - url.append(':').append(port); - break; + case "http": + if (port != 80) + url.append(':').append(port); + break; - default: + case "https": + if (port != 443) url.append(':').append(port); - } + break; + + default: + url.append(':').append(port); } } } @@ -1973,9 +1916,12 @@ public static List split(String str) } /** + *

* Take an arbitrary URI and provide a URI that is suitable for mounting the URI as a Java FileSystem. - * + *

+ *

* The resulting URI will point to the {@code jar:file://foo.jar!/} said Java Archive (jar, war, or zip) + *

* * @param uri the URI to mutate to a {@code jar:file:...} URI. * @return the jar:${uri_to_java_archive}!/${internal-reference} URI or the unchanged URI if not a Java Archive. @@ -2034,9 +1980,13 @@ public static URI toURI(String resource) } /** + *

* Unwrap a URI to expose its container path reference. + *

* + *

* Take out the container archive name URI from a {@code jar:file:${container-name}!/} URI. + *

* * @param uri the input URI * @return the container String if a {@code jar} scheme, or just the URI untouched. From df43f673c6a9e9bad64d439a635bc824f9d8b59e Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 3 Nov 2022 12:14:03 -0500 Subject: [PATCH 2/9] More cleanup of URIUtil constants --- .../deploy/providers/ContextProvider.java | 5 ++-- .../eclipse/jetty/deploy/MockAppProvider.java | 3 +-- .../jetty/http/pathmap/ServletPathSpec.java | 8 +++--- .../eclipse/jetty/server/ResourceListing.java | 2 +- .../eclipse/jetty/server/ResourceService.java | 4 +-- .../java/org/eclipse/jetty/util/URIUtil.java | 26 ++++++++----------- .../jetty/util/resource/CombinedResource.java | 2 +- .../jetty/util/resource/PathResource.java | 6 ++--- .../security/openid/OpenIdAuthenticator.java | 2 +- .../ee10/servlet/ServletContextHandler.java | 10 +++---- .../authentication/FormAuthenticator.java | 2 +- .../jetty/ee10/webapp/WebAppContext.java | 2 +- .../jetty/ee9/nested/ContextHandler.java | 10 +++---- .../jetty/ee9/nested/ResourceService.java | 4 +-- .../security/openid/OpenIdAuthenticator.java | 2 +- .../authentication/FormAuthenticator.java | 4 +-- .../jetty/ee9/webapp/WebAppContext.java | 2 +- 17 files changed, 44 insertions(+), 50 deletions(-) diff --git a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java index 9e6b6d71d887..4efdd1e0f822 100644 --- a/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java +++ b/jetty-core/jetty-deploy/src/main/java/org/eclipse/jetty/deploy/providers/ContextProvider.java @@ -36,7 +36,6 @@ import org.eclipse.jetty.util.FileID; import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.StringUtil; -import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.Environment; @@ -481,7 +480,7 @@ protected void initializeContextPath(ContextHandler context, Path path) // special case of archive (or dir) named "root" is / context if (contextPath.equalsIgnoreCase("root")) { - contextPath = URIUtil.SLASH; + contextPath = "/"; } // handle root with virtual host form else if (StringUtil.startsWithIgnoreCase(contextPath, "root-")) @@ -489,7 +488,7 @@ else if (StringUtil.startsWithIgnoreCase(contextPath, "root-")) int dash = contextPath.indexOf('-'); String virtual = contextPath.substring(dash + 1); context.setVirtualHosts(Arrays.asList(virtual.split(","))); - contextPath = URIUtil.SLASH; + contextPath = "/"; } // Ensure "/" is Prepended to all context paths. diff --git a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java index 989cc15ac908..bd76e5f24361 100644 --- a/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java +++ b/jetty-core/jetty-deploy/src/test/java/org/eclipse/jetty/deploy/MockAppProvider.java @@ -18,7 +18,6 @@ import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.FileID; -import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.component.Environment; @@ -71,7 +70,7 @@ public ContextHandler createContextHandler(App app) // special case of archive (or dir) named "root" is / context if (path.equalsIgnoreCase("root") || path.equalsIgnoreCase("root/")) - path = URIUtil.SLASH; + path = "/"; // Ensure "/" is Prepended to all context paths. if (path.charAt(0) != '/') diff --git a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java index e50f077e2a44..7eafc547ac30 100644 --- a/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java +++ b/jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/pathmap/ServletPathSpec.java @@ -177,15 +177,15 @@ public static String relativePath(String base, if (info.startsWith("./")) info = info.substring(2); - if (base.endsWith(URIUtil.SLASH)) - if (info.startsWith(URIUtil.SLASH)) + if (base.endsWith("/")) + if (info.startsWith("/")) path = base + info.substring(1); else path = base + info; - else if (info.startsWith(URIUtil.SLASH)) + else if (info.startsWith("/")) path = base + info; else - path = base + URIUtil.SLASH + info; + path = base + "/" + info; return path; } diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceListing.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceListing.java index 3e61300f3183..993177e37527 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceListing.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/ResourceListing.java @@ -225,7 +225,7 @@ public static String getAsXHTML(Resource resource, String base, boolean parent, // Ensure name has a slash if it's a directory if (item.isDirectory() && !name.endsWith("/")) - name += URIUtil.SLASH; + name += "/"; // Name buf.append(" reqRanges = request.getHeaders().getValuesList(HttpHeader.RANGE.asString()); - boolean endsWithSlash = pathInContext.endsWith(URIUtil.SLASH); + boolean endsWithSlash = pathInContext.endsWith("/"); boolean checkPrecompressedVariants = _precompressedFormats.size() > 0 && !endsWithSlash && reqRanges.isEmpty(); try @@ -552,7 +552,7 @@ private void sendDirectory(Request request, Response response, HttpContent httpC return; } - String base = URIUtil.addEncodedPaths(request.getHttpURI().getPath(), URIUtil.SLASH); + String base = URIUtil.addEncodedPaths(request.getHttpURI().getPath(), "/"); String listing = ResourceListing.getAsXHTML(httpContent.getResource(), base, pathInContext.length() > 1, request.getHttpURI().getQuery()); if (listing == null) { diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java index f449b2073858..74441b5a4987 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java @@ -54,14 +54,10 @@ public final class URIUtil .with("jar:") .build(); - public static final String SLASH = "/"; - public static final String HTTP = "http"; - public static final String HTTPS = "https"; - /** * The characters that are supported by the URI class and that can be decoded by {@link #canonicalPath(String)} */ - public static final boolean[] __uriSupportedCharacters = new boolean[] + private static final boolean[] URI_SUPPORTED_CHARACTERS = new boolean[] { false, // 0x00 is illegal false, // 0x01 is illegal @@ -680,7 +676,7 @@ private static boolean isSafe(int code) { // Allow any 8-bit character (as it's likely unicode). // or any character labeled with true in __uriSupportedCharacters static - return (code >= __uriSupportedCharacters.length || __uriSupportedCharacters[code]); + return (code >= URI_SUPPORTED_CHARACTERS.length || URI_SUPPORTED_CHARACTERS[code]); } /** @@ -969,7 +965,7 @@ public static String addEncodedPaths(String p1, String p2) if (buf.charAt(split - 1) == '/') { - if (p2.startsWith(URIUtil.SLASH)) + if (p2.startsWith("/")) { buf.deleteCharAt(split - 1); buf.insert(split - 1, p2); @@ -979,7 +975,7 @@ public static String addEncodedPaths(String p1, String p2) } else { - if (p2.startsWith(URIUtil.SLASH)) + if (p2.startsWith("/")) buf.insert(split, p2); else { @@ -1011,8 +1007,8 @@ public static String addPaths(String p1, String p2) if (p2 == null || p2.length() == 0) return p1; - boolean p1EndsWithSlash = p1.endsWith(SLASH); - boolean p2StartsWithSlash = p2.startsWith(SLASH); + boolean p1EndsWithSlash = p1.endsWith("/"); + boolean p2StartsWithSlash = p2.startsWith("/"); if (p1EndsWithSlash && p2StartsWithSlash) { @@ -1025,15 +1021,15 @@ public static String addPaths(String p1, String p2) StringBuilder buf = new StringBuilder(p1.length() + p2.length() + 2); buf.append(p1); - if (p1.endsWith(SLASH)) + if (p1.endsWith("/")) { - if (p2.startsWith(SLASH)) + if (p2.startsWith("/")) buf.setLength(buf.length() - 1); } else { - if (!p2.startsWith(SLASH)) - buf.append(SLASH); + if (!p2.startsWith("/")) + buf.append("/"); } buf.append(p2); @@ -1099,7 +1095,7 @@ public static String getUriLastPathSegment(URI uri) */ public static String parentPath(String p) { - if (p == null || URIUtil.SLASH.equals(p)) + if (p == null || "/".equals(p)) return null; int slash = p.lastIndexOf('/', p.length() - 2); if (slash >= 0) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/CombinedResource.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/CombinedResource.java index 896f18fbe3b6..e264e06d9d17 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/CombinedResource.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/CombinedResource.java @@ -131,7 +131,7 @@ public Resource resolve(String subUriPath) if (URIUtil.isNotNormalWithinSelf(subUriPath)) throw new IllegalArgumentException(subUriPath); - if (subUriPath.length() == 0 || URIUtil.SLASH.equals(subUriPath)) + if (subUriPath.length() == 0 || "/".equals(subUriPath)) { return this; } diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java index dfeb444212d6..f334d2077294 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/resource/PathResource.java @@ -182,8 +182,8 @@ public static boolean isSameName(Path pathA, Path pathB) if (Files.isDirectory(path)) { String uriString = uri.toASCIIString(); - if (!uriString.endsWith(URIUtil.SLASH)) - uri = URIUtil.correctFileURI(URI.create(uriString + URIUtil.SLASH)); + if (!uriString.endsWith("/")) + uri = URIUtil.correctFileURI(URI.create(uriString + "/")); } this.path = path; @@ -282,7 +282,7 @@ public Resource resolve(String subUriPath) if (URIUtil.isNotNormalWithinSelf(subUriPath)) throw new IllegalArgumentException(subUriPath); - if (URIUtil.SLASH.equals(subUriPath)) + if ("/".equals(subUriPath)) return this; URI uri = getURI(); diff --git a/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticator.java b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticator.java index 22dd8d1cd32d..e34e8111b61c 100644 --- a/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticator.java +++ b/jetty-ee10/jetty-ee10-openid/src/main/java/org/eclipse/jetty/ee10/security/openid/OpenIdAuthenticator.java @@ -389,7 +389,7 @@ public Authentication validateRequest(Request req, Response res, Callback cb, bo String uri = request.getRequestURI(); if (uri == null) - uri = URIUtil.SLASH; + uri = "/"; mandatory |= isJSecurityCheck(uri); if (!mandatory) 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 30a32c7e9316..a124d5c083b4 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 @@ -786,7 +786,7 @@ public Map getLocaleEncodings() */ public Resource getResource(String pathInContext) throws MalformedURLException { - if (pathInContext == null || !pathInContext.startsWith(URIUtil.SLASH)) + if (pathInContext == null || !pathInContext.startsWith("/")) throw new MalformedURLException(pathInContext); Resource baseResource = getBaseResource(); @@ -838,8 +838,8 @@ public Set getResourcePaths(String path) { Resource resource = getResource(path); - if (!path.endsWith(URIUtil.SLASH)) - path = path + URIUtil.SLASH; + if (!path.endsWith("/")) + path = path + "/"; HashSet set = new HashSet<>(); for (Resource item: resource.list()) @@ -2889,9 +2889,9 @@ public String getRealPath(String path) if (path == null) return null; if (path.length() == 0) - path = URIUtil.SLASH; + path = "/"; else if (path.charAt(0) != '/') - path = URIUtil.SLASH + path; + path = "/" + path; try { diff --git a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/FormAuthenticator.java b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/FormAuthenticator.java index 6b10a2f03cd2..8ae24f46a722 100644 --- a/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/FormAuthenticator.java +++ b/jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/security/authentication/FormAuthenticator.java @@ -253,7 +253,7 @@ public Authentication validateRequest(Request req, Response res, Callback callba { nuri = servletApiRequest.getContextPath(); if (nuri.length() == 0) - nuri = URIUtil.SLASH; + nuri = "/"; } else nuri = savedURI.asString(); diff --git a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java index 7ac59b85dbc1..f881706aaddc 100644 --- a/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java +++ b/jetty-ee10/jetty-ee10-webapp/src/main/java/org/eclipse/jetty/ee10/webapp/WebAppContext.java @@ -378,7 +378,7 @@ public ResourceFactory getResourceFactory() @Override public Resource getResource(String pathInContext) throws MalformedURLException { - if (pathInContext == null || !pathInContext.startsWith(URIUtil.SLASH)) + if (pathInContext == null || !pathInContext.startsWith("/")) throw new MalformedURLException(pathInContext); MalformedURLException mue = null; 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 02ad570627da..54aa0096935d 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 @@ -1384,7 +1384,7 @@ public Map getLocaleEncodings() */ public Resource getResource(String pathInContext) throws MalformedURLException { - if (pathInContext == null || !pathInContext.startsWith(URIUtil.SLASH)) + if (pathInContext == null || !pathInContext.startsWith("/")) throw new MalformedURLException(pathInContext); Resource baseResource = _coreContextHandler.getBaseResource(); @@ -1476,8 +1476,8 @@ public Set getResourcePaths(String path) { Resource resource = getResource(path); - if (!path.endsWith(URIUtil.SLASH)) - path = path + URIUtil.SLASH; + if (!path.endsWith("/")) + path = path + "/"; HashSet set = new HashSet<>(); @@ -1814,9 +1814,9 @@ public String getRealPath(String path) if (path == null) return null; if (path.length() == 0) - path = URIUtil.SLASH; + path = "/"; else if (path.charAt(0) != '/') - path = URIUtil.SLASH + path; + path = "/" + path; try { diff --git a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceService.java b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceService.java index 6a6b97e39aef..9cbc5a23e266 100644 --- a/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceService.java +++ b/jetty-ee9/jetty-ee9-nested/src/main/java/org/eclipse/jetty/ee9/nested/ResourceService.java @@ -233,7 +233,7 @@ public boolean doGet(HttpServletRequest request, HttpServletResponse response) String pathInContext = URIUtil.addPaths(servletPath, pathInfo); - boolean endsWithSlash = (pathInfo == null ? (_pathInfoOnly ? "" : servletPath) : pathInfo).endsWith(URIUtil.SLASH); + boolean endsWithSlash = (pathInfo == null ? (_pathInfoOnly ? "" : servletPath) : pathInfo).endsWith("/"); boolean checkPrecompressedVariants = _precompressedFormats.length > 0 && !endsWithSlash && !included && reqRanges == null; HttpContent content = null; @@ -650,7 +650,7 @@ protected void sendDirectory(HttpServletRequest request, } byte[] data = null; - String base = URIUtil.addEncodedPaths(request.getRequestURI(), URIUtil.SLASH); + String base = URIUtil.addEncodedPaths(request.getRequestURI(), "/"); String dir = ResourceListing.getAsXHTML(resource, base, pathInContext.length() > 1, request.getQueryString()); if (dir == null) { diff --git a/jetty-ee9/jetty-ee9-openid/src/main/java/org/eclipse/jetty/ee9/security/openid/OpenIdAuthenticator.java b/jetty-ee9/jetty-ee9-openid/src/main/java/org/eclipse/jetty/ee9/security/openid/OpenIdAuthenticator.java index dfcc778a6dc5..2852d0f3212f 100644 --- a/jetty-ee9/jetty-ee9-openid/src/main/java/org/eclipse/jetty/ee9/security/openid/OpenIdAuthenticator.java +++ b/jetty-ee9/jetty-ee9-openid/src/main/java/org/eclipse/jetty/ee9/security/openid/OpenIdAuthenticator.java @@ -378,7 +378,7 @@ public Authentication validateRequest(ServletRequest req, ServletResponse res, b String uri = request.getRequestURI(); if (uri == null) - uri = URIUtil.SLASH; + uri = "/"; mandatory |= isJSecurityCheck(uri); if (!mandatory) diff --git a/jetty-ee9/jetty-ee9-security/src/main/java/org/eclipse/jetty/ee9/security/authentication/FormAuthenticator.java b/jetty-ee9/jetty-ee9-security/src/main/java/org/eclipse/jetty/ee9/security/authentication/FormAuthenticator.java index 5b058821a0a9..45ea2f4ad35f 100644 --- a/jetty-ee9/jetty-ee9-security/src/main/java/org/eclipse/jetty/ee9/security/authentication/FormAuthenticator.java +++ b/jetty-ee9/jetty-ee9-security/src/main/java/org/eclipse/jetty/ee9/security/authentication/FormAuthenticator.java @@ -242,7 +242,7 @@ public Authentication validateRequest(ServletRequest req, ServletResponse res, b String uri = request.getRequestURI(); if (uri == null) - uri = URIUtil.SLASH; + uri = "/"; mandatory |= isJSecurityCheck(uri); if (!mandatory) @@ -275,7 +275,7 @@ public Authentication validateRequest(ServletRequest req, ServletResponse res, b { nuri = request.getContextPath(); if (nuri.length() == 0) - nuri = URIUtil.SLASH; + nuri = "/"; } formAuth = new FormAuthentication(getAuthMethod(), user); } diff --git a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java index 06c5a80b501e..9d7afbded2d7 100644 --- a/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java +++ b/jetty-ee9/jetty-ee9-webapp/src/main/java/org/eclipse/jetty/ee9/webapp/WebAppContext.java @@ -387,7 +387,7 @@ public ResourceFactory getResourceFactory() @Override public Resource getResource(String pathInContext) throws MalformedURLException { - if (pathInContext == null || !pathInContext.startsWith(URIUtil.SLASH)) + if (pathInContext == null || !pathInContext.startsWith("/")) throw new MalformedURLException(pathInContext); MalformedURLException mue = null; From a39ac07d619d29c016c40c5373111149e0d4dd50 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Thu, 3 Nov 2022 12:24:07 -0500 Subject: [PATCH 3/9] More cleanup of URIUtil methods + collapse separate methods + tag methods only used in URIUtilTest --- .../java/org/eclipse/jetty/util/URIUtil.java | 276 ++++++++---------- .../org/eclipse/jetty/util/URIUtilTest.java | 29 +- 2 files changed, 128 insertions(+), 177 deletions(-) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java index 74441b5a4987..3d41757d9bfd 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java @@ -206,180 +206,155 @@ public static String encodePath(String path) if (path == null || path.length() == 0) return path; - StringBuilder buf = encodePath(null, path); - return buf == null ? path : buf.toString(); - } - - /** - * Encode a URI path. - * - * @param path The path to encode - * @param buf StringBuilder to encode path into (or null) - * @return The StringBuilder or null if no substitutions required. - */ - public static StringBuilder encodePath(StringBuilder buf, String path) - { + StringBuilder buf = null; byte[] bytes = null; - if (buf == null) - { - loop: - for (int i = 0; i < path.length(); i++) - { - char c = path.charAt(i); - switch (c) - { - case '%': - case '?': - case ';': - case '#': - case '"': - case '\'': - case '<': - case '>': - case ' ': - case '[': - case '\\': - case ']': - case '^': - case '`': - case '{': - case '|': - case '}': - buf = new StringBuilder(path.length() * 2); - break loop; - default: - if (c < 0x20 || c >= 0x7f) - { - bytes = path.getBytes(StandardCharsets.UTF_8); - buf = new StringBuilder(path.length() * 2); - break loop; - } - } - } - if (buf == null) - return null; - } - - int i; loop: - for (i = 0; i < path.length(); i++) + for (int i = 0; i < path.length(); i++) { char c = path.charAt(i); switch (c) { - case '%' -> buf.append("%25"); - case '?' -> buf.append("%3F"); - case ';' -> buf.append("%3B"); - case '#' -> buf.append("%23"); - case '"' -> buf.append("%22"); - case '\'' -> buf.append("%27"); - case '<' -> buf.append("%3C"); - case '>' -> buf.append("%3E"); - case ' ' -> buf.append("%20"); - case '[' -> buf.append("%5B"); - case '\\' -> buf.append("%5C"); - case ']' -> buf.append("%5D"); - case '^' -> buf.append("%5E"); - case '`' -> buf.append("%60"); - case '{' -> buf.append("%7B"); - case '|' -> buf.append("%7C"); - case '}' -> buf.append("%7D"); - default -> - { + case '%': + case '?': + case ';': + case '#': + case '"': + case '\'': + case '<': + case '>': + case ' ': + case '[': + case '\\': + case ']': + case '^': + case '`': + case '{': + case '|': + case '}': + buf = new StringBuilder(path.length() * 2); + break loop; + default: if (c < 0x20 || c >= 0x7f) { bytes = path.getBytes(StandardCharsets.UTF_8); + buf = new StringBuilder(path.length() * 2); break loop; } - buf.append(c); - } } } - if (bytes != null) + if (buf != null) { - for (; i < bytes.length; i++) + int i = 0; + + loop: + for (i = 0; i < path.length(); i++) { - byte c = bytes[i]; + char c = path.charAt(i); switch (c) { - case '%': - buf.append("%25"); - continue; - case '?': - buf.append("%3F"); - continue; - case ';': - buf.append("%3B"); - continue; - case '#': - buf.append("%23"); - continue; - case '"': - buf.append("%22"); - continue; - case '\'': - buf.append("%27"); - continue; - case '<': - buf.append("%3C"); - continue; - case '>': - buf.append("%3E"); - continue; - case ' ': - buf.append("%20"); - continue; - case '[': - buf.append("%5B"); - continue; - case '\\': - buf.append("%5C"); - continue; - case ']': - buf.append("%5D"); - continue; - case '^': - buf.append("%5E"); - continue; - case '`': - buf.append("%60"); - continue; - case '{': - buf.append("%7B"); - continue; - case '|': - buf.append("%7C"); - continue; - case '}': - buf.append("%7D"); - continue; - default: + case '%' -> buf.append("%25"); + case '?' -> buf.append("%3F"); + case ';' -> buf.append("%3B"); + case '#' -> buf.append("%23"); + case '"' -> buf.append("%22"); + case '\'' -> buf.append("%27"); + case '<' -> buf.append("%3C"); + case '>' -> buf.append("%3E"); + case ' ' -> buf.append("%20"); + case '[' -> buf.append("%5B"); + case '\\' -> buf.append("%5C"); + case ']' -> buf.append("%5D"); + case '^' -> buf.append("%5E"); + case '`' -> buf.append("%60"); + case '{' -> buf.append("%7B"); + case '|' -> buf.append("%7C"); + case '}' -> buf.append("%7D"); + default -> + { if (c < 0x20 || c >= 0x7f) { - buf.append('%'); - TypeUtil.toHex(c, buf); + bytes = path.getBytes(StandardCharsets.UTF_8); + break loop; } - else - buf.append((char)c); + buf.append(c); + } } } - } - return buf; - } + if (bytes != null) + { + for (; i < bytes.length; i++) + { + byte c = bytes[i]; + switch (c) + { + case '%': + buf.append("%25"); + continue; + case '?': + buf.append("%3F"); + continue; + case ';': + buf.append("%3B"); + continue; + case '#': + buf.append("%23"); + continue; + case '"': + buf.append("%22"); + continue; + case '\'': + buf.append("%27"); + continue; + case '<': + buf.append("%3C"); + continue; + case '>': + buf.append("%3E"); + continue; + case ' ': + buf.append("%20"); + continue; + case '[': + buf.append("%5B"); + continue; + case '\\': + buf.append("%5C"); + continue; + case ']': + buf.append("%5D"); + continue; + case '^': + buf.append("%5E"); + continue; + case '`': + buf.append("%60"); + continue; + case '{': + buf.append("%7B"); + continue; + case '|': + buf.append("%7C"); + continue; + case '}': + buf.append("%7D"); + continue; + default: + if (c < 0x20 || c >= 0x7f) + { + buf.append('%'); + TypeUtil.toHex(c, buf); + } + else + buf.append((char)c); + } + } + } + } - /** - * Encode a raw URI String and convert any raw spaces to - * their "%20" equivalent. - * - * @param str input raw string - * @return output with spaces converted to "%20" - */ - public static String encodeSpaces(String str) - { - return StringUtil.replace(str, " ", "%20"); + return buf == null ? path : buf.toString(); } /** @@ -499,11 +474,12 @@ public static String decodeSpecific(String str, String charsToDecode) /** * Encode a URI path. * - * @param path The path the encode + * @param path The path to encode * @param buf StringBuilder to encode path into (or null) * @param encode String of characters to encode. % is always encoded. * @return The StringBuilder or null if no substitutions required. */ + // TODO: remove, only used in URIUtilTest? public static StringBuilder encodeString(StringBuilder buf, String path, String encode) @@ -1701,7 +1677,7 @@ private static boolean mustBeEncoded(int codepoint) return ((codepoint == '?') || (codepoint == '#')); } - public static boolean equalsIgnoreEncodings(URI uriA, URI uriB) + static boolean equalsIgnoreEncodings(URI uriA, URI uriB) { if (uriA.equals(uriB)) return true; diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java index 5dc2cbbd9355..18009d997a0b 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java @@ -75,10 +75,8 @@ public static Stream encodePathSource() public void testEncodePath(String rawPath, String expectedEncoded) { // test basic encode/decode - StringBuilder buf = new StringBuilder(); - buf.setLength(0); - URIUtil.encodePath(buf, rawPath); - assertEquals(expectedEncoded, buf.toString()); + String result = URIUtil.encodePath(rawPath); + assertEquals(expectedEncoded, result); } @Test @@ -831,29 +829,6 @@ public void testCorrectBadFileURIActualFile() throws Exception assertThat(URIUtil.correctFileURI(fileUrlUri).toASCIIString(), is(expectedUri.toASCIIString())); } - public static Stream encodeSpacesSource() - { - return Stream.of( - // null - Arguments.of(null, null), - - // no spaces - Arguments.of("abc", "abc"), - - // match - Arguments.of("a c", "a%20c"), - Arguments.of(" ", "%20%20%20"), - Arguments.of("a%20space", "a%20space") - ); - } - - @ParameterizedTest - @MethodSource("encodeSpacesSource") - public void testEncodeSpaces(String raw, String expected) - { - assertThat(URIUtil.encodeSpaces(raw), is(expected)); - } - public static Stream encodeSpecific() { return Stream.of( From d3ee9ff08ec50b06fd35f3dd8b6be059249f80ca Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 4 Nov 2022 09:16:38 -0500 Subject: [PATCH 4/9] More cleanup of URIUtil methods + simplify encodePath() --- .../java/org/eclipse/jetty/util/TypeUtil.java | 15 ++ .../java/org/eclipse/jetty/util/URIUtil.java | 194 +++++------------- .../org/eclipse/jetty/util/URIUtilTest.java | 15 +- 3 files changed, 80 insertions(+), 144 deletions(-) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java index a70723592de7..f1dbbf6f0e79 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java @@ -518,6 +518,21 @@ public static boolean isHex(String str, int offset, int len) return true; } + public static void toHex(char c, Appendable buf) + { + try + { + int d = 0xf & ((0xF0 & c) >> 4); + buf.append((char)((d > 9 ? ('A' - 10) : '0') + d)); + d = 0xf & c; + buf.append((char)((d > 9 ? ('A' - 10) : '0') + d)); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + public static void toHex(byte b, Appendable buf) { try diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java index 3d41757d9bfd..66ad23fe438d 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java @@ -189,10 +189,24 @@ public final class URIUtil false, // 0x7f DEL is illegal }; + private static final boolean[] ENCODE_PATH_NEEDS_ENCODING; + private URIUtil() { } + static + { + ENCODE_PATH_NEEDS_ENCODING = new boolean[128]; + // Special characters + for (char c: "%?;#\"'<> [\\]^`{|}".toCharArray()) + ENCODE_PATH_NEEDS_ENCODING[c] = true; + // control characters + ENCODE_PATH_NEEDS_ENCODING[0x7f] = true; + for (int i = 0; i < 0x20; i++) + ENCODE_PATH_NEEDS_ENCODING[i] = true; + } + /** * Encode a URI path. * This is the same encoding offered by URLEncoder, except that @@ -206,155 +220,61 @@ public static String encodePath(String path) if (path == null || path.length() == 0) return path; - StringBuilder buf = null; - byte[] bytes = null; - - loop: - for (int i = 0; i < path.length(); i++) + boolean needsEncoding = false; + boolean needsByteEncoding = false; + int length = path.length(); + for (int i = 0; i < length; i++) { char c = path.charAt(i); - switch (c) - { - case '%': - case '?': - case ';': - case '#': - case '"': - case '\'': - case '<': - case '>': - case ' ': - case '[': - case '\\': - case ']': - case '^': - case '`': - case '{': - case '|': - case '}': - buf = new StringBuilder(path.length() * 2); - break loop; - default: - if (c < 0x20 || c >= 0x7f) - { - bytes = path.getBytes(StandardCharsets.UTF_8); - buf = new StringBuilder(path.length() * 2); - break loop; - } - } + if (c > 0x7F) // 8-bit + + needsByteEncoding = true; + else if (ENCODE_PATH_NEEDS_ENCODING[c]) + needsEncoding = true; } - if (buf != null) + if (needsByteEncoding) + return encodePathBytes(path); + else if (needsEncoding) { - int i = 0; + return encodePathString(path); + } + else + return path; + } - loop: - for (i = 0; i < path.length(); i++) + private static String encodePathString(String path) + { + StringBuilder buf = new StringBuilder(path.length() * 2); + int length = path.length(); + for (int i = 0; i < length; i++) + { + char c = path.charAt(i); + if (ENCODE_PATH_NEEDS_ENCODING[c]) { - char c = path.charAt(i); - switch (c) - { - case '%' -> buf.append("%25"); - case '?' -> buf.append("%3F"); - case ';' -> buf.append("%3B"); - case '#' -> buf.append("%23"); - case '"' -> buf.append("%22"); - case '\'' -> buf.append("%27"); - case '<' -> buf.append("%3C"); - case '>' -> buf.append("%3E"); - case ' ' -> buf.append("%20"); - case '[' -> buf.append("%5B"); - case '\\' -> buf.append("%5C"); - case ']' -> buf.append("%5D"); - case '^' -> buf.append("%5E"); - case '`' -> buf.append("%60"); - case '{' -> buf.append("%7B"); - case '|' -> buf.append("%7C"); - case '}' -> buf.append("%7D"); - default -> - { - if (c < 0x20 || c >= 0x7f) - { - bytes = path.getBytes(StandardCharsets.UTF_8); - break loop; - } - buf.append(c); - } - } + buf.append('%'); + TypeUtil.toHex(c, buf); } + else + buf.append(c); + } + return buf.toString(); + } - if (bytes != null) + private static String encodePathBytes(String path) + { + StringBuilder buf = new StringBuilder(path.length() * 2); + byte[] pathBytes = path.getBytes(StandardCharsets.UTF_8); + for (byte b : pathBytes) + { + if (b < 0 || ENCODE_PATH_NEEDS_ENCODING[b]) { - for (; i < bytes.length; i++) - { - byte c = bytes[i]; - switch (c) - { - case '%': - buf.append("%25"); - continue; - case '?': - buf.append("%3F"); - continue; - case ';': - buf.append("%3B"); - continue; - case '#': - buf.append("%23"); - continue; - case '"': - buf.append("%22"); - continue; - case '\'': - buf.append("%27"); - continue; - case '<': - buf.append("%3C"); - continue; - case '>': - buf.append("%3E"); - continue; - case ' ': - buf.append("%20"); - continue; - case '[': - buf.append("%5B"); - continue; - case '\\': - buf.append("%5C"); - continue; - case ']': - buf.append("%5D"); - continue; - case '^': - buf.append("%5E"); - continue; - case '`': - buf.append("%60"); - continue; - case '{': - buf.append("%7B"); - continue; - case '|': - buf.append("%7C"); - continue; - case '}': - buf.append("%7D"); - continue; - default: - if (c < 0x20 || c >= 0x7f) - { - buf.append('%'); - TypeUtil.toHex(c, buf); - } - else - buf.append((char)c); - } - } + buf.append('%'); + TypeUtil.toHex(b, buf); } + else + buf.append((char)b); } - - return buf == null ? path : buf.toString(); + return buf.toString(); } /** diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java index 18009d997a0b..de9dbbefd67a 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java @@ -809,17 +809,18 @@ public void testCorrectFileURI(String input, String expected) } @Test - public void testCorrectBadFileURIActualFile() throws Exception + public void testCorrectBadFileURIActualFile(WorkDir workDir) throws Exception { - File file = MavenTestingUtils.getTargetFile("testCorrectBadFileURIActualFile.txt"); - FS.touch(file); + Path testfile = workDir.getEmptyPathDir().resolve("testCorrectBadFileURIActualFile.txt"); + FS.touch(testfile); - URI expectedUri = file.toPath().toUri(); + URI expectedUri = testfile.toUri(); // correct URI with `://` assertThat(expectedUri.toASCIIString(), containsString("://")); - URI fileUri = file.toURI(); - URI fileUrlUri = file.toURL().toURI(); + File file = testfile.toFile(); + URI fileUri = file.toURI(); // java produced bad format with only `:/` (not `://`) + URI fileUrlUri = file.toURL().toURI(); // java produced bad format with only `:/` (not `://`) // If these 2 tests start failing, that means Java itself has been fixed assertThat(fileUri.toASCIIString(), not(containsString("://"))); @@ -1020,7 +1021,7 @@ public void testEncodeDecodeVisibleOnly() { assertTrue(c > 0x20 && c < 0x80); assertFalse(Character.isWhitespace(c)); - assertFalse(Character.isISOControl(c)); + assertFalse(Character.isISOControl(c), "isISOControl(0x%2x)".formatted((byte)c)); } // check decode to original String decoded = URIUtil.decodePath(encoded); From 8afae1882535be9b6403f7ecbe689855206726a2 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 4 Nov 2022 09:40:57 -0500 Subject: [PATCH 5/9] Javadoc updates --- .../java/org/eclipse/jetty/util/URIUtil.java | 57 +++++++++++-------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java index 66ad23fe438d..96f12ad7eebd 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java @@ -210,7 +210,7 @@ private URIUtil() /** * Encode a URI path. * This is the same encoding offered by URLEncoder, except that - * the '/' character is not encoded. + * the '{@code /}' character is not encoded. * * @param path The path to encode * @return The encoded path @@ -325,7 +325,7 @@ public static String encodeSpecific(String str, String charsToEncode) * Decode a raw String and convert any specific URI encoded sequences into characters. * * @param str input raw string - * @param charsToDecode the list of raw characters that need to be decoded (if encountered), leaving all other encoded sequences alone. + * @param charsToDecode the list of raw characters that need to be decoded (if encountered), leaving all the other encoded sequences alone. * @return output with specified characters decoded. */ @SuppressWarnings("Duplicates") @@ -396,7 +396,7 @@ public static String decodeSpecific(String str, String charsToDecode) * * @param path The path to encode * @param buf StringBuilder to encode path into (or null) - * @param encode String of characters to encode. % is always encoded. + * @param encode String of characters to encode. '{@code %}' is always encoded. * @return The StringBuilder or null if no substitutions required. */ // TODO: remove, only used in URIUtilTest? @@ -626,11 +626,11 @@ private static void appendHexValue(Utf8StringBuilder builder, byte value) *

* Decode only the safe characters in a URI path and strip parameters of UTF-8 path. * Safe characters are ones that are not special delimiters and that can be passed to the JVM {@link URI} class. - * Unsafe characters, other than '/' will be encoded. Encodings will be uppercase hex. + * Unsafe characters, other than '{@code /}' will be encoded. Encodings will be uppercase hex. * Canonical paths are also normalized and may be used in string comparisons with other canonical paths. *

- * For example the path /fo %2fo/b%61r will be normalized to /fo%20%2Fo/bar, - * whilst {@link #decodePath(String)} would result in the ambiguous and URI illegal /fo /o/bar. + * For example the path {@code /fo %2fo/b%61r} will be normalized to {@code /fo%20%2Fo/bar}, + * whilst {@link #decodePath(String)} would result in the ambiguous and URI illegal {@code /fo /o/bar}. * @return the canonical path or null if it is non-normal * @see #decodePath(String) * @see #normalizePath(String) @@ -759,7 +759,8 @@ public static String canonicalPath(String path) } } - /* Decode a URI path and strip parameters of ISO-8859-1 path + /** + * Decode a URI path and strip parameters of ISO-8859-1 path */ private static String decodeISO88591Path(String path, int offset, int length) { @@ -831,7 +832,7 @@ private static String decodeISO88591Path(String path, int offset, int length) /** * Add two encoded URI path segments. * Handles null and empty paths, path and query params - * (eg ?a=b or ;JSESSIONID=xxx) and avoids duplicate '/' + * (e.g. {@code ?a=b} or {@code ;JSESSIONID=xxx}) and avoids duplicate '{@code /}' * * @param p1 URI path segment (should be encoded) * @param p2 URI path segment (should be encoded) @@ -885,8 +886,10 @@ public static String addEncodedPaths(String p1, String p2) /** * Add two Decoded URI path segments. - * Handles null and empty paths. Path and query params (eg ?a=b or - * ;JSESSIONID=xxx) are not handled + *

+ * Handles null and empty paths. + * Path and query params (e.g. {@code ?a=b} or {@code ;JSESSIONID=xxx}) are not handled + *

* * @param p1 URI path segment (should be decoded) * @param p2 URI path segment (should be decoded) @@ -932,10 +935,12 @@ public static String addPaths(String p1, String p2) return buf.toString(); } - /** Add a path and a query string + /** + * Add a path and a query string + * * @param path The path which may already contain a query * @param query The query string to add (if blank, no query is added) - * @return The path with any non-blank query added after a '?' or '&' as appropriate. + * @return The path with any non-blank query added after a '{@code ?}' or '{@code &}' as appropriate. */ public static String addPathQuery(String path, String query) { @@ -984,7 +989,9 @@ public static String getUriLastPathSegment(URI uri) /** * Return the parent Path. + *

* Treat a URI like a directory path and return the parent directory. + *

* * @param p the path to return a parent reference to * @return the parent path of the URI @@ -1000,7 +1007,8 @@ public static String parentPath(String p) } /** - *

Normalize a URI path and query by factoring out all segments of "." and ".." + *

+ * Normalize a URI path and query by factoring out all segments of '{@code .}' and '{@code ..}' * up until any query or fragment. * Null is returned if the path is normalized above its root. *

@@ -1108,8 +1116,8 @@ else if (slash) /** *

Check if a path would be normalized within itself. For example, - * /foo/../../bar is normalized above its root and would - * thus return false, whilst /foo/./bar/.. is normal within itself + * {@code /foo/../../bar} is normalized above its root and would + * thus return false, whilst {@code /foo/./bar/..} is normal within itself * and would return true. * @param path The path to check * @return True if the normal form of the path is within the root of the path. @@ -1121,11 +1129,11 @@ public static boolean isNotNormalWithinSelf(String path) } /** - *

Normalize a URI path by factoring out all segments of "." and "..". + *

Normalize a URI path by factoring out all segments of {@code .} and {@code ..}. * Null is returned if the path is normalized above its root. *

* - * @param path the decoded URI path to convert. Any special characters (e.g. '?', "#") are assumed to be part of + * @param path the decoded URI path to convert. Any special characters (e.g. {@code ?}, {@code #}) are assumed to be part of * the path segments. * @return the normalized path, or null if path traversal above root. * @see #normalizePathQuery(String) @@ -1258,7 +1266,7 @@ private static boolean doDotsSlash(StringBuilder canonical, int dots) /** * Convert a path to a compact form. - * All instances of "//" and "///" etc. are factored out to single "/" + * All instances of {@code //} and {@code ///} etc. are factored out to single {@code /} * * @param path the path to compact * @return the compacted path @@ -1442,7 +1450,7 @@ public static void appendSchemeHostPort(StringBuffer url, String scheme, String } } - // Only URIUtil is using this method + // Only used by URIUtil static boolean equalsIgnoreEncodings(String uriA, String uriB) { try @@ -1457,6 +1465,7 @@ static boolean equalsIgnoreEncodings(String uriA, String uriB) } } + // Only used by URIUtil static String ensureSafeEncoding(String path) { if (path == null) @@ -1682,8 +1691,8 @@ public static URI addPath(URI uri, String path) } /** - * Combine two query strings into one. Each query string should not contain the beginning '?' character, but - * may contain multiple parameters separated by the '{@literal &}' character. + * Combine two query strings into one. Each query string should not contain the beginning '{@code ?}' character, but + * may contain multiple parameters separated by the '{@code &}' character. * @param query1 the first query string. * @param query2 the second query string. * @return the combination of the two query strings. @@ -1746,12 +1755,12 @@ public static URI correctFileURI(URI uri) } /** - * Split a string of references, that may be split with {@code ,}, or {@code ;}, or {@code |} into URIs. + * Split a string of references, that may be split with '{@code ,}', or '{@code ;}', or '{@code |}' into URIs. *

* Each part of the input string could be path references (unix or windows style), or string URI references. *

*

- * If the result of processing the input segment is a java archive, then its resulting URI will be a mountable URI as `jar:file:...!/`. + * If the result of processing the input segment is a java archive, then its resulting URI will be a mountable URI as {@code jar:file:...!/} *

* * @param str the input string of references @@ -1816,7 +1825,7 @@ public static List split(String str) *

* * @param uri the URI to mutate to a {@code jar:file:...} URI. - * @return the jar:${uri_to_java_archive}!/${internal-reference} URI or the unchanged URI if not a Java Archive. + * @return the {@code jar:${uri_to_java_archive}!/${internal-reference}} URI or the unchanged URI if not a Java Archive. * @see FileID#isArchive(URI) */ public static URI toJarFileUri(URI uri) From bde3278deae8c1174a3cafae630af96c40a73ae5 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 4 Nov 2022 11:38:35 -0500 Subject: [PATCH 6/9] More tests --- .../src/test/java/org/eclipse/jetty/util/URIUtilTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java index de9dbbefd67a..313adddba9af 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java @@ -66,7 +66,9 @@ public static Stream encodePathSource() Arguments.of("/context/'list'/\"me\"/;", "/context/%27list%27/%22me%22/%3B%3Cscript%3Ewindow.alert(%27xss%27)%3B%3C/script%3E"), Arguments.of("test\u00f6?\u00f6:\u00df", "test%C3%B6%3F%C3%B6:%C3%9F"), - Arguments.of("test?\u00f6?\u00f6:\u00df", "test%3F%C3%B6%3F%C3%B6:%C3%9F") + Arguments.of("test?\u00f6?\u00f6:\u00df", "test%3F%C3%B6%3F%C3%B6:%C3%9F"), + Arguments.of("/test space/", "/test%20space/"), + Arguments.of("/test\u007fdel/", "/test%7Fdel/") ); } From 57bbeee08d2d7bfea5e3aa93938fbd1f75c89a06 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 4 Nov 2022 11:44:38 -0500 Subject: [PATCH 7/9] Updates from review --- .../java/org/eclipse/jetty/util/TypeUtil.java | 15 --------------- .../main/java/org/eclipse/jetty/util/URIUtil.java | 10 ++++++---- .../java/org/eclipse/jetty/util/URIUtilTest.java | 4 ++-- 3 files changed, 8 insertions(+), 21 deletions(-) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java index f1dbbf6f0e79..a70723592de7 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java @@ -518,21 +518,6 @@ public static boolean isHex(String str, int offset, int len) return true; } - public static void toHex(char c, Appendable buf) - { - try - { - int d = 0xf & ((0xF0 & c) >> 4); - buf.append((char)((d > 9 ? ('A' - 10) : '0') + d)); - d = 0xf & c; - buf.append((char)((d > 9 ? ('A' - 10) : '0') + d)); - } - catch (IOException e) - { - throw new RuntimeException(e); - } - } - public static void toHex(byte b, Appendable buf) { try diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java index 96f12ad7eebd..d74cee201a59 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java @@ -217,7 +217,7 @@ private URIUtil() */ public static String encodePath(String path) { - if (path == null || path.length() == 0) + if (StringUtil.isBlank(path)) return path; boolean needsEncoding = false; @@ -235,9 +235,7 @@ else if (ENCODE_PATH_NEEDS_ENCODING[c]) if (needsByteEncoding) return encodePathBytes(path); else if (needsEncoding) - { return encodePathString(path); - } else return path; } @@ -252,10 +250,12 @@ private static String encodePathString(String path) if (ENCODE_PATH_NEEDS_ENCODING[c]) { buf.append('%'); - TypeUtil.toHex(c, buf); + TypeUtil.toHex((byte)c, buf); } else + { buf.append(c); + } } return buf.toString(); } @@ -272,7 +272,9 @@ private static String encodePathBytes(String path) TypeUtil.toHex(b, buf); } else + { buf.append((char)b); + } } return buf.toString(); } diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java index 313adddba9af..d4fbad3e6275 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java @@ -1018,10 +1018,10 @@ public void testEncodeDecodeVisibleOnly() builder.append(i); String path = builder.toString(); String encoded = URIUtil.encodePath(path); - // Check endoded is visible + // Check encoded is visible for (char c : encoded.toCharArray()) { - assertTrue(c > 0x20 && c < 0x80); + assertTrue(c > 0x20 && c < 0x7f); assertFalse(Character.isWhitespace(c)); assertFalse(Character.isISOControl(c), "isISOControl(0x%2x)".formatted((byte)c)); } From ecb924b5c5f8149183ce726ad11ab424e7ddac2e Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 4 Nov 2022 12:23:49 -0500 Subject: [PATCH 8/9] equalsIgnoreEncoding cleanup (no longer used by Resource layer) --- .../java/org/eclipse/jetty/util/URIUtil.java | 63 ++------ .../org/eclipse/jetty/util/URIUtilTest.java | 141 +----------------- 2 files changed, 13 insertions(+), 191 deletions(-) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java index d74cee201a59..1573ff2dcd0f 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java @@ -1452,23 +1452,14 @@ public static void appendSchemeHostPort(StringBuffer url, String scheme, String } } - // Only used by URIUtil - static boolean equalsIgnoreEncodings(String uriA, String uriB) - { - try - { - String safeDecodedUriA = ensureSafeEncoding(uriA); - String safeDecodedUriB = ensureSafeEncoding(uriB); - return safeDecodedUriA.equals(safeDecodedUriB); - } - catch (IllegalArgumentException e) - { - return false; - } - } - - // Only used by URIUtil - static String ensureSafeEncoding(String path) + /** + * Encode characters in a path to ensure they only contain safe encodings suitable for both + * {@link URI} and {@link Paths#get(URI)} usage. + * + * @param path the path to encode + * @return the returned path with only safe encodings + */ + public static String encodePathSafeEncoding(String path) { if (path == null) return null; @@ -1608,42 +1599,6 @@ private static boolean mustBeEncoded(int codepoint) return ((codepoint == '?') || (codepoint == '#')); } - static boolean equalsIgnoreEncodings(URI uriA, URI uriB) - { - if (uriA.equals(uriB)) - return true; - - if (uriA.toASCIIString().equals(uriB.toASCIIString())) - return true; - - // TODO: this check occurs in uriA.equals(uriB) - if (uriA.getScheme() == null) - { - if (uriB.getScheme() != null) - return false; - } - else if (!uriA.getScheme().equalsIgnoreCase(uriB.getScheme())) - return false; - - if ("jar".equalsIgnoreCase(uriA.getScheme())) - { - // at this point we know that both uri's are "jar:" - URI uriAssp = URI.create(uriA.getRawSchemeSpecificPart()); - URI uriBssp = URI.create(uriB.getRawSchemeSpecificPart()); - return equalsIgnoreEncodings(uriAssp, uriBssp); - } - - if (uriA.getAuthority() == null) - { - if (uriB.getAuthority() != null) - return false; - } - else if (!uriA.getAuthority().equals(uriB.getAuthority())) - return false; - - return equalsIgnoreEncodings(uriA.getRawPath(), uriB.getRawPath()); - } - /** * Add a sub path to an existing URI. * @@ -1674,7 +1629,7 @@ public static URI addPath(URI uri, String path) // ensure that the base has a safe encoding suitable for both // URI and Paths.get(URI) later usage - path = ensureSafeEncoding(path); + path = encodePathSafeEncoding(path); pathLen = path.length(); if (base.length() == 0) diff --git a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java index d4fbad3e6275..dfba8cb54eca 100644 --- a/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java +++ b/jetty-core/jetty-util/src/test/java/org/eclipse/jetty/util/URIUtilTest.java @@ -565,7 +565,7 @@ public void testAddPathQuery(String path, String query, String expected) assertThat(actual, is(expected)); } - public static Stream ensureSafeEncodingSource() + public static Stream encodePathSafeEncodingSource() { return Stream.of( Arguments.of("/foo", "/foo"), @@ -589,10 +589,10 @@ public static Stream ensureSafeEncodingSource() } @ParameterizedTest - @MethodSource("ensureSafeEncodingSource") - public void testEnsureSafeEncoding(String input, String expected) + @MethodSource("encodePathSafeEncodingSource") + public void testEncodePathSafeEncoding(String input, String expected) { - assertThat(URIUtil.ensureSafeEncoding(input), is(expected)); + assertThat(URIUtil.encodePathSafeEncoding(input), is(expected)); } public static Stream compactPathSource() @@ -637,139 +637,6 @@ public void testParentPath(String path, String expectedPath) assertEquals(expectedPath, actual, String.format("parent %s", path)); } - public static Stream equalsIgnoreEncodingStringTrueSource() - { - return Stream.of( - Arguments.of("http://example.com/foo/bar", "http://example.com/foo/bar"), - Arguments.of("/barry%27s", "/barry%27s"), - Arguments.of("/b rry%27s", "/b%20rry%27s"), - Arguments.of("/barry's", "/barry%27s"), - Arguments.of("/barry%27s", "/barry's"), - Arguments.of("/b rry's", "/b%20rry%27s"), - Arguments.of("/b rry%27s", "/b%20rry's"), - Arguments.of("/re bar", "/re%20bar"), - - Arguments.of("/foo%2fbar", "/foo%2fbar"), - Arguments.of("/foo%2fbar", "/foo%2Fbar"), - - // encoded vs not-encode ("%" symbol is encoded as "%25") - Arguments.of("/abc%25xyz", "/abc%xyz"), - Arguments.of("/abc%25xy", "/abc%xy"), - Arguments.of("/abc%25x", "/abc%x"), - Arguments.of("/zzz%25", "/zzz%"), - - // unicode encoded vs not-encoded - Arguments.of("/path/to/bä€ãm/", "/path/to/b%C3%A4%E2%82%AC%C3%A3m/") - ); - } - - @ParameterizedTest - @MethodSource("equalsIgnoreEncodingStringTrueSource") - public void testEqualsIgnoreEncodingStringTrue(String uriA, String uriB) - { - assertTrue(URIUtil.equalsIgnoreEncodings(uriA, uriB)); - } - - public static Stream equalsIgnoreEncodingStringFalseSource() - { - return Stream.of( - // case difference - Arguments.of("ABC", "abc"), - // Encoding difference ("'" is "%27") - Arguments.of("/barry's", "/barry%26s"), - // Never match on "%2f" differences - only intested in filename / directory name differences - // This could be a directory called "foo" with a file called "bar" on the left, and just a file "foo%2fbar" on the right - Arguments.of("/foo/bar", "/foo%2fbar"), - // not actually encoded - Arguments.of("/foo2fbar", "/foo/bar"), - // path params - Arguments.of("/path;a=b/to;x=y/foo/", "/path/to/foo"), - // encoded vs not-encode ("%" symbol is encoded as "%25") - Arguments.of("/yyy%25zzz", "/aaa%xxx"), - Arguments.of("/zzz%25", "/aaa%"), - // %2F then multi-byte unicode - Arguments.of("/path/to/bãm/", "/path%2Fto/b%C3%A3m/"), - // multi-byte unicode then %2F - Arguments.of("/path/bãm/or/bust", "/path/b%C3%A3m/or%2Fbust"), - // mix of %2F and multiple consecutive multi-byte unicode - Arguments.of("/path/to/bä€ãm/", "/path%2Fto/b%C3%A4%E2%82%AC%C3%A3m/") - ); - } - - @ParameterizedTest - @MethodSource("equalsIgnoreEncodingStringFalseSource") - public void testEqualsIgnoreEncodingStringFalse(String uriA, String uriB) - { - assertFalse(URIUtil.equalsIgnoreEncodings(uriA, uriB)); - } - - public static Stream equalsIgnoreEncodingURITrueSource() - { - return Stream.of( - Arguments.of( - URI.create("HTTP:/foo/b%61r"), - URI.create("http:/f%6Fo/bar") - ), - Arguments.of( - URI.create("jar:file:/path/to/main.jar!/META-INF/versions/"), - URI.create("jar:file:/path/to/main.jar!/META-INF/%76ersions/") - ), - Arguments.of( - URI.create("JAR:FILE:/path/to/main.jar!/META-INF/versions/"), - URI.create("jar:file:/path/to/main.jar!/META-INF/versions/") - ), - // unicode in opaque jar:file: URI - Arguments.of( - URI.create("jar:file:///path/to/test.jar!/bãm/"), - URI.create("jar:file:///path/to/test.jar!/b%C3%A3m/") - ), - // multiple consecutive unicode - Arguments.of( - URI.create("file:///path/to/bä€ãm/"), - URI.create("file:///path/to/b%C3%A4%E2%82%AC%C3%A3m/") - ) - ); - } - - @ParameterizedTest - @MethodSource("equalsIgnoreEncodingURITrueSource") - public void testEqualsIgnoreEncodingURITrue(URI uriA, URI uriB) - { - assertTrue(URIUtil.equalsIgnoreEncodings(uriA, uriB)); - } - - public static Stream equalsIgnoreEncodingURIFalseSource() - { - return Stream.of( - Arguments.of( - URI.create("/foo%2Fbar"), - URI.create("/foo/bar") - ), - // %2F then unicode - Arguments.of( - URI.create("file:///path/to/bãm/"), - URI.create("file:///path%2Fto/b%C3%A3m/") - ), - // unicode then %2F - Arguments.of( - URI.create("file:///path/bãm/or/bust"), - URI.create("file:///path/b%C3%A3m/or%2Fbust") - ), - // mix of %2F and multiple consecutive unicode - Arguments.of( - URI.create("file:///path/to/bä€ãm/"), - URI.create("file:///path%2Fto/b%C3%A4%E2%82%AC%C3%A3m/") - ) - ); - } - - @ParameterizedTest - @MethodSource("equalsIgnoreEncodingURIFalseSource") - public void testEqualsIgnoreEncodingURIFalse(URI uriA, URI uriB) - { - assertFalse(URIUtil.equalsIgnoreEncodings(uriA, uriB)); - } - public static Stream correctBadFileURICases() { return Stream.of( From 555fb31789e0a71191e8c67526757c01b19397e3 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Fri, 4 Nov 2022 12:33:23 -0500 Subject: [PATCH 9/9] Changes from review --- .../main/java/org/eclipse/jetty/util/URIUtil.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java index 1573ff2dcd0f..be9e28f6a2fc 100644 --- a/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java +++ b/jetty-core/jetty-util/src/main/java/org/eclipse/jetty/util/URIUtil.java @@ -217,19 +217,27 @@ private URIUtil() */ public static String encodePath(String path) { - if (StringUtil.isBlank(path)) + if (StringUtil.isEmpty(path)) return path; - boolean needsEncoding = false; + // byte encoding always wins and, if encountered, should be used. boolean needsByteEncoding = false; + // string (char-by-char) encoding, but it could be followed by a need for byte encoding instead + boolean needsEncoding = false; int length = path.length(); for (int i = 0; i < length; i++) { char c = path.charAt(i); if (c > 0x7F) // 8-bit + + { needsByteEncoding = true; - else if (ENCODE_PATH_NEEDS_ENCODING[c]) + break; // have to encode byte by byte now + } + if (ENCODE_PATH_NEEDS_ENCODING[c]) + { + // could be followed by a byte encoding, so no break needsEncoding = true; + } } if (needsByteEncoding)