Resolved issue: NETTY-102: Allow DelimiterBasedFrameDecoder to include the delimiter in the frame.

* Added stripDelimiter option
* Much more efficient and accurate handling of discarding the frame that exceeds maxFrameLength
This commit is contained in:
Trustin Lee 2009-01-06 05:11:55 +00:00
parent c460c90dea
commit 7ea187df89

View File

@ -74,6 +74,9 @@ public class DelimiterBasedFrameDecoder extends FrameDecoder {
private final ChannelBuffer[] delimiters; private final ChannelBuffer[] delimiters;
private final int maxFrameLength; private final int maxFrameLength;
private final boolean stripDelimiter;
private volatile boolean discardingTooLongFrame;
private volatile long tooLongFrameLength;
/** /**
* Creates a new instance. * Creates a new instance.
@ -84,6 +87,21 @@ public class DelimiterBasedFrameDecoder extends FrameDecoder {
* @param delimiter the delimiter * @param delimiter the delimiter
*/ */
public DelimiterBasedFrameDecoder(int maxFrameLength, ChannelBuffer delimiter) { public DelimiterBasedFrameDecoder(int maxFrameLength, ChannelBuffer delimiter) {
this(maxFrameLength, true, delimiter);
}
/**
* Creates a new instance.
*
* @param maxFrameLength the maximum length of the decoded frame.
* A {@link TooLongFrameException} is thrown if
* the length of the frame exceeds this value.
* @param stripDelimiter whether the decoded frame should strip out the
* delimiter or not
* @param delimiter the delimiter
*/
public DelimiterBasedFrameDecoder(
int maxFrameLength, boolean stripDelimiter, ChannelBuffer delimiter) {
validateMaxFrameLength(maxFrameLength); validateMaxFrameLength(maxFrameLength);
validateDelimiter(delimiter); validateDelimiter(delimiter);
delimiters = new ChannelBuffer[] { delimiters = new ChannelBuffer[] {
@ -91,6 +109,7 @@ public class DelimiterBasedFrameDecoder extends FrameDecoder {
delimiter.readerIndex(), delimiter.readableBytes()) delimiter.readerIndex(), delimiter.readableBytes())
}; };
this.maxFrameLength = maxFrameLength; this.maxFrameLength = maxFrameLength;
this.stripDelimiter = stripDelimiter;
} }
/** /**
@ -102,6 +121,21 @@ public class DelimiterBasedFrameDecoder extends FrameDecoder {
* @param delimiters the delimiters * @param delimiters the delimiters
*/ */
public DelimiterBasedFrameDecoder(int maxFrameLength, ChannelBuffer... delimiters) { public DelimiterBasedFrameDecoder(int maxFrameLength, ChannelBuffer... delimiters) {
this(maxFrameLength, true, delimiters);
}
/**
* Creates a new instance.
*
* @param maxFrameLength the maximum length of the decoded frame.
* A {@link TooLongFrameException} is thrown if
* the length of the frame exceeds this value.
* @param stripDelimiter whether the decoded frame should strip out the
* delimiter or not
* @param delimiters the delimiters
*/
public DelimiterBasedFrameDecoder(
int maxFrameLength, boolean stripDelimiter, ChannelBuffer... delimiters) {
validateMaxFrameLength(maxFrameLength); validateMaxFrameLength(maxFrameLength);
if (delimiters == null) { if (delimiters == null) {
throw new NullPointerException("delimiters"); throw new NullPointerException("delimiters");
@ -116,42 +150,73 @@ public class DelimiterBasedFrameDecoder extends FrameDecoder {
this.delimiters[i] = d.slice(d.readerIndex(), d.readableBytes()); this.delimiters[i] = d.slice(d.readerIndex(), d.readableBytes());
} }
this.maxFrameLength = maxFrameLength; this.maxFrameLength = maxFrameLength;
this.stripDelimiter = stripDelimiter;
} }
@Override @Override
protected Object decode( protected Object decode(
ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception { ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer) throws Exception {
// Try all delimiters and choose the delimiter which yields the shortest frame. // Try all delimiters and choose the delimiter which yields the shortest frame.
int minDelimIndex = Integer.MAX_VALUE; int minFrameLength = Integer.MAX_VALUE;
ChannelBuffer minDelim = null; ChannelBuffer minDelim = null;
for (ChannelBuffer delim: delimiters) { for (ChannelBuffer delim: delimiters) {
int delimIndex = indexOf(buffer, delim); int frameLength = indexOf(buffer, delim);
if (delimIndex >= 0 && delimIndex < minDelimIndex) { if (frameLength >= 0 && frameLength < minFrameLength) {
minDelimIndex = delimIndex; minFrameLength = frameLength;
minDelim = delim; minDelim = delim;
} }
} }
if (minDelim != null) { if (minDelim != null) {
ChannelBuffer frame = buffer.readBytes(minDelimIndex); int minDelimLength = minDelim.capacity();
if (frame.readableBytes() > maxFrameLength) { ChannelBuffer frame;
fail(frame.readableBytes());
}
buffer.skipBytes(minDelim.capacity());
return frame;
}
if (buffer.readableBytes() > maxFrameLength) { if (discardingTooLongFrame) {
fail(buffer.readableBytes()); // We've just finished discarding a very large frame.
// Throw an exception and go back to the initial state.
long tooLongFrameLength = this.tooLongFrameLength;
this.tooLongFrameLength = 0L;
discardingTooLongFrame = false;
buffer.skipBytes(minFrameLength + minDelimLength);
fail(tooLongFrameLength + minFrameLength + minDelimLength);
}
if (minFrameLength > maxFrameLength) {
// Discard read frame.
buffer.skipBytes(minFrameLength + minDelimLength);
fail(minFrameLength);
}
if (stripDelimiter) {
frame = buffer.readBytes(minFrameLength);
buffer.skipBytes(minDelimLength);
} else {
frame = buffer.readBytes(minFrameLength + minDelimLength);
}
return frame;
} else {
if (buffer.readableBytes() > maxFrameLength) {
// Discard the content of the buffer until a delimiter is found.
tooLongFrameLength = buffer.readableBytes();
buffer.skipBytes(buffer.readableBytes());
discardingTooLongFrame = true;
}
return null;
} }
return null;
} }
private void fail(int frameLength) throws TooLongFrameException { private void fail(long frameLength) throws TooLongFrameException {
throw new TooLongFrameException( throw new TooLongFrameException(
"The frame length exceeds " + maxFrameLength + ": " + frameLength); "The frame length exceeds " + maxFrameLength + ": " + frameLength);
} }
/**
* Returns the number of bytes between the readerIndex of the haystack and
* the first needle found in the haystack. -1 is returned if no needle is
* found in the haystack.
*/
private static int indexOf(ChannelBuffer haystack, ChannelBuffer needle) { private static int indexOf(ChannelBuffer haystack, ChannelBuffer needle) {
for (int i = haystack.readerIndex(); i < haystack.writerIndex(); i ++) { for (int i = haystack.readerIndex(); i < haystack.writerIndex(); i ++) {
int haystackIndex = i; int haystackIndex = i;