Removing a SeekAheadNoBackArrayException to avoid exception handling

Motivation:

A `SeekAheadNoBackArrayException` used as check for `ByteBuf#hasArray`. The catch of exceptions carries a large overhead on stack trace filling, and this should be avoided.

Modifications:

- Remove the class `SeekAheadNoBackArrayException` and replace its usage with `if` statements.
- Use methods from `ObjectUtils` for better readability.
- Make private methods static where it make sense.
- Remove unused private methods.

Result:

Less of exception handling logic, better performance.
This commit is contained in:
Nikolay Fedorovskikh 2017-06-07 01:41:33 +05:00 committed by Scott Mitchell
parent e06cb82c4c
commit b03b0f22d1
3 changed files with 51 additions and 151 deletions

View File

@ -62,10 +62,6 @@ final class HttpPostBodyUtil {
this.value = value;
}
TransferEncodingMechanism() {
value = name();
}
public String value() {
return value;
}
@ -79,13 +75,6 @@ final class HttpPostBodyUtil {
private HttpPostBodyUtil() {
}
/**
* Exception when NO Backend Array is found
*/
static class SeekAheadNoBackArrayException extends Exception {
private static final long serialVersionUID = -630418804938699495L;
}
/**
* This class intends to decrease the CPU in seeking ahead some bytes in
* HttpPostRequestDecoder
@ -98,9 +87,12 @@ final class HttpPostBodyUtil {
int limit;
ByteBuf buffer;
SeekAheadOptimize(ByteBuf buffer) throws SeekAheadNoBackArrayException {
/**
* @param buffer buffer with a backing byte array
*/
SeekAheadOptimize(ByteBuf buffer) {
if (!buffer.hasArray()) {
throw new SeekAheadNoBackArrayException();
throw new IllegalArgumentException("buffer hasn't backing byte array");
}
this.buffer = buffer;
bytes = buffer.array();
@ -128,14 +120,6 @@ final class HttpPostBodyUtil {
int getReadPosition(int index) {
return index - origPos + readerIndex;
}
void clear() {
buffer = null;
bytes = null;
limit = 0;
pos = 0;
readerIndex = 0;
}
}
/**
@ -152,20 +136,6 @@ final class HttpPostBodyUtil {
return result;
}
/**
* Find the first whitespace
* @return the rank of the first whitespace
*/
static int findWhitespace(String sb, int offset) {
int result;
for (result = offset; result < sb.length(); result ++) {
if (Character.isWhitespace(sb.charAt(result))) {
break;
}
}
return result;
}
/**
* Find the end of String
* @return the rank of the end of string

View File

@ -22,7 +22,6 @@ import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.LastHttpContent;
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.handler.codec.http.multipart.HttpPostRequestDecoder.EndOfDataDecoderException;
@ -42,6 +41,7 @@ import java.util.Map;
import java.util.TreeMap;
import static io.netty.buffer.Unpooled.*;
import static io.netty.util.internal.ObjectUtil.*;
/**
* This decoder will decode Body and can handle POST BODY.
@ -172,18 +172,9 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
* errors
*/
public HttpPostMultipartRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset) {
if (factory == null) {
throw new NullPointerException("factory");
}
if (request == null) {
throw new NullPointerException("request");
}
if (charset == null) {
throw new NullPointerException("charset");
}
this.request = request;
this.charset = charset;
this.factory = factory;
this.request = checkNotNull(request, "request");
this.charset = checkNotNull(charset, "charset");
this.factory = checkNotNull(factory, "factory");
// Fill default values
setMultipart(this.request.headers().get(HttpHeaderNames.CONTENT_TYPE));
@ -238,10 +229,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
*/
@Override
public void setDiscardThreshold(int discardThreshold) {
if (discardThreshold < 0) {
throw new IllegalArgumentException("discardThreshold must be >= 0");
}
this.discardThreshold = discardThreshold;
this.discardThreshold = checkPositiveOrZero(discardThreshold, "discardThreshold");
}
/**
@ -547,7 +535,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
}
// load data
try {
loadFieldMultipart(multipartDataBoundary);
loadFieldMultipart(undecodedChunk, multipartDataBoundary, currentAttribute);
} catch (NotEnoughDataDecoderException ignored) {
return null;
}
@ -589,19 +577,16 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
*
* @throws NotEnoughDataDecoderException
*/
void skipControlCharacters() {
SeekAheadOptimize sao;
try {
sao = new SeekAheadOptimize(undecodedChunk);
} catch (SeekAheadNoBackArrayException ignored) {
private static void skipControlCharacters(ByteBuf undecodedChunk) {
if (!undecodedChunk.hasArray()) {
try {
skipControlCharactersStandard();
skipControlCharactersStandard(undecodedChunk);
} catch (IndexOutOfBoundsException e1) {
throw new NotEnoughDataDecoderException(e1);
}
return;
}
SeekAheadOptimize sao = new SeekAheadOptimize(undecodedChunk);
while (sao.pos < sao.limit) {
char c = (char) (sao.bytes[sao.pos++] & 0xFF);
if (!Character.isISOControl(c) && !Character.isWhitespace(c)) {
@ -612,7 +597,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
throw new NotEnoughDataDecoderException("Access out of bounds");
}
void skipControlCharactersStandard() {
private static void skipControlCharactersStandard(ByteBuf undecodedChunk) {
for (;;) {
char c = (char) undecodedChunk.readUnsignedByte();
if (!Character.isISOControl(c) && !Character.isWhitespace(c)) {
@ -639,7 +624,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
// --AaB03x or --AaB03x--
int readerIndex = undecodedChunk.readerIndex();
try {
skipControlCharacters();
skipControlCharacters(undecodedChunk);
} catch (NotEnoughDataDecoderException ignored) {
undecodedChunk.readerIndex(readerIndex);
return null;
@ -647,7 +632,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
skipOneLine();
String newline;
try {
newline = readDelimiter(delimiter);
newline = readDelimiter(undecodedChunk, delimiter);
} catch (NotEnoughDataDecoderException ignored) {
undecodedChunk.readerIndex(readerIndex);
return null;
@ -686,8 +671,8 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
while (!skipOneLine()) {
String newline;
try {
skipControlCharacters();
newline = readLine();
skipControlCharacters(undecodedChunk);
newline = readLine(undecodedChunk, charset);
} catch (NotEnoughDataDecoderException ignored) {
undecodedChunk.readerIndex(readerIndex);
return null;
@ -899,7 +884,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
}
// load data as much as possible
try {
readFileUploadByteMultipart(delimiter);
readFileUploadByteMultipart(undecodedChunk, delimiter, currentFileUpload);
} catch (NotEnoughDataDecoderException e) {
// do not change the buffer position
// since some can be already saved into FileUpload
@ -986,7 +971,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
* Need more chunks and reset the {@code readerIndex} to the previous
* value
*/
private String readLineStandard() {
private static String readLineStandard(ByteBuf undecodedChunk, Charset charset) {
int readerIndex = undecodedChunk.readerIndex();
try {
ByteBuf line = buffer(64);
@ -1026,13 +1011,11 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
* Need more chunks and reset the {@code readerIndex} to the previous
* value
*/
private String readLine() {
SeekAheadOptimize sao;
try {
sao = new SeekAheadOptimize(undecodedChunk);
} catch (SeekAheadNoBackArrayException ignored) {
return readLineStandard();
private static String readLine(ByteBuf undecodedChunk, Charset charset) {
if (!undecodedChunk.hasArray()) {
return readLineStandard(undecodedChunk, charset);
}
SeekAheadOptimize sao = new SeekAheadOptimize(undecodedChunk);
int readerIndex = undecodedChunk.readerIndex();
try {
ByteBuf line = buffer(64);
@ -1083,7 +1066,7 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
* Need more chunks and reset the {@code readerIndex} to the previous
* value
*/
private String readDelimiterStandard(String delimiter) {
private static String readDelimiterStandard(ByteBuf undecodedChunk, String delimiter) {
int readerIndex = undecodedChunk.readerIndex();
try {
StringBuilder sb = new StringBuilder(64);
@ -1177,13 +1160,11 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
* Need more chunks and reset the readerInder to the previous
* value
*/
private String readDelimiter(String delimiter) {
SeekAheadOptimize sao;
try {
sao = new SeekAheadOptimize(undecodedChunk);
} catch (SeekAheadNoBackArrayException ignored) {
return readDelimiterStandard(delimiter);
private static String readDelimiter(ByteBuf undecodedChunk, String delimiter) {
if (!undecodedChunk.hasArray()) {
return readDelimiterStandard(undecodedChunk, delimiter);
}
SeekAheadOptimize sao = new SeekAheadOptimize(undecodedChunk);
int readerIndex = undecodedChunk.readerIndex();
int delimiterPos = 0;
int len = delimiter.length();
@ -1299,7 +1280,8 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
* @throws ErrorDataDecoderException
* write IO error occurs with the FileUpload
*/
private void readFileUploadByteMultipartStandard(String delimiter) {
private static void readFileUploadByteMultipartStandard(ByteBuf undecodedChunk, String delimiter,
FileUpload currentFileUpload) {
int readerIndex = undecodedChunk.readerIndex();
// found the decoder limit
boolean newLine = true;
@ -1405,14 +1387,13 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
* @throws ErrorDataDecoderException
* write IO error occurs with the FileUpload
*/
private void readFileUploadByteMultipart(String delimiter) {
SeekAheadOptimize sao;
try {
sao = new SeekAheadOptimize(undecodedChunk);
} catch (SeekAheadNoBackArrayException ignored) {
readFileUploadByteMultipartStandard(delimiter);
private static void readFileUploadByteMultipart(ByteBuf undecodedChunk, String delimiter,
FileUpload currentFileUpload) {
if (!undecodedChunk.hasArray()) {
readFileUploadByteMultipartStandard(undecodedChunk, delimiter, currentFileUpload);
return;
}
SeekAheadOptimize sao = new SeekAheadOptimize(undecodedChunk);
int readerIndex = undecodedChunk.readerIndex();
// found the decoder limit
boolean newLine = true;
@ -1518,7 +1499,8 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
* Need more chunks
* @throws ErrorDataDecoderException
*/
private void loadFieldMultipartStandard(String delimiter) {
private static void loadFieldMultipartStandard(ByteBuf undecodedChunk, String delimiter,
Attribute currentAttribute) {
int readerIndex = undecodedChunk.readerIndex();
try {
// found the decoder limit
@ -1624,14 +1606,12 @@ public class HttpPostMultipartRequestDecoder implements InterfaceHttpPostRequest
* Need more chunks
* @throws ErrorDataDecoderException
*/
private void loadFieldMultipart(String delimiter) {
SeekAheadOptimize sao;
try {
sao = new SeekAheadOptimize(undecodedChunk);
} catch (SeekAheadNoBackArrayException ignored) {
loadFieldMultipartStandard(delimiter);
private static void loadFieldMultipart(ByteBuf undecodedChunk, String delimiter, Attribute currentAttribute) {
if (!undecodedChunk.hasArray()) {
loadFieldMultipartStandard(undecodedChunk, delimiter, currentAttribute);
return;
}
SeekAheadOptimize sao = new SeekAheadOptimize(undecodedChunk);
int readerIndex = undecodedChunk.readerIndex();
try {
// found the decoder limit

View File

@ -21,7 +21,6 @@ import io.netty.handler.codec.http.HttpContent;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.LastHttpContent;
import io.netty.handler.codec.http.QueryStringDecoder;
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.HttpPostRequestDecoder.EndOfDataDecoderException;
import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder.ErrorDataDecoderException;
@ -36,6 +35,7 @@ import java.util.Map;
import java.util.TreeMap;
import static io.netty.buffer.Unpooled.*;
import static io.netty.util.internal.ObjectUtil.*;
/**
* This decoder will decode Body and can handle POST BODY.
@ -145,18 +145,9 @@ public class HttpPostStandardRequestDecoder implements InterfaceHttpPostRequestD
* errors
*/
public HttpPostStandardRequestDecoder(HttpDataFactory factory, HttpRequest request, Charset charset) {
if (factory == null) {
throw new NullPointerException("factory");
}
if (request == null) {
throw new NullPointerException("request");
}
if (charset == null) {
throw new NullPointerException("charset");
}
this.request = request;
this.charset = charset;
this.factory = factory;
this.request = checkNotNull(request, "request");
this.charset = checkNotNull(charset, "charset");
this.factory = checkNotNull(factory, "factory");
if (request instanceof HttpContent) {
// Offer automatically if the given request is als type of HttpContent
// See #1089
@ -192,10 +183,7 @@ public class HttpPostStandardRequestDecoder implements InterfaceHttpPostRequestD
*/
@Override
public void setDiscardThreshold(int discardThreshold) {
if (discardThreshold < 0) {
throw new IllegalArgumentException("discardThreshold must be >= 0");
}
this.discardThreshold = discardThreshold;
this.discardThreshold = checkPositiveOrZero(discardThreshold, "discardThreshold");
}
/**
@ -513,13 +501,11 @@ public class HttpPostStandardRequestDecoder implements InterfaceHttpPostRequestD
* errors
*/
private void parseBodyAttributes() {
SeekAheadOptimize sao;
try {
sao = new SeekAheadOptimize(undecodedChunk);
} catch (SeekAheadNoBackArrayException ignored) {
if (!undecodedChunk.hasArray()) {
parseBodyAttributesStandard();
return;
}
SeekAheadOptimize sao = new SeekAheadOptimize(undecodedChunk);
int firstpos = undecodedChunk.readerIndex();
int currentpos = firstpos;
int equalpos;
@ -661,42 +647,6 @@ public class HttpPostStandardRequestDecoder implements InterfaceHttpPostRequestD
}
}
/**
* Skip control Characters
*/
void skipControlCharacters() {
SeekAheadOptimize sao;
try {
sao = new SeekAheadOptimize(undecodedChunk);
} catch (SeekAheadNoBackArrayException ignored) {
try {
skipControlCharactersStandard();
} catch (IndexOutOfBoundsException e) {
throw new NotEnoughDataDecoderException(e);
}
return;
}
while (sao.pos < sao.limit) {
char c = (char) (sao.bytes[sao.pos++] & 0xFF);
if (!Character.isISOControl(c) && !Character.isWhitespace(c)) {
sao.setReadPosition(1);
return;
}
}
throw new NotEnoughDataDecoderException("Access out of bounds");
}
void skipControlCharactersStandard() {
for (;;) {
char c = (char) undecodedChunk.readUnsignedByte();
if (!Character.isISOControl(c) && !Character.isWhitespace(c)) {
undecodedChunk.readerIndex(undecodedChunk.readerIndex() - 1);
break;
}
}
}
/**
* Destroy the {@link HttpPostStandardRequestDecoder} and release all it resources. After this method
* was called it is not possible to operate on it anymore.