Replace dynamic regular expressions with precompiled Patterns or new StringUtil.split()

This commit is contained in:
Trustin Lee 2012-11-10 00:41:22 +09:00
parent 8c0e5626c2
commit e21dc5925d
12 changed files with 127 additions and 33 deletions

View File

@ -15,6 +15,8 @@
*/
package io.netty.handler.codec.http;
import io.netty.util.internal.StringUtil;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Collections;
@ -40,7 +42,7 @@ import java.util.TreeSet;
*/
public final class CookieDecoder {
private static final String COMMA = ",";
private static final char COMMA = ',';
/**
* Decodes the specified HTTP header value into {@link Cookie}s.
@ -131,7 +133,7 @@ public final class CookieDecoder {
} else if (CookieHeaderNames.VERSION.equalsIgnoreCase(name)) {
version = Integer.parseInt(value);
} else if (CookieHeaderNames.PORT.equalsIgnoreCase(name)) {
String[] portList = value.split(COMMA);
String[] portList = StringUtil.split(value, COMMA);
for (String s1: portList) {
try {
ports.add(Integer.valueOf(s1));

View File

@ -16,6 +16,7 @@
package io.netty.handler.codec.http;
import java.util.List;
import java.util.regex.Pattern;
/**
* A utility class mainly for use with HTTP codec classes

View File

@ -18,6 +18,7 @@ package io.netty.handler.codec.http;
import io.netty.channel.embedded.EmbeddedByteChannel;
import io.netty.handler.codec.compression.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper;
import io.netty.util.internal.StringUtil;
/**
* Compresses an {@link HttpMessage} and an {@link HttpChunk} in {@code gzip} or
@ -126,7 +127,7 @@ public class HttpContentCompressor extends HttpContentEncoder {
float starQ = -1.0f;
float gzipQ = -1.0f;
float deflateQ = -1.0f;
for (String encoding : acceptEncoding.split(",")) {
for (String encoding: StringUtil.split(acceptEncoding, ',')) {
float q = 1.0f;
int equalsPos = encoding.indexOf('=');
if (equalsPos != -1) {

View File

@ -15,15 +15,6 @@
*/
package io.netty.handler.codec.http.multipart;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.http.HttpChunk;
import io.netty.handler.codec.http.HttpConstants;
@ -33,6 +24,17 @@ import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadNoBackArrayException;
import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.SeekAheadOptimize;
import io.netty.handler.codec.http.multipart.HttpPostBodyUtil.TransferEncodingMechanism;
import io.netty.util.internal.StringUtil;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import static io.netty.buffer.Unpooled.*;
/**
@ -254,7 +256,7 @@ public class HttpPostRequestDecoder {
String[] headerContentType = splitHeaderContentType(contentType);
if (headerContentType[0].toLowerCase().startsWith(HttpHeaders.Values.MULTIPART_FORM_DATA)
&& headerContentType[1].toLowerCase().startsWith(HttpHeaders.Values.BOUNDARY)) {
String[] boundary = headerContentType[1].split("=");
String[] boundary = StringUtil.split(headerContentType[1], '=');
if (boundary.length != 2) {
throw new ErrorDataDecoderException("Needs a boundary value");
}
@ -955,7 +957,7 @@ public class HttpPostRequestDecoder {
if (checkSecondArg) {
// read next values and store them in the map as Attribute
for (int i = 2; i < contents.length; i++) {
String[] values = contents[i].split("=");
String[] values = StringUtil.split(contents[i], '=');
Attribute attribute;
try {
attribute = factory.createAttribute(request, values[0].trim(),
@ -994,7 +996,7 @@ public class HttpPostRequestDecoder {
// Take care of possible "multipart/mixed"
if (contents[1].equalsIgnoreCase(HttpPostBodyUtil.MULTIPART_MIXED)) {
if (currentStatus == MultiPartStatus.DISPOSITION) {
String[] values = contents[2].split("=");
String[] values = StringUtil.split(contents[2], '=');
multipartMixedBoundary = "--" + values[1];
currentStatus = MultiPartStatus.MIXEDDELIMITER;
return decodeMultipart(MultiPartStatus.MIXEDDELIMITER);
@ -1004,7 +1006,7 @@ public class HttpPostRequestDecoder {
} else {
for (int i = 1; i < contents.length; i++) {
if (contents[i].toLowerCase().startsWith(HttpHeaders.Values.CHARSET)) {
String[] values = contents[i].split("=");
String[] values = StringUtil.split(contents[i], '=');
Attribute attribute;
try {
attribute = factory.createAttribute(request, HttpHeaders.Values.CHARSET,

View File

@ -18,6 +18,7 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.util.internal.StringUtil;
import java.util.Collections;
import java.util.LinkedHashSet;
@ -57,7 +58,7 @@ public abstract class WebSocketServerHandshaker {
this.version = version;
this.webSocketUrl = webSocketUrl;
if (subprotocols != null) {
String[] subprotocolArray = subprotocols.split(",");
String[] subprotocolArray = StringUtil.split(subprotocols, ',');
for (int i = 0; i < subprotocolArray.length; i++) {
subprotocolArray[i] = subprotocolArray[i].trim();
}
@ -132,7 +133,7 @@ public abstract class WebSocketServerHandshaker {
return null;
}
String[] requestedSubprotocolArray = requestedSubprotocols.split(",");
String[] requestedSubprotocolArray = StringUtil.split(requestedSubprotocols, ',');
for (String p: requestedSubprotocolArray) {
String requestedSubprotocol = p.trim();

View File

@ -23,6 +23,7 @@ import java.security.PrivilegedExceptionAction;
import java.util.Locale;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Pattern;
/**
@ -41,6 +42,8 @@ public final class DetectionUtil {
private static final boolean IS_ROOT;
static {
Pattern PERMISSION_DENIED = Pattern.compile(".*permission.*denied.*");
String os = SystemPropertyUtil.get("os.name", "").toLowerCase(Locale.UK);
// windows
IS_WINDOWS = os.contains("win");
@ -63,7 +66,7 @@ public final class DetectionUtil {
message = "";
}
message = message.toLowerCase();
if (message.matches(".*permission.*denied.*")) {
if (PERMISSION_DENIED.matcher(message).matches()) {
break;
}
} finally {

View File

@ -15,7 +15,9 @@
*/
package io.netty.util.internal;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;
/**
* String utility class.
@ -40,4 +42,47 @@ public final class StringUtil {
NEWLINE = newLine;
}
private static final String EMPTY_STRING = "";
/**
* Splits the specified {@link String} with the specified delimiter. This operation is a simplified and optimized
* version of {@link String#split(String)}.
*/
public static String[] split(String value, char delim) {
final int end = value.length();
final List<String> res = new ArrayList<String>();
int start = 0;
for (int i = 0; i < end; i ++) {
if (value.charAt(i) == delim) {
if (start == i) {
res.add(EMPTY_STRING);
} else {
res.add(value.substring(start, i));
}
start = i + 1;
}
}
if (start == 0) { // If no delimiter was found in the value
res.add(value);
} else {
if (start != end) {
// Add the last element if it's not empty.
res.add(value.substring(start, end));
} else {
// Truncate trailing empty elements.
for (int i = res.size() - 1; i >= 0; i --) {
if (res.get(i).length() == 0) {
res.remove(i);
} else {
break;
}
}
}
}
return res.toArray(new String[res.size()]);
}
}

View File

@ -20,6 +20,7 @@ import io.netty.logging.InternalLoggerFactory;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
/**
* A collection of utility methods to retrieve and parse the values of the Java system properties.
@ -122,6 +123,8 @@ public final class SystemPropertyUtil {
return def;
}
private static final Pattern INTEGER_PATTERN = Pattern.compile("-?[0-9]+");
/**
* Returns the value of the Java system property with the specified
* {@code key}, while falling back to the specified default value if
@ -138,7 +141,7 @@ public final class SystemPropertyUtil {
}
value = value.trim().toLowerCase();
if (value.matches("-?[0-9]+")) {
if (INTEGER_PATTERN.matcher(value).matches()) {
try {
return Integer.parseInt(value);
} catch (Exception e) {
@ -169,7 +172,7 @@ public final class SystemPropertyUtil {
}
value = value.trim().toLowerCase();
if (value.matches("-?[0-9]+")) {
if (INTEGER_PATTERN.matcher(value).matches()) {
try {
return Long.parseLong(value);
} catch (Exception e) {

View File

@ -15,14 +15,39 @@
*/
package io.netty.util.internal;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
import static org.junit.Assert.*;
public class StringUtilTest {
@Test
public void ensureNewlineExists() {
assertNotNull(StringUtil.NEWLINE);
}
@Test
public void splitSimple() {
assertArrayEquals(new String[] { "foo", "bar" }, StringUtil.split("foo:bar", ':'));
}
@Test
public void splitWithTrailingDelimiter() {
assertArrayEquals(new String[] { "foo", "bar" }, StringUtil.split("foo,bar,", ','));
}
@Test
public void splitWithTrailingDelimiters() {
assertArrayEquals(new String[] { "foo", "bar" }, StringUtil.split("foo!bar!!", '!'));
}
@Test
public void splitWithConsecutiveDelimiters() {
assertArrayEquals(new String[] { "foo", "", "bar" }, StringUtil.split("foo$$bar", '$'));
}
@Test
public void splitWithDelimiterAtBeginning() {
assertArrayEquals(new String[] { "", "foo", "bar" }, StringUtil.split("#foo#bar", '#'));
}
}

View File

@ -15,11 +15,6 @@
*/
package io.netty.example.http.file;
import static io.netty.handler.codec.http.HttpHeaders.*;
import static io.netty.handler.codec.http.HttpHeaders.Names.*;
import static io.netty.handler.codec.http.HttpMethod.*;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.*;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
@ -33,6 +28,7 @@ import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.stream.ChunkedFile;
import io.netty.util.CharsetUtil;
import javax.activation.MimetypesFileTypeMap;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
@ -44,8 +40,13 @@ import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.TimeZone;
import java.util.regex.Pattern;
import javax.activation.MimetypesFileTypeMap;
import static io.netty.handler.codec.http.HttpHeaders.Names.*;
import static io.netty.handler.codec.http.HttpHeaders.*;
import static io.netty.handler.codec.http.HttpMethod.*;
import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.*;
/**
* A simple handler that serves incoming HTTP requests to send their respective
@ -194,6 +195,8 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
}
}
private static final Pattern INSECURE_URI = Pattern.compile(".*[<>&\"].*");
private static String sanitizeUri(String uri) {
// Decode the path.
try {
@ -218,7 +221,7 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
if (uri.contains(File.separator + ".") ||
uri.contains("." + File.separator) ||
uri.startsWith(".") || uri.endsWith(".") ||
uri.matches(".*[<>&\"].*")) {
INSECURE_URI.matcher(uri).matches()) {
return null;
}
@ -226,6 +229,8 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
return System.getProperty("user.dir") + File.separator + uri;
}
private static final Pattern ALLOWED_FILE_NAME = Pattern.compile("[A-Za-z0-9][-_A-Za-z0-9\\.]*");
private static void sendListing(ChannelHandlerContext ctx, File dir) {
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.OK);
response.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8");
@ -252,7 +257,7 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda
}
String name = f.getName();
if (!name.matches("[A-Za-z0-9][-_A-Za-z0-9\\.]*")) {
if (!ALLOWED_FILE_NAME.matcher(name).matches()) {
continue;
}

View File

@ -24,6 +24,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Pattern;
/**
* Sends a list of continent/city pairs to a {@link LocalTimeServer} to
@ -101,10 +102,12 @@ public class LocalTimeClient {
" localhost 8080 America/New_York Asia/Seoul");
}
private static final Pattern CITY_PATTERN = Pattern.compile("^[_A-Za-z]+/[_A-Za-z]+$");
private static List<String> parseCities(String[] args, int offset) {
List<String> cities = new ArrayList<String>();
for (int i = offset; i < args.length; i ++) {
if (!args[i].matches("^[_A-Za-z]+/[_A-Za-z]+$")) {
if (!CITY_PATTERN.matcher(args[i]).matches()) {
System.err.println("Syntax error: '" + args[i] + "'");
printUsage();
return null;

View File

@ -32,12 +32,15 @@ import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
public class LocalTimeClientHandler extends ChannelInboundMessageHandlerAdapter<LocalTimes> {
private static final Logger logger = Logger.getLogger(
LocalTimeClientHandler.class.getName());
private static final Pattern DELIM = Pattern.compile("/");
// Stateful properties
private volatile Channel channel;
private final BlockingQueue<LocalTimes> answer = new LinkedBlockingQueue<LocalTimes>();
@ -46,7 +49,7 @@ public class LocalTimeClientHandler extends ChannelInboundMessageHandlerAdapter<
Locations.Builder builder = Locations.newBuilder();
for (String c: cities) {
String[] components = c.split("/");
String[] components = DELIM.split(c);
builder.addLocation(Location.newBuilder().
setContinent(Continent.valueOf(components[0].toUpperCase())).
setCity(components[1]).build());