From 33fe55c3398c05e1b51acc2df8e29c3a8eef2822 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 11 Jun 2019 11:25:50 -0500 Subject: [PATCH 1/4] Issue #3708 - use StringUtil alternatives for known slow JVM impls. + StringUtil.replace() + StringUtil.replaceFirst() + StringUtil.sanitizeFileSystemPath() Change existing usages of String.replace() to either use new StringUtil.replace() or other methods elsewhere that better suit that specific need. Signed-off-by: Joakim Erdfelt --- .../jetty/ant/AntWebInfConfiguration.java | 3 +- .../fcgi/client/http/HttpSenderOverFCGI.java | 2 +- .../fcgi/server/proxy/TryFilesFilter.java | 5 +- .../org/eclipse/jetty/http/MimeTypes.java | 2 +- .../jetty/util/StringReplaceBenchmark.java | 119 +++++++++++ .../jetty/nosql/mongodb/MongoUtils.java | 22 +- .../osgi/boot/OSGiWebInfConfiguration.java | 3 +- .../eclipse/jetty/proxy/ProxyServletTest.java | 3 +- .../jetty/quickstart/AttributeNormalizer.java | 9 +- .../rewrite/handler/RedirectRegexRule.java | 10 +- .../rewrite/handler/RewriteRegexRule.java | 17 +- .../jetty/security/PropertyUserStore.java | 3 +- .../org/eclipse/jetty/server/Response.java | 4 +- .../jetty/server/handler/DefaultHandler.java | 2 +- .../jetty/servlet/ResponseHeadersTest.java | 9 +- .../jetty/servlets/CrossOriginFilter.java | 5 +- .../org/eclipse/jetty/util/StringUtil.java | 191 ++++++++++-------- .../java/org/eclipse/jetty/util/URIUtil.java | 14 +- .../jetty/util/component/Dumpable.java | 11 +- .../eclipse/jetty/util/StringUtilTest.java | 83 ++++---- .../jetty/webapp/ClasspathPattern.java | 2 +- .../jetty/webapp/WebInfConfiguration.java | 4 +- .../src/main/java/com/acme/CookieDump.java | 8 +- .../src/main/java/com/acme/Dump.java | 18 +- 24 files changed, 347 insertions(+), 202 deletions(-) create mode 100644 jetty-jmh/src/main/java/org/eclipse/jetty/util/StringReplaceBenchmark.java diff --git a/jetty-ant/src/main/java/org/eclipse/jetty/ant/AntWebInfConfiguration.java b/jetty-ant/src/main/java/org/eclipse/jetty/ant/AntWebInfConfiguration.java index 6c2e9ada926..a82bfcf6478 100644 --- a/jetty-ant/src/main/java/org/eclipse/jetty/ant/AntWebInfConfiguration.java +++ b/jetty-ant/src/main/java/org/eclipse/jetty/ant/AntWebInfConfiguration.java @@ -28,6 +28,7 @@ import java.util.regex.Pattern; import org.apache.tools.ant.AntClassLoader; import org.eclipse.jetty.util.PatternMatcher; +import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.WebAppClassLoader; import org.eclipse.jetty.webapp.WebAppContext; @@ -88,7 +89,7 @@ public class AntWebInfConfiguration extends WebInfConfiguration } catch (URISyntaxException e) { - containerUris[i] = new URI(u.toString().replaceAll(" ", "%20")); + containerUris[i] = new URI(URIUtil.encodeSpaces(u.toString())); } i++; } diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java index 3a5a6b63a4d..4f836fddfe2 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java @@ -88,7 +88,7 @@ public class HttpSenderOverFCGI extends HttpSender for (HttpField field : headers) { String name = field.getName(); - String fcgiName = "HTTP_" + name.replaceAll("-", "_").toUpperCase(Locale.ENGLISH); + String fcgiName = "HTTP_" + name.replace('-', '_').toUpperCase(Locale.ENGLISH); fcgiHeaders.add(fcgiName, field.getValue()); } diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java index 3964dea1150..7a047c6c1cc 100644 --- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java +++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/proxy/TryFilesFilter.java @@ -24,7 +24,6 @@ import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; - import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -35,6 +34,8 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.util.StringUtil; + /** * Inspired by nginx's try_files functionality. *

@@ -132,7 +133,7 @@ public class TryFilesFilter implements Filter path += info; if (!path.startsWith("/")) path = "/" + path; - return value.replaceAll("\\$path", path); + return StringUtil.replace(value, "$path", path); } @Override diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java b/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java index 333163dd53f..3b428888e80 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java @@ -196,7 +196,7 @@ public class MimeTypes int charset=type.toString().indexOf(";charset="); if (charset>0) { - String alt=type.toString().replace(";charset=","; charset="); + String alt = StringUtil.replace(type.toString(), ";charset=", "; charset="); CACHE.put(alt,type); TYPES.put(alt,type.asBuffer()); } diff --git a/jetty-jmh/src/main/java/org/eclipse/jetty/util/StringReplaceBenchmark.java b/jetty-jmh/src/main/java/org/eclipse/jetty/util/StringReplaceBenchmark.java new file mode 100644 index 00000000000..61419271778 --- /dev/null +++ b/jetty-jmh/src/main/java/org/eclipse/jetty/util/StringReplaceBenchmark.java @@ -0,0 +1,119 @@ +// +// ======================================================================== +// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + +package org.eclipse.jetty.util; + +import java.util.concurrent.TimeUnit; + +import org.openjdk.jmh.annotations.Benchmark; +import org.openjdk.jmh.annotations.Fork; +import org.openjdk.jmh.annotations.Level; +import org.openjdk.jmh.annotations.Measurement; +import org.openjdk.jmh.annotations.Param; +import org.openjdk.jmh.annotations.Scope; +import org.openjdk.jmh.annotations.Setup; +import org.openjdk.jmh.annotations.State; +import org.openjdk.jmh.annotations.Warmup; +import org.openjdk.jmh.infra.Blackhole; +import org.openjdk.jmh.runner.Runner; +import org.openjdk.jmh.runner.RunnerException; +import org.openjdk.jmh.runner.options.Options; +import org.openjdk.jmh.runner.options.OptionsBuilder; + +@Fork(value = 3) +@State(Scope.Benchmark) +@Warmup(iterations = 4, time = 1, timeUnit = TimeUnit.SECONDS) +@Measurement(iterations = 4, time = 1, timeUnit = TimeUnit.SECONDS) +public class StringReplaceBenchmark +{ + @Param({"3", "100", "1000"}) + int size; + + @Param({"0", "1", "3", "50"}) + int matches; + + String input; + + @Setup(Level.Trial) + public void setupTrial() throws Exception + { + String pattern = "abc"; + StringBuilder str = new StringBuilder(); + while (str.length() < size) + { + str.append(pattern); + } + + if (matches > 0) + { + int partSize = (int)((double)str.length() / (double)matches); + for (int i = 0; i < matches; i++) + { + str.insert((i * partSize), "'"); + } + } + input = str.toString(); + } + + @Benchmark + public void testJavaStringReplace_Growth(Blackhole blackhole) + { + blackhole.consume(input.replace("'", "FOOBAR")); + } + + @Benchmark + public void testJavaStringReplace_Same(Blackhole blackhole) + { + blackhole.consume(input.replace("'", "X")); + } + + @Benchmark + public void testJavaStringReplace_Reduce(Blackhole blackhole) + { + blackhole.consume(input.replace("'", "")); + } + + @Benchmark + public void testJettyStringUtilReplace_Growth(Blackhole blackhole) + { + blackhole.consume(StringUtil.replace(input, "'", "FOOBAR")); + } + + @Benchmark + public void testJettyStringUtilReplace_Same(Blackhole blackhole) + { + blackhole.consume(StringUtil.replace(input, "'", "X")); + } + + @Benchmark + public void testJettyStringUtilReplace_Reduce(Blackhole blackhole) + { + blackhole.consume(StringUtil.replace(input, "'", "")); + } + + public static void main(String[] args) throws RunnerException + { + Options opt = new OptionsBuilder() + .include(StringReplaceBenchmark.class.getSimpleName()) +// .addProfiler(GCProfiler.class) + .forks(1) + .build(); + + new Runner(opt).run(); + } +} diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoUtils.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoUtils.java index 9dfec359bba..a66ca44cc54 100644 --- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoUtils.java +++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoUtils.java @@ -27,10 +27,10 @@ import java.util.Date; import java.util.HashMap; import java.util.Map; -import org.eclipse.jetty.util.ClassLoadingObjectInputStream; - import com.mongodb.BasicDBObject; import com.mongodb.DBObject; +import org.eclipse.jetty.util.ClassLoadingObjectInputStream; +import org.eclipse.jetty.util.StringUtil; /** * MongoUtils @@ -69,23 +69,23 @@ public class MongoUtils throw new IllegalStateException(valueToDecode.getClass().toString()); } } - - - + public static String decodeName(String name) { - return name.replace("%2E",".").replace("%25","%"); + String cleaned = name; + cleaned = StringUtil.replace(cleaned, "%2E", "."); + cleaned = StringUtil.replace(cleaned, "%25", "%"); + return cleaned; } - - public static String encodeName(String name) { - return name.replace("%","%25").replace(".","%2E"); + String cleaned = name; + cleaned = StringUtil.replace(cleaned, "%", "%25"); + cleaned = StringUtil.replace(cleaned, ".", "%2E"); + return cleaned; } - - public static Object encodeName(Object value) throws IOException { if (value instanceof Number || value instanceof String || value instanceof Boolean || value instanceof Date) diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java index 11af9ca7985..1ba0c301e91 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/OSGiWebInfConfiguration.java @@ -35,6 +35,7 @@ import java.util.regex.Pattern; import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory; import org.eclipse.jetty.osgi.boot.utils.Util; import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker; +import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; @@ -339,7 +340,7 @@ public class OSGiWebInfConfiguration extends WebInfConfiguration } catch (URISyntaxException e) { - uri = new URI(url.toString().replaceAll(" ", "%20")); + uri = new URI(URIUtil.encodeSpaces(url.toString())); } String key = resourcePath.startsWith("/") ? resourcePath.substring(1) : resourcePath; resourceMap.put(key + ";" + fragment.getSymbolicName(), Resource.newResource(uri)); diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java index 8791899bb18..71afb0e0904 100644 --- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java +++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java @@ -43,7 +43,6 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; import java.util.zip.GZIPOutputStream; - import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; @@ -670,7 +669,7 @@ public class ProxyServletTest // Make the request to the proxy, it should transparently forward to the server ContentResponse response = client.newRequest("localhost", proxyConnector.getLocalPort()) - .path((prefix + target).replaceAll("//", "/")) + .path((prefix + target).replace("//", "/")) .timeout(5, TimeUnit.SECONDS) .send(); assertEquals(200, response.getStatus()); diff --git a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/AttributeNormalizer.java b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/AttributeNormalizer.java index c153be32d3c..522bcccf6b5 100644 --- a/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/AttributeNormalizer.java +++ b/jetty-quickstart/src/main/java/org/eclipse/jetty/quickstart/AttributeNormalizer.java @@ -37,6 +37,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; @@ -454,13 +455,7 @@ public class AttributeNormalizer // leftover expanded.append(str.substring(offset)); - // special case for "$$" - if (expanded.indexOf("$$") >= 0) - { - return expanded.toString().replaceAll("\\$\\$","\\$"); - } - - return expanded.toString(); + return StringUtil.replace(expanded.toString(), "$$", "$"); } private String getString(String property) diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java index 1f023bd432a..acd31fa7960 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RedirectRegexRule.java @@ -24,6 +24,7 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.annotation.Name; /** @@ -93,7 +94,7 @@ public class RedirectRegexRule extends RegexRule for (int g = 1; g <= matcher.groupCount(); g++) { String group = matcher.group(g); - target = target.replaceAll("\\$" + g, group); + target = StringUtil.replace(target, "$" + g, group); } target = response.encodeRedirectURL(target); @@ -110,6 +111,11 @@ public class RedirectRegexRule extends RegexRule @Override public String toString() { - return String.format("%s[%d>%s]", super.toString(), _statusCode, _location); + StringBuilder str = new StringBuilder(); + str.append(super.toString()); + str.append('[').append(_statusCode); + str.append('>').append(_location); + str.append(']'); + return str.toString(); } } diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java index 1888fdc9815..f41cbdb9032 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java @@ -20,11 +20,11 @@ package org.eclipse.jetty.rewrite.handler; import java.io.IOException; import java.util.regex.Matcher; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.annotation.Name; /** @@ -96,15 +96,22 @@ public class RewriteRegexRule extends RegexRule implements Rule.ApplyURI group=""; else group = Matcher.quoteReplacement(group); - target=target.replaceAll("\\$"+g,group); - if (query!=null) - query=query.replaceAll("\\$"+g,group); + String dollarGroup = "$" + g; + target = StringUtil.replace(target, dollarGroup, group); + if (query != null) + query = StringUtil.replace(query, dollarGroup, group); } if (query!=null) { if (_queryGroup) - query=query.replace("$Q",request.getQueryString()==null?"":request.getQueryString()); + { + String replacement = ""; + if (request.getQueryString() != null) + replacement = request.getQueryString(); + + query = StringUtil.replace(query, "$Q", replacement); + } request.setAttribute("org.eclipse.jetty.rewrite.handler.RewriteRegexRule.Q",query); } diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java b/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java index e43f4719d30..9d0ea1004b4 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java @@ -140,7 +140,8 @@ public class PropertyUserStore extends UserStore implements PathWatcher.Listener int bang_slash = uri.indexOf("!/"); if (colon < 0 || bang_slash < 0 || colon > bang_slash) throw new IllegalArgumentException("Not resolved JarFile resource: " + uri); - String entry_path = uri.substring(colon + 2).replace("!/", "__").replace('/', '_').replace('.', '_'); + + String entry_path = StringUtil.sanitizeFileSystemPath(uri.substring(colon + 2)); Path tmpDirectory = Files.createTempDirectory("users_store"); tmpDirectory.toFile().deleteOnExit(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java index f5733470fb2..1809d5b1cea 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java @@ -52,12 +52,10 @@ import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.http.PreEncodedHttpField; -import org.eclipse.jetty.http.Syntax; import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ErrorHandler; import org.eclipse.jetty.server.session.SessionHandler; -import org.eclipse.jetty.util.QuotedStringTokenizer; import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; @@ -237,7 +235,7 @@ public class Response implements HttpServletResponse if (i >= 0) { httpOnly = true; - comment = comment.replace(HTTP_ONLY_COMMENT, "").trim(); + comment = StringUtil.replace(comment.trim(), HTTP_ONLY_COMMENT, ""); if (comment.length() == 0) comment = null; } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java index 06c74a91a21..21e814f03d6 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DefaultHandler.java @@ -164,7 +164,7 @@ public class DefaultHandler extends AbstractHandler { writer.append(""); } - writer.append(contextPath.replaceAll("%", "%")); + writer.append(StringUtil.replace(contextPath, "%", "%")); if (context.isRunning()) { writer.append(""); diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java index ffaaed78bf0..eb801824bea 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java @@ -18,13 +18,9 @@ package org.eclipse.jetty.servlet; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.is; - import java.io.IOException; import java.net.URLDecoder; import java.nio.ByteBuffer; - import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -38,6 +34,9 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + public class ResponseHeadersTest { public static class SimulateUpgradeServlet extends HttpServlet @@ -144,7 +143,7 @@ public class ResponseHeadersTest assertThat("Response Code",response.getStatus(),is(200)); assertThat("Response Header Content-Type",response.get("Content-Type"),is("text/plain;charset=UTF-8")); - String expected = actualPathInfo.replaceAll("%0A", " "); // replace OBS fold with space + String expected = actualPathInfo.replace("%0A", " "); // replace OBS fold with space expected = URLDecoder.decode(expected, "utf-8"); // decode the rest expected = expected.trim(); // trim whitespace at start/end assertThat("Response Header X-example", response.get("X-Example"), is(expected)); diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java index 692be858f1c..4c3393a90a4 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java @@ -26,7 +26,6 @@ import java.util.Enumeration; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; - import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; @@ -368,8 +367,8 @@ public class CrossOriginFilter implements Filter private String parseAllowedWildcardOriginToRegex(String allowedOrigin) { - String regex = allowedOrigin.replace(".", "\\."); - return regex.replace("*", ".*"); // we want to be greedy here to match multiple subdomains, thus we use .* + String regex = StringUtil.replace(allowedOrigin, ".", "\\."); + return StringUtil.replace(regex, "*", ".*"); // we want to be greedy here to match multiple subdomains, thus we use .* } private boolean isSimpleRequest(HttpServletRequest request) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java index 9b1ef63603b..0bf5a431194 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java @@ -26,14 +26,13 @@ import java.util.List; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -/** Fast String Utilities. +/** + * Fast String Utilities. * * These string utilities provide both convenience methods and * performance improvements over most standard library versions. The * main aim of the optimizations is to avoid object creation unless * absolutely required. - * - * */ public class StringUtil { @@ -62,8 +61,7 @@ public class StringUtil CHARSETS.put("iso-8859-1",__ISO_8859_1); CHARSETS.put("iso_8859_1",__ISO_8859_1); } - - /* ------------------------------------------------------------ */ + /** Convert alternate charset names (eg utf8) to normalized * name (eg UTF-8). * @param s the charset to normalize @@ -74,8 +72,7 @@ public class StringUtil String n=CHARSETS.get(s); return (n==null)?s:n; } - - /* ------------------------------------------------------------ */ + /** Convert alternate charset names (eg utf8) to normalized * name (eg UTF-8). * @param s the charset to normalize @@ -88,9 +85,7 @@ public class StringUtil String n=CHARSETS.get(s,offset,length); return (n==null)?s.substring(offset,offset+length):n; } - - /* ------------------------------------------------------------ */ public static final char[] lowercases = { '\000','\001','\002','\003','\004','\005','\006','\007', '\010','\011','\012','\013','\014','\015','\016','\017', @@ -109,7 +104,6 @@ public class StringUtil '\160','\161','\162','\163','\164','\165','\166','\167', '\170','\171','\172','\173','\174','\175','\176','\177' }; - /* ------------------------------------------------------------ */ /** * fast lower case conversion. Only works on ascii (not unicode) * @param s the string to convert @@ -122,7 +116,6 @@ public class StringUtil char[] c = null; int i=s.length(); - // look for first conversion while (i-->0) { @@ -138,7 +131,6 @@ public class StringUtil } } } - while (i-->0) { if(c[i]<=127) @@ -148,8 +140,43 @@ public class StringUtil return c==null?s:new String(c); } + /** + * Replace all characters from input string that are known to have + * special meaning in various filesytems. + * + *

+ * This will replace all of the following characters + * with a "{@code _}" (underscore). + *

+ * + * + * @param str the raw input string + * @return the sanitized output string. + */ + public static String sanitizeFileSystemPath(String str) + { + char[] chars = str.toCharArray(); + int len = chars.length; + for (int i = 0; i < len; i++) + { + char c = chars[i]; + if ((c <= 0x1F) || // control characters + (c >= 0x7F) || // over 7-bit printable ASCII + (c == '!') || (c == '|') || (c == '.') || + (c == ':') || (c == '>') || (c == '<') || + (c == '?') || (c == '/') || (c == '\\') || + (c == '*') || (c == '"') || (c == '&')) + { + chars[i] = '_'; + } + } + return String.valueOf(chars); + } - /* ------------------------------------------------------------ */ public static boolean startsWithIgnoreCase(String s,String w) { if (w==null) @@ -174,13 +201,11 @@ public class StringUtil } return true; } - - /* ------------------------------------------------------------ */ + public static boolean endsWithIgnoreCase(String s,String w) { if (w==null) return true; - if (s==null) return false; @@ -206,8 +231,7 @@ public class StringUtil } return true; } - - /* ------------------------------------------------------------ */ + /** * returns the next index of a character from the chars string * @param s the input string to search @@ -221,10 +245,13 @@ public class StringUtil return i; return -1; } - - /* ------------------------------------------------------------ */ + /** - * replace substrings within string. + * Replace substrings within string. + *

+ * Fast replacement for {@code java.lang.String#}{@link String#replace(CharSequence, CharSequence)} + *

+ * * @param s the input string * @param sub the string to look for * @param with the string to replace with @@ -232,27 +259,56 @@ public class StringUtil */ public static String replace(String s, String sub, String with) { - int c=0; - int i=s.indexOf(sub,c); + int c = 0; + int i = s.indexOf(sub, c); if (i == -1) + { return s; - - StringBuilder buf = new StringBuilder(s.length()+with.length()); - + } + StringBuilder buf = new StringBuilder(s.length() + with.length()); do { - buf.append(s.substring(c,i)); + buf.append(s, c, i); buf.append(with); - c=i+sub.length(); - } while ((i=s.indexOf(sub,c))!=-1); - - if (c + * Fast replacement for {@code java.lang.String#}{@link String#replaceFirst(String, String)}, but without + * Regex support. + *

+ * + * @param original the original string + * @param target the target string to look for + * @param replacement the replacement string to use + * @return the replaced string + */ + public static String replaceFirst(String original, String target, String replacement) + { + int idx = original.indexOf(target); + if (idx == -1) + return original; + + int offset = 0; + int originalLen = original.length(); + StringBuilder buf = new StringBuilder(originalLen + replacement.length()); + buf.append(original, offset, idx); + offset += idx + target.length(); + buf.append(replacement); + buf.append(original, offset, originalLen); + + return buf.toString(); + } + /** Remove single or double quotes. * @param s the input string * @return the string with quotes removed @@ -263,8 +319,6 @@ public class StringUtil return QuotedStringTokenizer.unquote(s); } - - /* ------------------------------------------------------------ */ /** Append substring to StringBuilder * @param buf StringBuilder to append to * @param s String to append from @@ -288,8 +342,6 @@ public class StringUtil } } - - /* ------------------------------------------------------------ */ /** * append hex digit * @param buf the buffer to append to @@ -310,7 +362,6 @@ public class StringUtil buf.append((char)c); } - /* ------------------------------------------------------------ */ /** * Append 2 digits (zero padded) to the StringBuffer * @@ -325,8 +376,7 @@ public class StringUtil buf.append((char)(i%10+'0')); } } - - /* ------------------------------------------------------------ */ + /** * Append 2 digits (zero padded) to the StringBuilder * @@ -341,8 +391,7 @@ public class StringUtil buf.append((char)(i%10+'0')); } } - - /* ------------------------------------------------------------ */ + /** Return a non null string. * @param s String * @return The string passed in or empty string if it is null. @@ -353,8 +402,7 @@ public class StringUtil return ""; return s; } - - /* ------------------------------------------------------------ */ + public static boolean equals(String s,char[] buf, int offset, int length) { if (s.length()!=length) @@ -365,13 +413,11 @@ public class StringUtil return true; } - /* ------------------------------------------------------------ */ public static String toUTF8String(byte[] b,int offset,int length) { return new String(b,offset,length,StandardCharsets.UTF_8); } - /* ------------------------------------------------------------ */ public static String toString(byte[] b,int offset,int length,String charset) { try @@ -380,6 +426,7 @@ public class StringUtil } catch (UnsupportedEncodingException e) { + LOG.warn(e); throw new IllegalArgumentException(e); } } @@ -431,7 +478,6 @@ public class StringUtil return -1; } - /* ------------------------------------------------------------ */ /** * Test if a string is null or only has whitespace characters in it. *

@@ -494,7 +540,6 @@ public class StringUtil return str == null || str.isEmpty(); } - /* ------------------------------------------------------------ */ /** * Test if a string is not null and contains at least 1 non-whitespace characters in it. *

@@ -534,14 +579,11 @@ public class StringUtil return false; } - /* ------------------------------------------------------------ */ public static boolean isUTF8(String charset) { return __UTF8.equalsIgnoreCase(charset)||__UTF8.equalsIgnoreCase(normalizeCharset(charset)); } - - /* ------------------------------------------------------------ */ public static String printable(String name) { if (name==null) @@ -556,7 +598,7 @@ public class StringUtil return buf.toString(); } - /* ------------------------------------------------------------ */ + public static String printable(byte[] b) { StringBuilder buf = new StringBuilder(); @@ -592,13 +634,10 @@ public class StringUtil } catch(Exception e) { - LOG.warn(e); return s.getBytes(); } } - - - + /** * Converts a binary SID to a string SID * @@ -631,7 +670,6 @@ public class StringUtil // the number of subAuthorities we need to attach int subAuthorityCount = sidBytes[1]; - // attach each of the subAuthorities for (int i = 0; i < subAuthorityCount; ++i) { @@ -670,10 +708,8 @@ public class StringUtil // the revision byte sidBytes[byteCount++] = (byte)Integer.parseInt(sidTokens[1]); - // the # of sub authorities byte sidBytes[byteCount++] = (byte)subAuthorityCount; - // the certAuthority String hexStr = Long.toHexString(Long.parseLong(sidTokens[2])); @@ -681,7 +717,6 @@ public class StringUtil { hexStr = "0" + hexStr; } - // place the certAuthority 6 bytes for ( int i = 0 ; i < hexStr.length(); i = i + 2) { @@ -720,7 +755,6 @@ public class StringUtil int val = 0; boolean started = false; boolean minus = false; - for (int i = from; i < string.length(); i++) { char b = string.charAt(i); @@ -741,7 +775,6 @@ public class StringUtil else break; } - if (started) return minus?(-val):val; throw new NumberFormatException(string); @@ -759,7 +792,6 @@ public class StringUtil long val = 0; boolean started = false; boolean minus = false; - for (int i = 0; i < string.length(); i++) { char b = string.charAt(i); @@ -780,7 +812,6 @@ public class StringUtil else break; } - if (started) return minus?(-val):val; throw new NumberFormatException(string); @@ -799,12 +830,10 @@ public class StringUtil { return null; } - if (str.length() <= maxSize) { return str; } - return str.substring(0,maxSize); } @@ -817,20 +846,18 @@ public class StringUtil { if (s==null) return new String[]{}; - if (!s.startsWith("[") || !s.endsWith("]")) throw new IllegalArgumentException(); if (s.length()==2) return new String[]{}; - return csvSplit(s,1,s.length()-2); } /** - * Parse a CSV string using {@link #csvSplit(List,String, int, int)} - * @param s The string to parse - * @return An array of parsed values. - */ + * Parse a CSV string using {@link #csvSplit(List,String, int, int)} + * @param s The string to parse + * @return An array of parsed values. + */ public static String[] csvSplit(String s) { if (s==null) @@ -851,13 +878,12 @@ public class StringUtil return null; if (off<0 || len<0 || off>s.length()) throw new IllegalArgumentException(); - List list = new ArrayList<>(); csvSplit(list,s,off,len); return list.toArray(new String[list.size()]); } - enum CsvSplitState { PRE_DATA, QUOTE, SLOSH, DATA, WHITE, POST_DATA }; + enum CsvSplitState { PRE_DATA, QUOTE, SLOSH, DATA, WHITE, POST_DATA } /** Split a quoted comma separated string to a list *

Handle rfc4180-like @@ -890,7 +916,6 @@ public class StringUtil case PRE_DATA: if (Character.isWhitespace(ch)) continue; - if ('"'==ch) { state=CsvSplitState.QUOTE; @@ -902,11 +927,9 @@ public class StringUtil list.add(""); continue; } - state=CsvSplitState.DATA; out.append(ch); continue; - case DATA: if (Character.isWhitespace(ch)) { @@ -923,7 +946,6 @@ public class StringUtil state=CsvSplitState.PRE_DATA; continue; } - out.append(ch); continue; @@ -947,7 +969,6 @@ public class StringUtil out.append(ch); last=-1; continue; - case QUOTE: if ('\\'==ch) { @@ -978,13 +999,11 @@ public class StringUtil continue; } } - switch(state) { case PRE_DATA: case POST_DATA: break; - case DATA: case QUOTE: case SLOSH: @@ -1011,7 +1030,6 @@ public class StringUtil loop: for (;iThis method calls {@link String#valueOf(Object)} unless the object is null, * in which case null is returned

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 50394f7c6fb..17da05adf80 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 @@ -268,7 +268,19 @@ public class URIUtil return buf; } - + + /** + * 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"); + } + /* ------------------------------------------------------------ */ /** Encode a URI path. * @param path The path the encode diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java index 78229fc30ba..ee9fc9df110 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java @@ -26,6 +26,7 @@ import java.util.Iterator; import java.util.Map; import java.util.stream.Stream; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedOperation; @@ -99,9 +100,15 @@ public interface Dumpable else if (o instanceof Map) s = String.format("%s@%x{size=%d}",o.getClass().getName(),o.hashCode(),((Map)o).size()); else if (o instanceof Dumpable) - s = ((Dumpable)o).dumpSelf().replace("\r\n","|").replace("\n","|"); + { + s = StringUtil.replace(((Dumpable)o).dumpSelf(), "\r\n", "|") + .replace('\n', '|'); + } else - s = String.valueOf(o).replace("\r\n","|").replace("\n","|"); + { + s = StringUtil.replace(String.valueOf(o), "\r\n", "|") + .replace('\n', '|'); + } if (o instanceof LifeCycle) out.append(s).append(" - ").append((AbstractLifeCycle.getState((LifeCycle)o))).append("\n"); diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/StringUtilTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/StringUtilTest.java index 51d53110410..f244de11b03 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/StringUtilTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/StringUtilTest.java @@ -18,19 +18,21 @@ package org.eclipse.jetty.util; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; - +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.arrayContaining; import static org.hamcrest.Matchers.emptyArray; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertTrue; public class StringUtilTest @@ -98,7 +100,36 @@ public class StringUtilTest s=" \u0690bc "; assertEquals(StringUtil.replace(s, "\u0690bc", "xyz")," xyz "); + } + public static Stream replaceFirstArgs() { + List data = new ArrayList<>(); + + // [original, target, replacement, expected] + + // no match + data.add(new String[]{ "abc", "z", "foo", "abc" }); + + // matches at start of string + data.add(new String[]{ "abc", "a", "foo", "foobc" }); + data.add(new String[]{ "abcabcabc", "a", "foo", "foobcabcabc" }); + + // matches in middle of string + data.add(new String[]{ "abc", "b", "foo", "afooc" }); + data.add(new String[]{ "abcabcabc", "b", "foo", "afoocabcabc" }); + data.add(new String[]{ "abcabcabc", "cab", "X", "abXcabc" }); + + // matches at end of string + data.add(new String[]{ "abc", "c", "foo", "abfoo" }); + + return data.stream(); + } + + @ParameterizedTest + @MethodSource(value = "replaceFirstArgs") + public void testReplaceFirst(String original, String target, String replacement, String expected) + { + assertThat(StringUtil.replaceFirst(original, target, replacement), is(expected)); } @Test @@ -165,50 +196,6 @@ public class StringUtilTest assertEquals(sid5, StringUtil.sidBytesToString(sid5Bytes)); assertEquals(sid6, StringUtil.sidBytesToString(sid6Bytes)); assertEquals(sid12, StringUtil.sidBytesToString(sid12Bytes)); - - } - - - public static void main(String[] arg) throws Exception - { - String string = "Now \u0690xxxxxxxx"; - System.err.println(string); - byte[] bytes=string.getBytes(StandardCharsets.UTF_8); - System.err.println(new String(bytes)); - System.err.println(bytes.length); - long calc=0; - Utf8StringBuffer strbuf = new Utf8StringBuffer(bytes.length); - for (int i=0;i<10;i++) - { - long s1=TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); - for (int j=1000000; j-->0;) - { - calc+=new String(bytes,0,bytes.length,StandardCharsets.UTF_8).hashCode(); - } - long s2=TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); - for (int j=1000000; j-->0;) - { - calc+=StringUtil.toUTF8String(bytes,0,bytes.length).hashCode(); - } - long s3=TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); - for (int j=1000000; j-->0;) - { - Utf8StringBuffer buffer = new Utf8StringBuffer(bytes.length); - buffer.append(bytes,0,bytes.length); - calc+=buffer.toString().hashCode(); - } - long s4=TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); - for (int j=1000000; j-->0;) - { - strbuf.reset(); - strbuf.append(bytes,0,bytes.length); - calc+=strbuf.toString().hashCode(); - } - long s5=TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); - - System.err.println((s2-s1)+", "+(s3-s2)+", "+(s4-s3)+", "+(s5-s4)); - } - System.err.println(calc); } @Test diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java index ee203b35e50..1ca05559cc9 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java @@ -703,7 +703,7 @@ public class ClasspathPattern extends AbstractSet name=name.substring(0,name.length()-6); // Treat path elements as packages for name matching - name=name.replace("/","."); + name = name.replace('/', '.'); return combine(_packageOrNamePatterns, name, _locations, ()-> { 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 b94f7881a69..20aec0b824d 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 @@ -38,6 +38,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.JavaVersion; import org.eclipse.jetty.util.PatternMatcher; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -210,7 +211,8 @@ public class WebInfConfiguration extends AbstractConfiguration } catch (URISyntaxException e) { - containerUris.add(new URI(u.toString().replaceAll(" ", "%20"))); + String fixedUriStr = StringUtil.replace(u.toString(), " ", "%20"); + containerUris.add(new URI(fixedUriStr)); } } } diff --git a/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/CookieDump.java b/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/CookieDump.java index 48d79149dec..68c0c98c128 100644 --- a/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/CookieDump.java +++ b/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/CookieDump.java @@ -21,7 +21,6 @@ package com.acme; import java.io.IOException; import java.io.PrintWriter; import java.util.concurrent.TimeUnit; - import javax.servlet.RequestDispatcher; import javax.servlet.ServletException; import javax.servlet.http.Cookie; @@ -29,7 +28,6 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - /** * Test Servlet Cookies. */ @@ -117,9 +115,9 @@ public class CookieDump extends HttpServlet { if (string==null) return null; - string=string.replace("&", "&"); - string=string.replace( "<", "<"); - string=string.replace( ">", ">"); + string = string.replace("&", "&"); + string = string.replace( "<", "<"); + string = string.replace( ">", ">"); return string; } diff --git a/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/Dump.java b/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/Dump.java index 1110e8bfeff..e2a2f575feb 100644 --- a/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/Dump.java +++ b/tests/test-webapps/test-jetty-webapp/src/main/java/com/acme/Dump.java @@ -36,7 +36,6 @@ import java.util.Enumeration; import java.util.Locale; import java.util.Timer; import java.util.TimerTask; - import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; @@ -57,13 +56,14 @@ import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; import javax.servlet.http.Part; - -/** +/** * Dump Servlet Request. */ @SuppressWarnings("serial") public class Dump extends HttpServlet { + /** Zero Width Space, to allow text to be wrapped at designated spots */ + private static final String ZWSP = "​"; boolean fixed; Timer _timer; @@ -647,7 +647,7 @@ public class Dump extends HttpServlet { name= (String)a.nextElement(); pout.write("\n"); - pout.write(""+name.replace("."," .")+": "); + pout.write("" + name.replace(".", ZWSP + ".") + ": "); Object value=request.getAttribute(name); if (value instanceof File) { @@ -678,7 +678,7 @@ public class Dump extends HttpServlet { name= (String)a.nextElement(); pout.write("\n"); - pout.write(""+name.replace("."," .")+": "); + pout.write("" + name.replace(".", ZWSP + ".") + ": "); pout.write(""+ toString(getServletContext().getInitParameter(name)) + ""); } @@ -689,7 +689,7 @@ public class Dump extends HttpServlet { name= (String)a.nextElement(); pout.write("\n"); - pout.write(""+name.replace("."," .")+": "); + pout.write("" + name.replace(".", ZWSP + ".") + ": "); pout.write(""+"
" + toString(getServletContext().getAttribute(name)) + "
"+""); } @@ -1055,9 +1055,9 @@ public class Dump extends HttpServlet { if (s==null) return "null"; - s=s.replaceAll("&","&"); - s=s.replaceAll("<","<"); - s=s.replaceAll(">",">"); + s = s.replace("&","&"); + s = s.replace("<","<"); + s = s.replace(">",">"); return s; } } From 245388210311e3fce7c4300ec9c848ca01fc9637 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 11 Jun 2019 13:02:40 -0500 Subject: [PATCH 2/4] Issue #3708 - Reverting change to RewriteRegexRule + Moving away from Regex / Pattern isn't appropriate here, as the entire class is dedicated to Regex behaviors. Signed-off-by: Joakim Erdfelt --- .../jetty/rewrite/handler/RewriteRegexRule.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java index f41cbdb9032..5423f88170c 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RewriteRegexRule.java @@ -24,7 +24,6 @@ import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.annotation.Name; /** @@ -96,22 +95,15 @@ public class RewriteRegexRule extends RegexRule implements Rule.ApplyURI group=""; else group = Matcher.quoteReplacement(group); - String dollarGroup = "$" + g; - target = StringUtil.replace(target, dollarGroup, group); - if (query != null) - query = StringUtil.replace(query, dollarGroup, group); + target=target.replaceAll("\\$"+g,group); + if (query!=null) + query=query.replaceAll("\\$"+g,group); } if (query!=null) { if (_queryGroup) - { - String replacement = ""; - if (request.getQueryString() != null) - replacement = request.getQueryString(); - - query = StringUtil.replace(query, "$Q", replacement); - } + query=query.replace("$Q",request.getQueryString()==null?"":request.getQueryString()); request.setAttribute("org.eclipse.jetty.rewrite.handler.RewriteRegexRule.Q",query); } From 877815e1959963f051a1c425405b0a4360f8a9e7 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 12 Jun 2019 09:56:41 -0500 Subject: [PATCH 3/4] Issue #3708 - Adding new methods and converting codebase to use them + StringUtil.replace(String, char, char) + StringUtil.strip(String, String) + URIUtil.encodeSpecific(String, String) + URIUtil.decodeSpecific(String, String) + TypeUtil.toClassReference(Class) + TypeUtil.toClassReference(String) Signed-off-by: Joakim Erdfelt --- .../asyncrest/AbstractRestServlet.java | 22 +++- .../eclipse/jetty/embedded/Http2Server.java | 3 +- .../jetty/annotations/AnnotationParser.java | 18 +-- .../annotations/TestAnnotationParser.java | 28 ++-- .../fcgi/client/http/HttpSenderOverFCGI.java | 3 +- .../org/eclipse/jetty/http/QuotedCSVTest.java | 12 +- .../jetty/http2/client/ProxyProtocolTest.java | 3 +- .../eclipse/jetty/io/ssl/SslConnection.java | 6 +- .../org/eclipse/jetty/jmx/MBeanContainer.java | 12 +- .../jetty/maven/plugin/JettyRunMojo.java | 3 +- .../maven/plugin/JettyWebAppContext.java | 6 +- .../maven/plugin/SelectiveJarResource.java | 3 +- .../nosql/mongodb/MongoSessionDataStore.java | 24 ++-- .../jetty/nosql/mongodb/MongoUtils.java | 12 +- .../osgi/annotations/AnnotationParser.java | 3 +- .../jetty/osgi/boot/BundleWebAppProvider.java | 2 +- .../webapp/OSGiWebappClassLoader.java | 6 +- .../jetty/plus/jndi/NamingEntryUtil.java | 6 +- .../jetty/proxy/AbstractProxyServlet.java | 4 +- .../eclipse/jetty/proxy/ProxyServletTest.java | 3 +- .../jetty/security/PropertyUserStore.java | 2 +- .../org/eclipse/jetty/server/Response.java | 4 +- .../jetty/server/session/SessionContext.java | 4 +- .../eclipse/jetty/server/AsyncStressTest.java | 8 +- .../eclipse/jetty/servlet/ServletHolder.java | 6 +- .../jetty/servlet/ResponseHeadersTest.java | 3 +- .../java/org/eclipse/jetty/servlets/CGI.java | 3 +- .../jetty/servlets/CrossOriginFilterTest.java | 5 +- .../org/eclipse/jetty/util/StringUtil.java | 73 +++++++++-- .../java/org/eclipse/jetty/util/TypeUtil.java | 42 +++++- .../java/org/eclipse/jetty/util/URIUtil.java | 115 ++++++++++++++++ .../jetty/util/component/Dumpable.java | 10 +- .../java/org/eclipse/jetty/util/log/Log.java | 3 +- .../jetty/util/resource/JarFileResource.java | 5 +- .../jetty/util/resource/JarResource.java | 3 +- .../eclipse/jetty/util/PathWatcherTest.java | 20 +-- .../org/eclipse/jetty/util/URIUtilTest.java | 123 +++++++++++++++++- .../jetty/webapp/ClasspathPattern.java | 3 +- .../jetty/webapp/WebAppClassLoader.java | 13 +- .../jetty/webapp/WebInfConfiguration.java | 18 +-- .../websocket/jsr356/server/WSServer.java | 11 +- .../jetty/websocket/server/WSServer.java | 3 +- .../test/support/TestableJettyServer.java | 54 ++++---- .../com/acme/test/ClassLoaderServlet.java | 1 - 44 files changed, 512 insertions(+), 199 deletions(-) diff --git a/examples/async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest/AbstractRestServlet.java b/examples/async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest/AbstractRestServlet.java index a3373fee901..b4779b8d8a8 100644 --- a/examples/async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest/AbstractRestServlet.java +++ b/examples/async-rest/async-rest-jar/src/main/java/org/eclipse/jetty/example/asyncrest/AbstractRestServlet.java @@ -24,7 +24,6 @@ import java.math.RoundingMode; import java.net.URLEncoder; import java.util.Map; import java.util.Queue; - import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -67,12 +66,25 @@ public class AbstractRestServlet extends HttpServlet _appid = servletConfig.getInitParameter(APPID_PARAM); } - - public static String sanitize(String s) + // TODO: consider using StringUtil.sanitizeFileSystemName instead of this? + // might introduce jetty-util dependency though + public static String sanitize(String str) { - if (s==null) + if (str == null) return null; - return s.replace("<","?").replace("&","?").replace("\n","?"); + + char[] chars = str.toCharArray(); + int len = chars.length; + for (int i = 0; i < len; i++) + { + char c = chars[i]; + if ((c <= 0x1F) || // control characters + (c == '<') || (c == '&')) + { + chars[i] = '?'; + } + } + return String.valueOf(chars); } protected String restURL(String item) diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java index 8f219520966..3f50d7ee953 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/Http2Server.java @@ -24,7 +24,6 @@ import java.io.IOException; import java.lang.management.ManagementFactory; import java.util.Date; import java.util.EnumSet; - import javax.servlet.DispatcherType; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -158,7 +157,7 @@ public class Http2Server public void destroy() { } - }; + } static Servlet servlet = new HttpServlet() { diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java index 0e7597a1de5..5b9da0b87a3 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java @@ -38,6 +38,8 @@ import org.eclipse.jetty.util.Loader; import org.eclipse.jetty.util.ManifestUtils; import org.eclipse.jetty.util.MultiException; import org.eclipse.jetty.util.MultiReleaseJarFile; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; @@ -151,7 +153,7 @@ public class AnnotationParser if (name.endsWith(".class")) name = name.substring(0, name.length()-".class".length()); - return name.replace('/', '.'); + return StringUtil.replace(name,'/', '.'); } /** @@ -600,7 +602,7 @@ public class AnnotationParser return; String tmp = className; - className = className.replace('.', '/')+".class"; + className = TypeUtil.toClassReference(className); URL resource = Loader.getResource(className); if (resource!= null) { @@ -626,7 +628,7 @@ public class AnnotationParser Class cz = clazz; while (cz != Object.class) { - String nameAsResource = cz.getName().replace('.', '/')+".class"; + String nameAsResource = TypeUtil.toClassReference(cz); URL resource = Loader.getResource(nameAsResource); if (resource!= null) { @@ -675,8 +677,7 @@ public class AnnotationParser { try { - String name = s; - s = s.replace('.', '/')+".class"; + String name = TypeUtil.toClassReference(s); URL resource = Loader.getResource(s); if (resource!= null) { @@ -727,8 +728,9 @@ public class AnnotationParser { Path classpath = rootFile.toPath().relativize(file.toPath()); String str = classpath.toString(); - str = str.substring(0, str.lastIndexOf(".class")).replace('/', '.').replace('\\', '.'); - + str = str.substring(0, str.lastIndexOf(".class")); + str = StringUtil.replace(str, File.separatorChar, '.'); + try { if (LOG.isDebugEnabled()) @@ -910,7 +912,7 @@ public class AnnotationParser //check file is a valid class file name if (isValidClassFileName(name) && isValidClassFilePath(name)) { - String shortName = name.replace('/', '.').substring(0,name.length()-6); + String shortName = StringUtil.replace(name, '/', '.').substring(0, name.length() - 6); addParsedClass(shortName, Resource.newResource("jar:"+jar.getURI()+"!/"+entry.getNameInJar())); if (LOG.isDebugEnabled()) LOG.debug("Scanning class from jar {}!/{}", jar, entry); diff --git a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java index 0d7cf14ddb8..05e54e8823c 100644 --- a/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java +++ b/jetty-annotations/src/test/java/org/eclipse/jetty/annotations/TestAnnotationParser.java @@ -19,11 +19,12 @@ package org.eclipse.jetty.annotations; import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; @@ -42,6 +43,7 @@ import org.eclipse.jetty.toolchain.test.IO; 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.TypeUtil; import org.eclipse.jetty.util.resource.PathResource; import org.eclipse.jetty.util.resource.Resource; import org.junit.jupiter.api.Test; @@ -298,25 +300,17 @@ public class TestAnnotationParser private void copyClass(Class clazz, File basedir) throws IOException { - String classname = clazz.getName().replace('.',File.separatorChar) + ".class"; - URL url = this.getClass().getResource('/'+classname); - assertThat("URL for: " + classname,url,notNullValue()); + String classRef = TypeUtil.toClassReference(clazz); + URL url = this.getClass().getResource('/' + classRef); + assertThat("URL for: " + classRef, url, notNullValue()); - String classpath = classname.substring(0,classname.lastIndexOf(File.separatorChar)); - FS.ensureDirExists(new File(basedir,classpath)); + Path outputFile = basedir.toPath().resolve(classRef); + FS.ensureDirExists(outputFile.getParent()); - InputStream in = null; - OutputStream out = null; - try + try (InputStream in = url.openStream(); + OutputStream out = Files.newOutputStream(outputFile)) { - in = url.openStream(); - out = new FileOutputStream(new File(basedir,classname)); - IO.copy(in,out); - } - finally - { - IO.close(out); - IO.close(in); + IO.copy(in, out); } } } diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java index 4f836fddfe2..2671b025c09 100644 --- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java +++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpSenderOverFCGI.java @@ -35,6 +35,7 @@ import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Jetty; +import org.eclipse.jetty.util.StringUtil; public class HttpSenderOverFCGI extends HttpSender { @@ -88,7 +89,7 @@ public class HttpSenderOverFCGI extends HttpSender for (HttpField field : headers) { String name = field.getName(); - String fcgiName = "HTTP_" + name.replace('-', '_').toUpperCase(Locale.ENGLISH); + String fcgiName = "HTTP_" + StringUtil.replace(name, '-', '_').toUpperCase(Locale.ENGLISH); fcgiHeaders.add(fcgiName, field.getValue()); } diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedCSVTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedCSVTest.java index 954fb562dfc..9a719a08583 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedCSVTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/QuotedCSVTest.java @@ -18,13 +18,13 @@ package org.eclipse.jetty.http; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - +import org.eclipse.jetty.util.StringUtil; import org.hamcrest.Matchers; - import org.junit.jupiter.api.Test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; + public class QuotedCSVTest { @Test @@ -111,13 +111,13 @@ public class QuotedCSVTest { if (buffer.toString().contains("DELETE")) { - String s = buffer.toString().replace("DELETE",""); + String s = StringUtil.strip(buffer.toString(), "DELETE"); buffer.setLength(0); buffer.append(s); } if (buffer.toString().contains("APPEND")) { - String s = buffer.toString().replace("APPEND","Append")+"!"; + String s = StringUtil.replace(buffer.toString(), "APPEND", "Append") + "!"; buffer.setLength(0); buffer.append(s); } diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyProtocolTest.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyProtocolTest.java index c06a555aa21..d63ab045dd5 100644 --- a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyProtocolTest.java +++ b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/ProxyProtocolTest.java @@ -48,6 +48,7 @@ import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.util.FuturePromise; import org.eclipse.jetty.util.Promise; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.TypeUtil; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; @@ -167,7 +168,7 @@ public class ProxyProtocolTest // String is: "MAGIC VER|CMD FAM|PROT LEN SRC_ADDR DST_ADDR SRC_PORT DST_PORT PP2_TYPE_SSL LEN CLIENT VERIFY PP2_SUBTYPE_SSL_VERSION LEN 1.2" String request1 = "0D0A0D0A000D0A515549540A 21 11 001A 0A000004 0A000005 8420 22B8 20 000B 01 00000000 21 0003 312E32"; - request1 = request1.replace(" ", ""); + request1 = StringUtil.strip(request1, " "); SocketChannel channel = SocketChannel.open(); channel.connect(new InetSocketAddress("localhost", connector.getLocalPort())); channel.write(ByteBuffer.wrap(TypeUtil.fromHexString(request1))); diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java index a2c1fdc732e..94485855edb 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java @@ -25,7 +25,6 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicReference; - import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.HandshakeStatus; @@ -41,6 +40,7 @@ import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.WriteFlusher; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.Invocable; @@ -584,7 +584,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr if (LOG.isDebugEnabled()) LOG.debug("unwrap net_filled={} {} encryptedBuffer={} unwrapBuffer={} appBuffer={}", net_filled, - unwrapResult.toString().replace('\n', ' '), + StringUtil.replace(unwrapResult.toString(), '\n', ' '), BufferUtil.toSummaryString(_encryptedInput), BufferUtil.toDetailString(app_in), BufferUtil.toDetailString(buffer)); @@ -895,7 +895,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr } if (LOG.isDebugEnabled()) LOG.debug("wrap {} {} ioDone={}/{}", - wrapResult.toString().replace('\n', ' '), + StringUtil.replace(wrapResult.toString(), '\n', ' '), BufferUtil.toSummaryString(_encryptedOutput), _sslEngine.isInboundDone(), _sslEngine.isOutboundDone()); diff --git a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java index 42955478c5c..8b75436f0c1 100644 --- a/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java +++ b/jetty-jmx/src/main/java/org/eclipse/jetty/jmx/MBeanContainer.java @@ -30,7 +30,6 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; - import javax.management.InstanceNotFoundException; import javax.management.MBeanInfo; import javax.management.MBeanRegistrationException; @@ -39,6 +38,7 @@ import javax.management.ObjectName; import javax.management.modelmbean.ModelMBean; import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.Container; @@ -394,15 +394,7 @@ public class MBeanContainer implements Container.InheritedListener, Dumpable, De */ public String makeName(String basis) { - if (basis == null) - return null; - return basis - .replace(':', '_') - .replace('*', '_') - .replace('?', '_') - .replace('=', '_') - .replace(',', '_') - .replace(' ', '_'); + return StringUtil.sanitizeFileSystemName(basis); } @Override diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java index 56b50cf11cf..c90dd62e3f6 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyRunMojo.java @@ -43,6 +43,7 @@ import org.apache.maven.project.MavenProject; import org.eclipse.jetty.maven.plugin.utils.MavenProjectHelper; import org.eclipse.jetty.util.PathWatcher; import org.eclipse.jetty.util.PathWatcher.PathWatchEvent; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.util.resource.ResourceCollection; import org.eclipse.jetty.webapp.WebAppContext; @@ -661,7 +662,7 @@ public class JettyRunMojo extends AbstractJettyMojo int i = name.lastIndexOf('/'); if (i>0) name = name.substring(i+1,name.length()); - name = name.replace('.', '_'); + name = StringUtil.replace(name, '.', '_'); //name = name+(++COUNTER); //add some digits to ensure uniqueness File overlaysDir = new File (project.getBuild().getDirectory(), "jetty_overlays"); File dir = new File(overlaysDir, name); diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java index 98abd11fa47..0c3d250ae00 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/JettyWebAppContext.java @@ -514,7 +514,7 @@ public class JettyWebAppContext extends WebAppContext int i=0; while (res == null && (i < _webInfClasses.size())) { - String newPath = uri.replace(WEB_INF_CLASSES_PREFIX, _webInfClasses.get(i).getPath()); + String newPath = StringUtil.replace(uri, WEB_INF_CLASSES_PREFIX, _webInfClasses.get(i).getPath()); res = Resource.newResource(newPath); if (!res.exists()) { @@ -529,7 +529,7 @@ public class JettyWebAppContext extends WebAppContext { // Return the real jar file for all accesses to // /WEB-INF/lib/*.jar - String jarName = uri.replace(WEB_INF_LIB_PREFIX, ""); + String jarName = StringUtil.strip(uri, WEB_INF_LIB_PREFIX); if (jarName.startsWith("/") || jarName.startsWith("\\")) jarName = jarName.substring(1); if (jarName.length()==0) @@ -581,7 +581,7 @@ public class JettyWebAppContext extends WebAppContext while (i < _webInfClasses.size()) { - String newPath = path.replace(WEB_INF_CLASSES_PREFIX, _webInfClasses.get(i).getPath()); + String newPath = StringUtil.replace(path, WEB_INF_CLASSES_PREFIX, _webInfClasses.get(i).getPath()); allPaths.addAll(super.getResourcePaths(newPath)); i++; } diff --git a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/SelectiveJarResource.java b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/SelectiveJarResource.java index 91a9d4b9623..2830d5a1469 100644 --- a/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/SelectiveJarResource.java +++ b/jetty-maven-plugin/src/main/java/org/eclipse/jetty/maven/plugin/SelectiveJarResource.java @@ -33,6 +33,7 @@ import java.util.jar.Manifest; import org.codehaus.plexus.util.SelectorUtils; import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -146,7 +147,7 @@ public class SelectiveJarResource extends JarResource String entryName = entry.getName(); LOG.debug("Looking at "+entryName); - String dotCheck = entryName.replace('\\', '/'); + String dotCheck = StringUtil.replace(entryName, '\\', '/'); dotCheck = URIUtil.canonicalPath(dotCheck); if (dotCheck == null) { diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.java index ab0b3fa6579..81630dff3d1 100644 --- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.java +++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoSessionDataStore.java @@ -27,16 +27,6 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -import org.eclipse.jetty.nosql.NoSqlSessionDataStore; -import org.eclipse.jetty.server.session.SessionContext; -import org.eclipse.jetty.server.session.SessionData; -import org.eclipse.jetty.server.session.UnreadableSessionDataException; -import org.eclipse.jetty.util.ClassLoadingObjectInputStream; -import org.eclipse.jetty.util.annotation.ManagedAttribute; -import org.eclipse.jetty.util.annotation.ManagedObject; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; - import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObjectBuilder; @@ -46,6 +36,16 @@ import com.mongodb.DBObject; import com.mongodb.MongoException; import com.mongodb.WriteConcern; import com.mongodb.WriteResult; +import org.eclipse.jetty.nosql.NoSqlSessionDataStore; +import org.eclipse.jetty.server.session.SessionContext; +import org.eclipse.jetty.server.session.SessionData; +import org.eclipse.jetty.server.session.UnreadableSessionDataException; +import org.eclipse.jetty.util.ClassLoadingObjectInputStream; +import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.annotation.ManagedAttribute; +import org.eclipse.jetty.util.annotation.ManagedObject; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; /** * MongoSessionDataStore @@ -574,8 +574,8 @@ public class MongoSessionDataStore extends NoSqlSessionDataStore { if (vhost == null) return ""; - - return vhost.replace('.', '_'); + + return StringUtil.replace(vhost, '.', '_'); } diff --git a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoUtils.java b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoUtils.java index a66ca44cc54..afe3c76f984 100644 --- a/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoUtils.java +++ b/jetty-nosql/src/main/java/org/eclipse/jetty/nosql/mongodb/MongoUtils.java @@ -30,7 +30,7 @@ import java.util.Map; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; import org.eclipse.jetty.util.ClassLoadingObjectInputStream; -import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.URIUtil; /** * MongoUtils @@ -72,18 +72,12 @@ public class MongoUtils public static String decodeName(String name) { - String cleaned = name; - cleaned = StringUtil.replace(cleaned, "%2E", "."); - cleaned = StringUtil.replace(cleaned, "%25", "%"); - return cleaned; + return URIUtil.decodeSpecific(name, ".%"); } public static String encodeName(String name) { - String cleaned = name; - cleaned = StringUtil.replace(cleaned, "%", "%25"); - cleaned = StringUtil.replace(cleaned, ".", "%2E"); - return cleaned; + return URIUtil.encodeSpecific(name, ".%"); } public static Object encodeName(Object value) throws IOException diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java index 5b7be2e2510..d520cb8316e 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationParser.java @@ -30,6 +30,7 @@ import java.util.TreeSet; import java.util.concurrent.ConcurrentHashMap; import org.eclipse.jetty.osgi.boot.utils.BundleFileLocatorHelperFactory; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.resource.Resource; import org.objectweb.asm.Opcodes; import org.osgi.framework.Bundle; @@ -209,7 +210,7 @@ public class AnnotationParser extends org.eclipse.jetty.annotations.AnnotationPa continue; } //transform into a classname to pass to the resolver - String shortName = name.replace('/', '.').substring(0,name.length()-6); + String shortName = StringUtil.replace(name, '/', '.').substring(0, name.length() - 6); addParsedClass(shortName, getResource(bundle)); try (InputStream classInputStream = classUrl.openStream()) diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java index a3768471d2b..fb3fdea4c94 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/BundleWebAppProvider.java @@ -280,7 +280,7 @@ public class BundleWebAppProvider extends AbstractWebAppProvider implements Bund // the location will often reflect the version. // maybe this is relevant when the file is a war) String location = bundle.getLocation(); - String toks[] = location.replace('\\', '/').split("/"); + String toks[] = StringUtil.replace(location, '\\', '/').split("/"); contextPath = toks[toks.length - 1]; // remove .jar, .war etc: int lastDot = contextPath.lastIndexOf('.'); diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java index 5e4212ff8c0..9afb705ad34 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/boot/internal/webapp/OSGiWebappClassLoader.java @@ -30,10 +30,10 @@ import java.util.List; import java.util.Set; import java.util.StringTokenizer; import java.util.jar.JarFile; - import javax.servlet.http.HttpServlet; import org.eclipse.jetty.osgi.boot.utils.BundleClassLoaderHelperFactory; +import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; @@ -61,12 +61,12 @@ public class OSGiWebappClassLoader extends WebAppClassLoader implements BundleRe public static void addClassThatIdentifiesAJarThatMustBeRejected(Class zclass) { - JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(zclass.getName().replace('.', '/') + ".class"); + JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(TypeUtil.toClassReference(zclass.getName())); } public static void addClassThatIdentifiesAJarThatMustBeRejected(String zclassName) { - JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(zclassName.replace('.', '/') + ".class"); + JAR_WITH_SUCH_CLASS_MUST_BE_EXCLUDED.add(TypeUtil.toClassReference(zclassName)); } static diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntryUtil.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntryUtil.java index f753bcfa218..956488be3a0 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntryUtil.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntryUtil.java @@ -30,6 +30,7 @@ import javax.naming.NameParser; import javax.naming.NamingEnumeration; import javax.naming.NamingException; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -238,8 +239,9 @@ public class NamingEntryUtil if (scope==null) return ""; - String str = scope.getClass().getName()+"@"+Long.toHexString(scope.hashCode()); - str=str.replace('/', '_').replace(' ', '_'); + String str = scope.getClass().getName() + "@" + Long.toHexString(scope.hashCode()); + // TODO: Is this sanitize step still needed? (this canonicalizeScope method has been reduced in functionality a lot over the years) + str = StringUtil.sanitizeFileSystemName(str); return str; } } diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java index 0badfed54c7..cadc3cf5b62 100644 --- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java +++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/AbstractProxyServlet.java @@ -29,7 +29,6 @@ import java.util.Locale; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.TimeoutException; - import javax.servlet.AsyncContext; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; @@ -51,6 +50,7 @@ import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.util.HttpCookieStore; import org.eclipse.jetty.util.ProcessorUtils; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.QueuedThreadPool; @@ -207,7 +207,7 @@ public abstract class AbstractProxyServlet extends HttpServlet protected Logger createLogger() { String servletName = getServletConfig().getServletName(); - servletName = servletName.replace('-', '.'); + servletName = StringUtil.replace(servletName, '-', '.'); if ((getClass().getPackage() != null) && !servletName.startsWith(getClass().getPackage().getName())) { servletName = getClass().getName() + "." + servletName; diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java index 71afb0e0904..762c0ab33bb 100644 --- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java +++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java @@ -85,6 +85,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.hamcrest.Matchers; import org.junit.jupiter.api.AfterEach; @@ -669,7 +670,7 @@ public class ProxyServletTest // Make the request to the proxy, it should transparently forward to the server ContentResponse response = client.newRequest("localhost", proxyConnector.getLocalPort()) - .path((prefix + target).replace("//", "/")) + .path(StringUtil.replace((prefix + target), "//", "/")) .timeout(5, TimeUnit.SECONDS) .send(); assertEquals(200, response.getStatus()); diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java b/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java index 9d0ea1004b4..b7e9a63269e 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/PropertyUserStore.java @@ -141,7 +141,7 @@ public class PropertyUserStore extends UserStore implements PathWatcher.Listener if (colon < 0 || bang_slash < 0 || colon > bang_slash) throw new IllegalArgumentException("Not resolved JarFile resource: " + uri); - String entry_path = StringUtil.sanitizeFileSystemPath(uri.substring(colon + 2)); + String entry_path = StringUtil.sanitizeFileSystemName(uri.substring(colon + 2)); Path tmpDirectory = Files.createTempDirectory("users_store"); tmpDirectory.toFile().deleteOnExit(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java index 1809d5b1cea..e03c00711b9 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java @@ -235,7 +235,7 @@ public class Response implements HttpServletResponse if (i >= 0) { httpOnly = true; - comment = StringUtil.replace(comment.trim(), HTTP_ONLY_COMMENT, ""); + comment = StringUtil.strip(comment.trim(), HTTP_ONLY_COMMENT); if (comment.length() == 0) comment = null; } @@ -1144,7 +1144,7 @@ public class Response implements HttpServletResponse return; _locale = locale; - _fields.put(HttpHeader.CONTENT_LANGUAGE, locale.toString().replace('_', '-')); + _fields.put(HttpHeader.CONTENT_LANGUAGE, StringUtil.replace(locale.toString(), '_', '-')); if (_outputType != OutputType.NONE) return; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionContext.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionContext.java index 70e9ef8cdf3..97235ecac05 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionContext.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/SessionContext.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.server.session; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.server.handler.ContextHandler.Context; +import org.eclipse.jetty.util.StringUtil; /** * SessionContext @@ -138,7 +139,6 @@ public class SessionContext if (path==null) return ""; - return path.replace('/', '_').replace('.','_').replace('\\','_'); + return StringUtil.sanitizeFileSystemName(path); } - } diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java index 5982de96d0a..4b74de2bc02 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/AsyncStressTest.java @@ -18,8 +18,6 @@ package org.eclipse.jetty.server; -import static org.junit.jupiter.api.Assertions.assertEquals; - import java.io.IOException; import java.io.InputStream; import java.net.InetAddress; @@ -29,7 +27,6 @@ import java.util.Random; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; - import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; @@ -40,6 +37,7 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.QueuedThreadPool; @@ -49,6 +47,8 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; + @Disabled @Tag("stress") public class AsyncStressTest @@ -123,7 +123,7 @@ public class AsyncStressTest int p=path[i][l]=_random.nextInt(__paths.length); int period = _random.nextInt(290)+10; - String uri=__paths[p][0].replace("",Integer.toString(period)); + String uri = StringUtil.replace(__paths[p][0], "", Integer.toString(period)); long start=TimeUnit.NANOSECONDS.toMillis(System.nanoTime()); String request = diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java index 8ed3d020e8a..1d0352c0307 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHolder.java @@ -966,7 +966,7 @@ public class ServletHolder extends Holder implements UserIdentity.Scope } catch (Exception e) { - String tmp = jsp.replace('.','_'); + String tmp = StringUtil.replace(jsp, '.', '_'); if (LOG.isDebugEnabled()) { LOG.warn("JspUtil.makeJavaIdentifier failed for jsp "+jsp +" using "+tmp+" instead"); @@ -1003,9 +1003,9 @@ public class ServletHolder extends Holder implements UserIdentity.Scope s = 1; //remove the element after last slash, which should be name of jsp - tmp = tmp.substring(s,i); + tmp = tmp.substring(s,i).trim(); - tmp = tmp.replace('/','.').trim(); + tmp = StringUtil.replace(tmp, '/', '.'); tmp = (".".equals(tmp)? "": tmp); if (LOG.isDebugEnabled()) { diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java index eb801824bea..f9d1caa5612 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/ResponseHeadersTest.java @@ -30,6 +30,7 @@ import org.eclipse.jetty.http.HttpTester; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.server.LocalConnector; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.util.StringUtil; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -143,7 +144,7 @@ public class ResponseHeadersTest assertThat("Response Code",response.getStatus(),is(200)); assertThat("Response Header Content-Type",response.get("Content-Type"),is("text/plain;charset=UTF-8")); - String expected = actualPathInfo.replace("%0A", " "); // replace OBS fold with space + String expected = StringUtil.replace(actualPathInfo, "%0A", " "); // replace OBS fold with space expected = URLDecoder.decode(expected, "utf-8"); // decode the rest expected = expected.trim(); // trim whitespace at start/end assertThat("Response Header X-example", response.get("X-Example"), is(expected)); diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java index 63222907c08..39929a87a56 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CGI.java @@ -29,7 +29,6 @@ import java.util.Enumeration; import java.util.HashMap; import java.util.Locale; import java.util.Map; - import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -324,7 +323,7 @@ public class CGI extends HttpServlet if (name.equalsIgnoreCase("Proxy")) continue; String value = req.getHeader(name); - env.set("HTTP_" + name.toUpperCase(Locale.ENGLISH).replace('-', '_'), value); + env.set("HTTP_" + StringUtil.replace(name.toUpperCase(Locale.ENGLISH), '-', '_'), value); } // these extra ones were from printenv on www.dev.nomura.co.uk diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java index 5687334d40a..598c40beceb 100644 --- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java +++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java @@ -34,6 +34,7 @@ import org.eclipse.jetty.http.HttpTester; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletTester; +import org.eclipse.jetty.util.StringUtil; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -94,7 +95,7 @@ public class CrossOriginFilterTest CountDownLatch latch = new CountDownLatch(1); tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*"); - String otherOrigin = origin.replace("localhost", "127.0.0.1"); + String otherOrigin = StringUtil.replace(origin, "localhost", "127.0.0.1"); String request = "" + "GET / HTTP/1.1\r\n" + "Host: localhost\r\n" + @@ -284,7 +285,7 @@ public class CrossOriginFilterTest { FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter()); String origin = "http://localhost"; - String otherOrigin = origin.replace("localhost", "127.0.0.1"); + String otherOrigin = StringUtil.replace(origin, "localhost", "127.0.0.1"); filterHolder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, origin + "," + otherOrigin); tester.getContext().addFilter(filterHolder, "/*", EnumSet.of(DispatcherType.REQUEST)); diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java index 0bf5a431194..b2e148ea97a 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/StringUtil.java @@ -142,7 +142,7 @@ public class StringUtil /** * Replace all characters from input string that are known to have - * special meaning in various filesytems. + * special meaning in various filesystems. * *

* This will replace all of the following characters @@ -151,14 +151,18 @@ public class StringUtil *

    *
  • Control Characters
  • *
  • Anything not 7-bit printable ASCII
  • - *
  • Special characters "{@code !|/.,:&><\?*"}"
  • + *
  • Special characters: pipe, redirect, combine, slash, equivalence, bang, glob, selection, etc...
  • + *
  • Space
  • *
* * @param str the raw input string - * @return the sanitized output string. + * @return the sanitized output string. or null if {@code str} is null. */ - public static String sanitizeFileSystemPath(String str) + public static String sanitizeFileSystemName(String str) { + if (str == null) + return null; + char[] chars = str.toCharArray(); int len = chars.length; for (int i = 0; i < len; i++) @@ -166,10 +170,18 @@ public class StringUtil char c = chars[i]; if ((c <= 0x1F) || // control characters (c >= 0x7F) || // over 7-bit printable ASCII - (c == '!') || (c == '|') || (c == '.') || - (c == ':') || (c == '>') || (c == '<') || - (c == '?') || (c == '/') || (c == '\\') || - (c == '*') || (c == '"') || (c == '&')) + // piping : special meaning on unix / osx / windows + (c == '|') || (c == '>') || (c == '<') || (c == '/') || (c == '&') || + // special characters on windows + (c == '\\') || (c == '.') || (c == ':') || + // special characters on osx + (c == '=') || (c == '"') || (c == ',') || + // glob / selection characters on most OS's + (c == '*') || (c == '?') || + // bang execution on unix / osx + (c == '!') || + // spaces are just generally difficult to work with + (c == ' ')) { chars[i] = '_'; } @@ -246,6 +258,41 @@ public class StringUtil return -1; } + /** + * Replace chars within string. + *

+ * Fast replacement for {@code java.lang.String#}{@link String#replace(char, char)} + *

+ * + * @param str the input string + * @param find the char to look for + * @param with the char to replace with + * @return the now replaced string + */ + public static String replace(String str, char find, char with) + { + if (str == null) + return null; + + if (find == with) + return str; + + int c = 0; + int idx = str.indexOf(find, c); + if (idx == -1) + { + return str; + } + char[] chars = str.toCharArray(); + int len = chars.length; + for (int i = idx; i < len; i++) + { + if (chars[i] == find) + chars[i] = with; + } + return String.valueOf(chars); + } + /** * Replace substrings within string. *

@@ -259,6 +306,9 @@ public class StringUtil */ public static String replace(String s, String sub, String with) { + if (s == null) + return null; + int c = 0; int i = s.indexOf(sub, c); if (i == -1) @@ -277,7 +327,7 @@ public class StringUtil { buf.append(s.substring(c)); } - return buf.toString(); + return buf.toString(); } /** @@ -1082,6 +1132,11 @@ public class StringUtil return out.toString(); } + public static String strip(String str, String find) + { + return StringUtil.replace(str, find, ""); + } + /** The String value of an Object *

This method calls {@link String#valueOf(Object)} unless the object is null, * in which case null is returned

diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java index cb675f97cf5..93c705b1ca3 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/TypeUtil.java @@ -206,6 +206,46 @@ public class TypeUtil return class2Name.get(type); } + /** + * Return the Classpath / Classloader reference for the + * provided class file. + * + *

+ * Convenience method for the code + *

+ * + *
+     * String ref = myObject.getClass().getName().replace('.','/') + ".class";
+     * 
+ * + * @param clazz the class to reference + * @return the classpath reference syntax for the class file + */ + public static String toClassReference(Class clazz) + { + return TypeUtil.toClassReference(clazz.getName()); + } + + /** + * Return the Classpath / Classloader reference for the + * provided class file. + * + *

+ * Convenience method for the code + *

+ * + *
+     * String ref = myClassName.replace('.','/') + ".class";
+     * 
+ * + * @param className the class to reference + * @return the classpath reference syntax for the class file + */ + public static String toClassReference(String className) + { + return StringUtil.replace(className, '.', '/').concat(".class"); + } + /* ------------------------------------------------------------ */ /** Convert String value to instance. * @param type The class of the instance, which may be a primitive TYPE field. @@ -560,7 +600,7 @@ public class TypeUtil } } - String resourceName = clazz.getName().replace('.', '/') + ".class"; + String resourceName = TypeUtil.toClassReference(clazz); ClassLoader loader = clazz.getClassLoader(); URL url = (loader == null ? ClassLoader.getSystemClassLoader() : loader).getResource(resourceName); if (url != null) 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 17da05adf80..a0123e8c39b 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 @@ -281,6 +281,121 @@ public class URIUtil return StringUtil.replace(str, " ", "%20"); } + /** + * Encode a raw String and convert any specific characters to their URI encoded equivalent. + * + * @param str input raw string + * @param charsToEncode the list of raw characters that need to be encoded (if encountered) + * @return output with specified characters encoded. + */ + @SuppressWarnings("Duplicates") + public static String encodeSpecific(String str, String charsToEncode) + { + if ((str == null) || (str.length() == 0)) + return null; + + if ((charsToEncode == null) || (charsToEncode.length() == 0)) + return str; + + char[] find = charsToEncode.toCharArray(); + int len = str.length(); + StringBuilder ret = new StringBuilder((int)(len * 0.20d)); + for (int i = 0; i < len; i++) + { + char c = str.charAt(i); + boolean escaped = false; + for (char f : find) + { + if (c == f) + { + escaped = true; + ret.append('%'); + int d = 0xf & ((0xF0 & c) >> 4); + ret.append((char)((d > 9 ? ('A' - 10) : '0') + d)); + d = 0xf & c; + ret.append((char)((d > 9 ? ('A' - 10) : '0') + d)); + break; + } + } + if (!escaped) + { + ret.append(c); + } + } + return ret.toString(); + } + + /** + * 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. + * @return output with specified characters decoded. + */ + @SuppressWarnings("Duplicates") + public static String decodeSpecific(String str, String charsToDecode) + { + if ((str == null) || (str.length() == 0)) + return null; + + if ((charsToDecode == null) || (charsToDecode.length() == 0)) + return str; + + int idx = str.indexOf('%'); + if (idx == -1) + { + // no hits + return str; + } + + char[] find = charsToDecode.toCharArray(); + int len = str.length(); + Utf8StringBuilder ret = new Utf8StringBuilder(len); + ret.append(str, 0, idx); + + for (int i = idx; i < len; i++) + { + char c = str.charAt(i); + switch (c) + { + case '%': + 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) + { + if (f == result) + { + ret.append(result); + decoded = true; + break; + } + } + if (decoded) + { + i += 2; + } + else + { + ret.append(c); + } + } + else + { + throw new IllegalArgumentException("Bad URI % encoding"); + } + break; + default: + ret.append(c); + break; + } + } + return ret.toString(); + } + /* ------------------------------------------------------------ */ /** Encode a URI path. * @param path The path the encode diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java index ee9fc9df110..1e973f3f5c6 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/component/Dumpable.java @@ -101,13 +101,15 @@ public interface Dumpable s = String.format("%s@%x{size=%d}",o.getClass().getName(),o.hashCode(),((Map)o).size()); else if (o instanceof Dumpable) { - s = StringUtil.replace(((Dumpable)o).dumpSelf(), "\r\n", "|") - .replace('\n', '|'); + s = ((Dumpable)o).dumpSelf(); + s = StringUtil.replace(s, "\r\n", "|"); + s = StringUtil.replace(s, '\n', '|'); } else { - s = StringUtil.replace(String.valueOf(o), "\r\n", "|") - .replace('\n', '|'); + s = String.valueOf(o); + s = StringUtil.replace(s, "\r\n", "|"); + s = StringUtil.replace(s, '\n', '|'); } if (o instanceof LifeCycle) diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java index 5a8e6c49dd1..71d6861216a 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/log/Log.java @@ -33,6 +33,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.eclipse.jetty.util.Loader; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.Uptime; import org.eclipse.jetty.util.annotation.ManagedAttribute; @@ -102,7 +103,7 @@ public class Log // NOTE: cannot use jetty-util's StringUtil as that initializes logging itself. if (osName != null && osName.length() > 0) { - osName = osName.toLowerCase(Locale.ENGLISH).replace(' ','-'); + osName = StringUtil.replace(osName.toLowerCase(Locale.ENGLISH), ' ', '-'); loadProperties("jetty-logging-" + osName + ".properties",__props); } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java index 406781fa8ca..d7861a37baf 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarFileResource.java @@ -29,6 +29,7 @@ import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -326,8 +327,8 @@ public class JarFileResource extends JarResource String dir=_urlString.substring(_urlString.lastIndexOf("!/")+2); while(e.hasMoreElements()) { - JarEntry entry = e.nextElement(); - String name=entry.getName().replace('\\','/'); + JarEntry entry = e.nextElement(); + String name = StringUtil.replace(entry.getName(), '\\', '/'); if(!name.startsWith(dir) || name.length()==dir.length()) { continue; diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarResource.java b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarResource.java index 199141c57c7..099f15276e9 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarResource.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/resource/JarResource.java @@ -32,6 +32,7 @@ import java.util.jar.JarInputStream; import java.util.jar.Manifest; import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -212,7 +213,7 @@ public class JarResource extends URLResource continue; } - String dotCheck = entryName.replace('\\', '/'); + String dotCheck = StringUtil.replace(entryName, '\\', '/'); dotCheck = URIUtil.canonicalPath(dotCheck); if (dotCheck == null) { diff --git a/jetty-util/src/test/java/org/eclipse/jetty/util/PathWatcherTest.java b/jetty-util/src/test/java/org/eclipse/jetty/util/PathWatcherTest.java index 774103192d3..72eb0a5aecf 100644 --- a/jetty-util/src/test/java/org/eclipse/jetty/util/PathWatcherTest.java +++ b/jetty-util/src/test/java/org/eclipse/jetty/util/PathWatcherTest.java @@ -18,15 +18,6 @@ package org.eclipse.jetty.util; -import static org.eclipse.jetty.util.PathWatcher.PathWatchEventType.ADDED; -import static org.eclipse.jetty.util.PathWatcher.PathWatchEventType.DELETED; -import static org.eclipse.jetty.util.PathWatcher.PathWatchEventType.MODIFIED; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.contains; -import static org.hamcrest.Matchers.greaterThan; -import static org.hamcrest.Matchers.is; -import static org.junit.jupiter.api.Assertions.assertTrue; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -52,6 +43,15 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; +import static org.eclipse.jetty.util.PathWatcher.PathWatchEventType.ADDED; +import static org.eclipse.jetty.util.PathWatcher.PathWatchEventType.DELETED; +import static org.eclipse.jetty.util.PathWatcher.PathWatchEventType.MODIFIED; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.contains; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.junit.jupiter.api.Assertions.assertTrue; + @Disabled @ExtendWith(WorkDirExtension.class) public class PathWatcherTest @@ -111,7 +111,7 @@ public class PathWatcherTest synchronized (events) { Path relativePath = this.baseDir.relativize(event.getPath()); - String key = relativePath.toString().replace(File.separatorChar,'/'); + String key = StringUtil.replace(relativePath.toString(), File.separatorChar, '/'); List types = this.events.get(key); if (types == null) 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 92838feb54c..45f474aef9a 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,18 +18,22 @@ package org.eclipse.jetty.util; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertTrue; -import java.net.URI; -import java.nio.charset.StandardCharsets; - -import org.junit.jupiter.api.Test; - - /** * URIUtil Tests. */ @@ -314,4 +318,109 @@ public class URIUtilTest assertThat(URIUtil.getJarSource(new URI("jar:file:///tmp/foo.jar")),is(new URI("file:///tmp/foo.jar"))); assertThat(URIUtil.getJarSource(new URI("jar:file:///tmp/foo.jar!/some/path")),is(new URI("file:///tmp/foo.jar"))); } + + public static Stream encodeSpaces() + { + List data = new ArrayList<>(); + + // [raw, expected] + + // null + data.add(new String[]{null, null}); + + // no spaces + data.add(new String[]{"abc", "abc"}); + + // match + data.add(new String[]{"a c", "a%20c"}); + data.add(new String[]{" ", "%20%20%20"}); + data.add(new String[]{"a%20space", "a%20space"}); + + return data.stream(); + } + + @ParameterizedTest + @MethodSource(value = "encodeSpaces") + public void testEncodeSpaces(String raw, String expected) + { + assertThat(URIUtil.encodeSpaces(raw), is(expected)); + } + + public static Stream encodeSpecific() + { + List data = new ArrayList<>(); + + // [raw, chars, expected] + + // null input + data.add(new String[]{null, null, null}); + + // null chars + data.add(new String[]{"abc", null, "abc"}); + + // empty chars + data.add(new String[]{"abc", "", "abc"}); + + // no matches + data.add(new String[]{"abc", ".;", "abc"}); + data.add(new String[]{"xyz", ".;", "xyz"}); + data.add(new String[]{":::", ".;", ":::"}); + + // matches + data.add(new String[]{"a c", " ", "a%20c"}); + data.add(new String[]{"name=value", "=", "name%3Dvalue"}); + data.add(new String[]{"This has fewer then 10% hits.", ".%", "This has fewer then 10%25 hits%2E"}); + + // partially encoded already + data.add(new String[]{"a%20name=value%20pair", "=", "a%20name%3Dvalue%20pair"}); + data.add(new String[]{"a%20name=value%20pair", "=%", "a%2520name%3Dvalue%2520pair"}); + + return data.stream(); + } + + @ParameterizedTest + @MethodSource(value = "encodeSpecific") + public void testEncodeSpecific(String raw, String chars, String expected) + { + assertThat(URIUtil.encodeSpecific(raw, chars), is(expected)); + } + + public static Stream decodeSpecific() + { + List data = new ArrayList<>(); + + // [raw, chars, expected] + + // null input + data.add(new String[]{null, null, null}); + + // null chars + data.add(new String[]{"abc", null, "abc"}); + + // empty chars + data.add(new String[]{"abc", "", "abc"}); + + // no matches + data.add(new String[]{"abc", ".;", "abc"}); + data.add(new String[]{"xyz", ".;", "xyz"}); + data.add(new String[]{":::", ".;", ":::"}); + + // matches + data.add(new String[]{"a%20c", " ", "a c"}); + data.add(new String[]{"name%3Dvalue", "=", "name=value"}); + data.add(new String[]{"This has fewer then 10%25 hits%2E", ".%", "This has fewer then 10% hits."}); + + // partially decode + data.add(new String[]{"a%20name%3Dvalue%20pair", "=", "a%20name=value%20pair"}); + data.add(new String[]{"a%2520name%3Dvalue%2520pair", "=%", "a%20name=value%20pair"}); + + return data.stream(); + } + + @ParameterizedTest + @MethodSource(value = "decodeSpecific") + public void testDecodeSpecific(String raw, String chars, String expected) + { + assertThat(URIUtil.decodeSpecific(raw, chars), is(expected)); + } } diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java index 1ca05559cc9..db395405695 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/ClasspathPattern.java @@ -41,6 +41,7 @@ import java.util.function.Supplier; import org.eclipse.jetty.io.RuntimeIOException; import org.eclipse.jetty.util.ArrayTernaryTrie; import org.eclipse.jetty.util.IncludeExcludeSet; +import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; @@ -703,7 +704,7 @@ public class ClasspathPattern extends AbstractSet name=name.substring(0,name.length()-6); // Treat path elements as packages for name matching - name = name.replace('/', '.'); + name = StringUtil.replace(name, '/', '.'); return combine(_packageOrNamePatterns, name, _locations, ()-> { diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java index 967b9e5feaa..18231f5650a 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebAppClassLoader.java @@ -41,7 +41,8 @@ import java.util.concurrent.CopyOnWriteArrayList; import org.eclipse.jetty.util.ClassVisibilityChecker; import org.eclipse.jetty.util.IO; -import org.eclipse.jetty.util.StringUtil; +import org.eclipse.jetty.util.TypeUtil; +import org.eclipse.jetty.util.URIUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; @@ -338,12 +339,10 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility if(LOG.isDebugEnabled()) LOG.debug("addJar - {}", fn); String fnlc=fn.getName().toLowerCase(Locale.ENGLISH); - // don't check if this is a directory, see Bug 353165 + // don't check if this is a directory (prevents use of symlinks), see Bug 353165 if (isFileSupported(fnlc)) { - String jar=fn.toString(); - jar=StringUtil.replace(jar, ",", "%2C"); - jar=StringUtil.replace(jar, ";", "%3B"); + String jar = URIUtil.encodeSpecific(fn.toString(), ",;"); addClassPath(jar); } } @@ -630,7 +629,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility // Look in the webapp classloader as a resource, to avoid // loading a system class. Class webapp_class = null; - String path = name.replace('.', '/').concat(".class"); + String path = TypeUtil.toClassReference(name); URL webapp_url = findResource(path); if (webapp_url!=null && (!checkSystemResource || !_context.isSystemResource(name,webapp_url))) @@ -656,7 +655,7 @@ public class WebAppClassLoader extends URLClassLoader implements ClassVisibility return super.findClass(name); } - String path = name.replace('.', '/').concat(".class"); + String path = TypeUtil.toClassReference(name); URL url = findResource(path); if (url==null) throw new ClassNotFoundException(name); 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 20aec0b824d..d7339989fb1 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 @@ -211,8 +211,7 @@ public class WebInfConfiguration extends AbstractConfiguration } catch (URISyntaxException e) { - String fixedUriStr = StringUtil.replace(u.toString(), " ", "%20"); - containerUris.add(new URI(fixedUriStr)); + containerUris.add(new URI(URIUtil.encodeSpaces(u.toString()))); } } } @@ -813,10 +812,7 @@ public class WebInfConfiguration extends AbstractConfiguration } //Context name - String contextPath = context.getContextPath(); - contextPath=contextPath.replace('/','_'); - contextPath=contextPath.replace('\\','_'); - canonicalName.append(contextPath); + canonicalName.append(context.getContextPath()); //Virtual host (if there is one) canonicalName.append("-"); @@ -826,17 +822,9 @@ public class WebInfConfiguration extends AbstractConfiguration else canonicalName.append(vhosts[0]); - // sanitize - for (int i=0;i clazz) throws Exception { ClassLoader cl = Thread.currentThread().getContextClassLoader(); - String endpointPath = clazz.getName().replace('.','/') + ".class"; + String endpointPath = TypeUtil.toClassReference(clazz); URL classUrl = cl.getResource(endpointPath); assertThat("Class URL for: " + clazz,classUrl,notNullValue()); File destFile = Paths.get( classesDir.toString(), FS.separators( endpointPath)).toFile(); diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WSServer.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WSServer.java index 3bdc4bf257a..f5627707216 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WSServer.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WSServer.java @@ -37,6 +37,7 @@ import org.eclipse.jetty.toolchain.test.IO; import org.eclipse.jetty.toolchain.test.JAR; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.jupiter.WorkDir; +import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; @@ -82,7 +83,7 @@ public class WSServer public void copyClass(Class clazz) throws Exception { ClassLoader cl = Thread.currentThread().getContextClassLoader(); - String endpointPath = clazz.getName().replace('.','/') + ".class"; + String endpointPath = TypeUtil.toClassReference(clazz); URL classUrl = cl.getResource(endpointPath); assertThat("Class URL for: " + clazz,classUrl,notNullValue()); File destFile = new File(classesDir,FS.separators(endpointPath)); diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java index a612e6592e7..a59f93d7a72 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/support/TestableJettyServer.java @@ -18,18 +18,16 @@ package org.eclipse.jetty.test.support; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertTrue; - import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.net.UnknownHostException; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -39,9 +37,14 @@ import java.util.Properties; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.xml.XmlConfiguration; import org.junit.jupiter.api.Disabled; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + /** * Allows for setting up a Jetty server for testing based on XML configuration files. */ @@ -55,8 +58,8 @@ public class TestableJettyServer private String _scheme = HttpScheme.HTTP.asString(); /* Popular Directories */ - private File baseDir; - private File testResourcesDir; + private Path baseDir; + private Path testResourcesDir; public TestableJettyServer() throws IOException { @@ -64,31 +67,28 @@ public class TestableJettyServer Properties properties = new Properties(); /* Establish Popular Directories */ - String baseDirPath = System.getProperty("basedir"); - if (baseDirPath == null) - { - baseDirPath = System.getProperty("user.dir","."); - } - baseDir = new File(baseDirPath); - properties.setProperty("test.basedir",baseDir.getAbsolutePath()); + baseDir = MavenTestingUtils.getBasePath(); + properties.setProperty("test.basedir",baseDir.toString()); - testResourcesDir = new File(baseDirPath,"src/test/resources".replace('/',File.separatorChar)); - properties.setProperty("test.resourcesdir",testResourcesDir.getAbsolutePath()); + testResourcesDir = MavenTestingUtils.getTestResourcesPath(); + properties.setProperty("test.resourcesdir",testResourcesDir.toString()); - File testDocRoot = new File(testResourcesDir,"docroots"); - properties.setProperty("test.docroot.base",testDocRoot.getAbsolutePath()); + Path testDocRoot = MavenTestingUtils.getTestResourcePathDir("docroots"); + properties.setProperty("test.docroot.base",testDocRoot.toString()); - File targetDir = new File(baseDir,"target"); - properties.setProperty("test.targetdir",targetDir.getAbsolutePath()); + Path targetDir = MavenTestingUtils.getTargetPath(); + properties.setProperty("test.targetdir",targetDir.toString()); - File webappsDir = new File(targetDir,"webapps"); - properties.setProperty("test.webapps",webappsDir.getAbsolutePath()); + Path webappsDir = MavenTestingUtils.getTargetPath("webapps"); + properties.setProperty("test.webapps",webappsDir.toString()); // Write out configuration for use by ConfigurationManager. - File testConfig = new File(targetDir,"testable-jetty-server-config.properties"); - FileOutputStream out = new FileOutputStream(testConfig); - properties.store(out,"Generated by " + TestableJettyServer.class.getName()); - + Path testConfig = targetDir.resolve("testable-jetty-server-config.properties"); + try(OutputStream out = Files.newOutputStream(testConfig)) + { + properties.store(out,"Generated by " + TestableJettyServer.class.getName()); + } + for (Object key:properties.keySet()) _properties.put(String.valueOf(key),String.valueOf(properties.get(key))); } @@ -105,7 +105,7 @@ public class TestableJettyServer public void addConfiguration(String testConfigName) throws MalformedURLException { - addConfiguration(new File(testResourcesDir,testConfigName)); + addConfiguration(MavenTestingUtils.getTestResourceFile(testConfigName)); } public void setProperty(String key, String value) diff --git a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/ClassLoaderServlet.java b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/ClassLoaderServlet.java index ad348ff2a0d..59f1d8eed90 100644 --- a/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/ClassLoaderServlet.java +++ b/tests/test-webapps/test-servlet-spec/test-spec-webapp/src/main/java/com/acme/test/ClassLoaderServlet.java @@ -24,7 +24,6 @@ import java.net.URISyntaxException; import java.net.URL; import java.security.CodeSource; import java.security.ProtectionDomain; - import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; From adb6e7c8d2702d52a4ce0af34aec51dead0b88c9 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 12 Jun 2019 10:39:05 -0500 Subject: [PATCH 4/4] Issue #3708 - Correcting for testcases Signed-off-by: Joakim Erdfelt --- .../jetty/annotations/AnnotationParser.java | 17 ++++++++--------- .../jetty/plus/jndi/NamingEntryUtil.java | 4 ++-- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java index 5b9da0b87a3..786d7c468c6 100644 --- a/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java +++ b/jetty-annotations/src/main/java/org/eclipse/jetty/annotations/AnnotationParser.java @@ -601,13 +601,12 @@ public class AnnotationParser if (className == null) return; - String tmp = className; - className = TypeUtil.toClassReference(className); - URL resource = Loader.getResource(className); + String classRef = TypeUtil.toClassReference(className); + URL resource = Loader.getResource(classRef); if (resource!= null) { Resource r = Resource.newResource(resource); - addParsedClass(tmp, r); + addParsedClass(className, r); try (InputStream is = r.getInputStream()) { scanClass(handlers, null, is); @@ -673,16 +672,16 @@ public class AnnotationParser { MultiException me = new MultiException(); - for (String s:classNames) + for (String className : classNames) { try { - String name = TypeUtil.toClassReference(s); - URL resource = Loader.getResource(s); + String classRef = TypeUtil.toClassReference(className); + URL resource = Loader.getResource(classRef); if (resource!= null) { Resource r = Resource.newResource(resource); - addParsedClass(name, r); + addParsedClass(className, r); try (InputStream is = r.getInputStream()) { scanClass(handlers, null, is); @@ -691,7 +690,7 @@ public class AnnotationParser } catch (Exception e) { - me.add(new RuntimeException("Error scanning class "+s, e)); + me.add(new RuntimeException("Error scanning class "+className, e)); } } me.ifExceptionThrow(); diff --git a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntryUtil.java b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntryUtil.java index 956488be3a0..e005d4337b2 100644 --- a/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntryUtil.java +++ b/jetty-plus/src/main/java/org/eclipse/jetty/plus/jndi/NamingEntryUtil.java @@ -240,8 +240,8 @@ public class NamingEntryUtil return ""; String str = scope.getClass().getName() + "@" + Long.toHexString(scope.hashCode()); - // TODO: Is this sanitize step still needed? (this canonicalizeScope method has been reduced in functionality a lot over the years) - str = StringUtil.sanitizeFileSystemName(str); + str = StringUtil.replace(str, '/', '_'); + str = StringUtil.replace(str, ' ', '_'); return str; } }