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).
+ *
+ *
+ * - Control Characters
+ * - Anything not 7-bit printable ASCII
+ * - Special characters "{@code !|/.,:&><\?*"}"
+ *
+ *
+ * @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;
}
}