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 <joakim.erdfelt@gmail.com>
This commit is contained in:
Joakim Erdfelt 2019-06-11 11:25:50 -05:00
parent da4f116c63
commit 33fe55c339
24 changed files with 347 additions and 202 deletions

View File

@ -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++;
}

View File

@ -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());
}

View File

@ -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.
* <p>
@ -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

View File

@ -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());
}

View File

@ -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();
}
}

View File

@ -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
@ -70,22 +70,22 @@ public class MongoUtils
}
}
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)

View File

@ -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));

View File

@ -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());

View File

@ -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)

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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();

View File

@ -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;
}

View File

@ -164,7 +164,7 @@ public class DefaultHandler extends AbstractHandler
{
writer.append("<a href=\"").append(href).append("\">");
}
writer.append(contextPath.replaceAll("%", "&#37;"));
writer.append(StringUtil.replace(contextPath, "%", "&#37;"));
if (context.isRunning())
{
writer.append("</a>");

View File

@ -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));

View File

@ -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)

View File

@ -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
{
@ -63,7 +62,6 @@ public class StringUtil
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
@ -75,7 +73,6 @@ public class StringUtil
return (n==null)?s:n;
}
/* ------------------------------------------------------------ */
/** Convert alternate charset names (eg utf8) to normalized
* name (eg UTF-8).
* @param s the charset to normalize
@ -89,8 +86,6 @@ public class StringUtil
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.
*
* <p>
* This will replace all of the following characters
* with a "{@code _}" (underscore).
* </p>
* <ul>
* <li>Control Characters</li>
* <li>Anything not 7-bit printable ASCII</li>
* <li>Special characters "{@code !|/.,:&><\?*"}"</li>
* </ul>
*
* @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)
@ -175,12 +202,10 @@ public class StringUtil
return true;
}
/* ------------------------------------------------------------ */
public static boolean endsWithIgnoreCase(String s,String w)
{
if (w==null)
return true;
if (s==null)
return false;
@ -207,7 +232,6 @@ public class StringUtil
return true;
}
/* ------------------------------------------------------------ */
/**
* returns the next index of a character from the chars string
* @param s the input string to search
@ -222,9 +246,12 @@ public class StringUtil
return -1;
}
/* ------------------------------------------------------------ */
/**
* replace substrings within string.
* Replace substrings within string.
* <p>
* Fast replacement for {@code java.lang.String#}{@link String#replace(CharSequence, CharSequence)}
* </p>
*
* @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);
c = i + sub.length();
}
while ((i = s.indexOf(sub, c)) != -1);
if (c < s.length())
{
buf.append(s.substring(c));
}
return buf.toString();
}
if (c<s.length())
buf.append(s.substring(c,s.length()));
/**
* Replace first substrings within string.
* <p>
* Fast replacement for {@code java.lang.String#}{@link String#replaceFirst(String, String)}, but without
* Regex support.
* </p>
*
* @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
*
@ -326,7 +377,6 @@ public class StringUtil
}
}
/* ------------------------------------------------------------ */
/**
* Append 2 digits (zero padded) to the StringBuilder
*
@ -342,7 +392,6 @@ public class StringUtil
}
}
/* ------------------------------------------------------------ */
/** Return a non null string.
* @param s String
* @return The string passed in or empty string if it is null.
@ -354,7 +403,6 @@ public class StringUtil
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.
* <p>
@ -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.
* <p>
@ -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<String> 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
* <p>Handle <a href="https://www.ietf.org/rfc/rfc4180.txt">rfc4180</a>-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 (;i<html.length();i++)
{
char c=html.charAt(i);
switch(c)
{
case '&' :
@ -1020,13 +1038,11 @@ public class StringUtil
case '\'':
case '"':
break loop;
default:
if (Character.isISOControl(c) && !Character.isWhitespace(c))
break loop;
}
}
// No characters need sanitizing, so return original string
if (i==html.length())
return html;
@ -1039,7 +1055,6 @@ public class StringUtil
for (;i<html.length();i++)
{
char c=html.charAt(i);
switch(c)
{
case '&' :
@ -1057,7 +1072,6 @@ public class StringUtil
case '"':
out.append("&quot;");
break;
default:
if (Character.isISOControl(c) && !Character.isWhitespace(c))
out.append('?');
@ -1068,7 +1082,6 @@ public class StringUtil
return out.toString();
}
/* ------------------------------------------------------------ */
/** The String value of an Object
* <p>This method calls {@link String#valueOf(Object)} unless the object is null,
* in which case null is returned</p>

View File

@ -269,6 +269,18 @@ 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

View File

@ -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");

View File

@ -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<String[]> replaceFirstArgs() {
List<String[]> 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

View File

@ -703,7 +703,7 @@ public class ClasspathPattern extends AbstractSet<String>
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, ()->
{

View File

@ -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));
}
}
}

View File

@ -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("&", "&amp;");
string=string.replace( "<", "&lt;");
string=string.replace( ">", "&gt;");
string = string.replace("&", "&amp;");
string = string.replace( "<", "&lt;");
string = string.replace( ">", "&gt;");
return string;
}

View File

@ -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 = "&#8203;";
boolean fixed;
Timer _timer;
@ -647,7 +647,7 @@ public class Dump extends HttpServlet
{
name= (String)a.nextElement();
pout.write("</tr><tr>\n");
pout.write("<th align=\"right\" valign=\"top\">"+name.replace("."," .")+":&nbsp;</th>");
pout.write("<th align=\"right\" valign=\"top\">" + name.replace(".", ZWSP + ".") + ":&nbsp;</th>");
Object value=request.getAttribute(name);
if (value instanceof File)
{
@ -678,7 +678,7 @@ public class Dump extends HttpServlet
{
name= (String)a.nextElement();
pout.write("</tr><tr>\n");
pout.write("<th align=\"right\" valign=\"top\">"+name.replace("."," .")+":&nbsp;</th>");
pout.write("<th align=\"right\" valign=\"top\">" + name.replace(".", ZWSP + ".") + ":&nbsp;</th>");
pout.write("<td>"+ toString(getServletContext().getInitParameter(name)) + "</td>");
}
@ -689,7 +689,7 @@ public class Dump extends HttpServlet
{
name= (String)a.nextElement();
pout.write("</tr><tr>\n");
pout.write("<th align=\"right\" valign=\"top\">"+name.replace("."," .")+":&nbsp;</th>");
pout.write("<th align=\"right\" valign=\"top\">" + name.replace(".", ZWSP + ".") + ":&nbsp;</th>");
pout.write("<td>"+"<pre>" + toString(getServletContext().getAttribute(name)) + "</pre>"+"</td>");
}
@ -1055,9 +1055,9 @@ public class Dump extends HttpServlet
{
if (s==null)
return "null";
s=s.replaceAll("&","&amp;");
s=s.replaceAll("<","&lt;");
s=s.replaceAll(">","&gt;");
s = s.replace("&","&amp;");
s = s.replace("<","&lt;");
s = s.replace(">","&gt;");
return s;
}
}