зеркало из https://github.com/Azure/DotNetty.git
Merge pull request #105 from jeoffman/dev
Porting in DelimiterBasedFrameDecoder from Java (redo)
This commit is contained in:
Коммит
49308fb644
|
@ -137,7 +137,7 @@ namespace DotNetty.Codecs
|
|||
}
|
||||
}
|
||||
|
||||
protected abstract void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output);
|
||||
protected internal abstract void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output);
|
||||
|
||||
static IByteBuffer ExpandCumulation(IByteBufferAllocator allocator, IByteBuffer cumulation, int readable)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,291 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using DotNetty.Buffers;
|
||||
using DotNetty.Transport.Channels;
|
||||
|
||||
namespace DotNetty.Codecs
|
||||
{
|
||||
/// <summary>
|
||||
/// A decoder that splits the received <see cref="DotNetty.Buffers.IByteBuffer"/> by one or more
|
||||
/// delimiters.It is particularly useful for decoding the frames which ends
|
||||
/// with a delimiter such as <see cref = "DotNetty.Codecs.Delimiters.NullDelimiter" /> or
|
||||
/// <see cref = "DotNetty.Codecs.Delimiters.LineDelimiter" />
|
||||
/// --- Specifying more than one delimiter </ h3 >
|
||||
/// <see cref = "DotNetty.Codecs.Delimiters.NullDelimiter" /> allows you to specify more than one
|
||||
/// delimiter. If more than one delimiter is found in the buffer, it chooses
|
||||
/// the delimiter which produces the shortest frame. For example, if you have
|
||||
/// the following data in the buffer:
|
||||
/// +--------------+
|
||||
/// | ABC\nDEF\r\n |
|
||||
/// +--------------+
|
||||
/// a <see cref = "DotNetty.Codecs.Delimiters.LineDelimiter" /> will choose '\n' as the first delimiter and produce two frames:
|
||||
/// +-----+-----+
|
||||
/// | ABC | DEF |
|
||||
/// +-----+-----+
|
||||
/// rather than incorrectly choosing '\r\n' as the first delimiter:
|
||||
/// +----------+
|
||||
/// | ABC\nDEF |
|
||||
/// +----------+
|
||||
/// </summary>
|
||||
public class DelimiterBasedFrameDecoder : ByteToMessageDecoder
|
||||
{
|
||||
IByteBuffer[] delimiters;
|
||||
int maxFrameLength;
|
||||
bool stripDelimiter;
|
||||
bool failFast;
|
||||
bool discardingTooLongFrame;
|
||||
int tooLongFrameLength;
|
||||
LineBasedFrameDecoder lineBasedDecoder; // Set only when decoding with "\n" and "\r\n" as the delimiter.
|
||||
|
||||
/// <summary>Common constructor</summary>
|
||||
/// <param name="maxFrameLength">The maximum length of the decoded frame
|
||||
/// NOTE: A see <cref="DotNetty.Codecs.TooLongFrameException" /> is thrown if the length of the frame exceeds this value.</param>
|
||||
/// <param name="stripDelimiter">whether the decoded frame should strip out the delimiter or not</param>
|
||||
/// <param name="failFast">If true, a <cref="DotNetty.Codecs.TooLongFrameException" /> is
|
||||
/// thrown as soon as the decoder notices the length of the
|
||||
/// frame will exceed<tt>maxFrameLength</tt> regardless of
|
||||
/// whether the entire frame has been read.
|
||||
/// If false, a <cref="DotNetty.Codecs.TooLongFrameException" /> is
|
||||
/// thrown after the entire frame that exceeds maxFrameLength has been read.</param>
|
||||
/// <param name="delimiter">the delimiter</param>
|
||||
public DelimiterBasedFrameDecoder(int maxFrameLength, bool stripDelimiter, bool failFast, params IByteBuffer[] delimiters)
|
||||
{
|
||||
ValidateMaxFrameLength(maxFrameLength);
|
||||
if(delimiters == null)
|
||||
throw new NullReferenceException("delimiters");
|
||||
|
||||
if(delimiters.Length == 0)
|
||||
throw new ArgumentException("empty delimiters");
|
||||
|
||||
if(IsLineBased(delimiters) && !IsSubclass())
|
||||
{
|
||||
lineBasedDecoder = new LineBasedFrameDecoder(maxFrameLength, stripDelimiter, failFast);
|
||||
this.delimiters = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.delimiters = new IByteBuffer[delimiters.Length];
|
||||
for(int i = 0; i < delimiters.Length; i++)
|
||||
{
|
||||
IByteBuffer d = delimiters[i];
|
||||
ValidateDelimiter(d);
|
||||
this.delimiters[i] = d.Slice(d.ReaderIndex, d.ReadableBytes);
|
||||
}
|
||||
lineBasedDecoder = null;
|
||||
}
|
||||
this.maxFrameLength = maxFrameLength;
|
||||
this.stripDelimiter = stripDelimiter;
|
||||
this.failFast = failFast;
|
||||
}
|
||||
|
||||
public DelimiterBasedFrameDecoder(int maxFrameLength, IByteBuffer delimiter)
|
||||
: this(maxFrameLength, true, true, new IByteBuffer[] { delimiter })
|
||||
{
|
||||
}
|
||||
|
||||
public DelimiterBasedFrameDecoder(int maxFrameLength, bool stripDelimiter, IByteBuffer delimiter)
|
||||
: this(maxFrameLength, stripDelimiter, true, new IByteBuffer[] { delimiter })
|
||||
{
|
||||
}
|
||||
|
||||
public DelimiterBasedFrameDecoder(int maxFrameLength, bool stripDelimiter, bool failFast, IByteBuffer delimiter)
|
||||
: this(maxFrameLength, true, failFast, new IByteBuffer[] { delimiter })
|
||||
{
|
||||
}
|
||||
|
||||
public DelimiterBasedFrameDecoder(int maxFrameLength, params IByteBuffer[] delimiters)
|
||||
: this(maxFrameLength, true, true, delimiters )
|
||||
{
|
||||
}
|
||||
|
||||
public DelimiterBasedFrameDecoder(int maxFrameLength, bool stripDelimiter, params IByteBuffer[] delimiters)
|
||||
: this(maxFrameLength, stripDelimiter, true, delimiters)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>Returns true if the delimiters are "\n" and "\r\n"</summary>
|
||||
static bool IsLineBased(IByteBuffer[] delimiters)
|
||||
{
|
||||
if (delimiters.Length != 2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
IByteBuffer a = delimiters[0];
|
||||
IByteBuffer b = delimiters[1];
|
||||
if (a.Capacity < b.Capacity)
|
||||
{
|
||||
a = delimiters[1];
|
||||
b = delimiters[0];
|
||||
}
|
||||
return a.Capacity == 2 && b.Capacity == 1 && a.GetByte(0) == '\r' && a.GetByte(1) == '\n' && b.GetByte(0) == '\n';
|
||||
}
|
||||
|
||||
|
||||
/// <summary>ReturnsReturn true if the current instance is a subclass of DelimiterBasedFrameDecoder</summary>
|
||||
bool IsSubclass()
|
||||
{
|
||||
return (this is DelimiterBasedFrameDecoder);
|
||||
}
|
||||
|
||||
override protected internal void Decode(IChannelHandlerContext ctx, IByteBuffer input, List<object> output)
|
||||
{
|
||||
var decoded = Decode(ctx, input);
|
||||
if (decoded != null)
|
||||
output.Add(decoded);
|
||||
}
|
||||
|
||||
/// <summary>Create a frame out of the <see cref="DotNetty.Buffers.IByteBuffer"/> and return it</summary>
|
||||
/// <param name="ctx">the <see cref="DotNetty.Transport.Channels.IChannelHandlerContext" /> which this <see cref="DotNetty.Codecs.ByteToMessageDecoder"/> belongs to</param>
|
||||
/// <param name="buffer">the <see cref="DotNetty.Buffers.IByteBuffer" /> from which to read data</param>
|
||||
/// <returns>the <see cref="DotNetty.Buffers.IByteBuffer" /> which represent the frame or null if no frame could be created.</returns>
|
||||
protected object Decode(IChannelHandlerContext ctx, IByteBuffer buffer)
|
||||
{
|
||||
if (lineBasedDecoder != null)
|
||||
{
|
||||
return lineBasedDecoder.Decode(ctx, buffer);
|
||||
}
|
||||
|
||||
// Try all delimiters and choose the delimiter which yields the shortest frame.
|
||||
int minFrameLength = int.MaxValue;
|
||||
IByteBuffer minDelim = null;
|
||||
foreach (IByteBuffer delim in delimiters)
|
||||
{
|
||||
int frameLength = IndexOf(buffer, delim);
|
||||
if (frameLength >= 0 && frameLength < minFrameLength)
|
||||
{
|
||||
minFrameLength = frameLength;
|
||||
minDelim = delim;
|
||||
}
|
||||
}
|
||||
|
||||
if (minDelim != null)
|
||||
{
|
||||
int minDelimLength = minDelim.Capacity;
|
||||
IByteBuffer frame;
|
||||
|
||||
if (discardingTooLongFrame)
|
||||
{
|
||||
// We've just finished discarding a very large frame.
|
||||
// Go back to the initial state.
|
||||
discardingTooLongFrame = false;
|
||||
buffer.SkipBytes(minFrameLength + minDelimLength);
|
||||
|
||||
int tooLongFrameLength = this.tooLongFrameLength;
|
||||
this.tooLongFrameLength = 0;
|
||||
if (!failFast)
|
||||
{
|
||||
Fail(tooLongFrameLength);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (minFrameLength > this.maxFrameLength)
|
||||
{
|
||||
// Discard read frame.
|
||||
buffer.SkipBytes(minFrameLength + minDelimLength);
|
||||
Fail(minFrameLength);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (stripDelimiter)
|
||||
{
|
||||
frame = buffer.ReadSlice(minFrameLength);
|
||||
buffer.SkipBytes(minDelimLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
frame = buffer.ReadSlice(minFrameLength + minDelimLength);
|
||||
}
|
||||
|
||||
return frame.Retain();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!discardingTooLongFrame)
|
||||
{
|
||||
if (buffer.ReadableBytes > maxFrameLength)
|
||||
{
|
||||
// Discard the content of the buffer until a delimiter is found.
|
||||
tooLongFrameLength = buffer.ReadableBytes;
|
||||
buffer.SkipBytes(buffer.ReadableBytes);
|
||||
discardingTooLongFrame = true;
|
||||
if (failFast)
|
||||
{
|
||||
Fail(tooLongFrameLength);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Still discarding the buffer since a delimiter is not found.
|
||||
tooLongFrameLength += buffer.ReadableBytes;
|
||||
buffer.SkipBytes(buffer.ReadableBytes);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
void Fail(long frameLength)
|
||||
{
|
||||
if (frameLength > 0)
|
||||
throw new TooLongFrameException("frame length exceeds " + maxFrameLength +": " + frameLength + " - discarded");
|
||||
else
|
||||
throw new TooLongFrameException("frame length exceeds " + maxFrameLength +" - discarding");
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
static int IndexOf(IByteBuffer haystack, IByteBuffer needle)
|
||||
{
|
||||
for (int i = haystack.ReaderIndex; i < haystack.WriterIndex; i++)
|
||||
{
|
||||
int haystackIndex = i;
|
||||
int needleIndex;
|
||||
for (needleIndex = 0; needleIndex < needle.Capacity; needleIndex++)
|
||||
{
|
||||
if (haystack.GetByte(haystackIndex) != needle.GetByte(needleIndex))
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
haystackIndex++;
|
||||
if (haystackIndex == haystack.WriterIndex && needleIndex != needle.Capacity - 1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needleIndex == needle.Capacity)
|
||||
{
|
||||
// Found the needle from the haystack!
|
||||
return i - haystack.ReaderIndex;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void ValidateDelimiter(IByteBuffer delimiter)
|
||||
{
|
||||
if (delimiter == null)
|
||||
throw new NullReferenceException("delimiter");
|
||||
|
||||
if (!delimiter.IsReadable())
|
||||
throw new ArgumentException("empty delimiter");
|
||||
}
|
||||
|
||||
static void ValidateMaxFrameLength(int maxFrameLength)
|
||||
{
|
||||
if (maxFrameLength <= 0)
|
||||
throw new ArgumentException("maxFrameLength must be a positive integer: " + maxFrameLength);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright (c) Microsoft. All rights reserved.
|
||||
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
||||
|
||||
using DotNetty.Buffers;
|
||||
|
||||
namespace DotNetty.Codecs
|
||||
{
|
||||
public class Delimiters
|
||||
{
|
||||
/// <summary>Returns a null (0x00) delimiter, which could be used for Flash XML socket or any similar protocols</summary>
|
||||
public static IByteBuffer[] NullDelimiter()
|
||||
{
|
||||
return new IByteBuffer[] { Unpooled.WrappedBuffer(new byte[] { 0 }) };
|
||||
}
|
||||
|
||||
/// <summary>Returns {@code CR ('\r')} and {@code LF ('\n')} delimiters, which could
|
||||
/// be used for text-based line protocols.</summary>
|
||||
public static IByteBuffer[] LineDelimiter()
|
||||
{
|
||||
return new IByteBuffer[]
|
||||
{
|
||||
Unpooled.WrappedBuffer(new byte[] { (byte)'\r', (byte)'\n' }),
|
||||
Unpooled.WrappedBuffer(new byte[] { (byte)'\n' }),
|
||||
};
|
||||
}
|
||||
|
||||
private Delimiters()
|
||||
{
|
||||
// Unused
|
||||
}
|
||||
}
|
||||
}
|
|
@ -46,6 +46,8 @@
|
|||
<Compile Include="CodecException.cs" />
|
||||
<Compile Include="CorruptedFrameException.cs" />
|
||||
<Compile Include="DecoderException.cs" />
|
||||
<Compile Include="DelimiterBasedFrameDecoder.cs" />
|
||||
<Compile Include="Delimiters.cs" />
|
||||
<Compile Include="EncoderException.cs" />
|
||||
<Compile Include="Json\JsonObjectDecoder.cs" />
|
||||
<Compile Include="LengthFieldBasedFrameDecoder.cs" />
|
||||
|
|
|
@ -57,7 +57,7 @@ namespace DotNetty.Codecs.Json
|
|||
this.streamArrayElements = streamArrayElements;
|
||||
}
|
||||
|
||||
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
|
||||
protected internal override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
|
||||
{
|
||||
if (this.state == StCorrupted)
|
||||
{
|
||||
|
|
|
@ -287,7 +287,7 @@ namespace DotNetty.Codecs
|
|||
this.failFast = failFast;
|
||||
}
|
||||
|
||||
protected override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
|
||||
protected internal override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
|
||||
{
|
||||
object decoded = this.Decode(context, input);
|
||||
if (decoded != null)
|
||||
|
|
|
@ -66,7 +66,7 @@ namespace DotNetty.Codecs
|
|||
this.stripDelimiter = stripDelimiter;
|
||||
}
|
||||
|
||||
protected sealed override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
|
||||
protected internal sealed override void Decode(IChannelHandlerContext context, IByteBuffer input, List<object> output)
|
||||
{
|
||||
object decode = this.Decode(context, input);
|
||||
if (decode != null)
|
||||
|
@ -80,7 +80,7 @@ namespace DotNetty.Codecs
|
|||
/// </summary>
|
||||
/// <param name="ctx">the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to</param>
|
||||
/// <param name="buffer">the {@link ByteBuf} from which to read data</param>
|
||||
protected object Decode(IChannelHandlerContext ctx, IByteBuffer buffer)
|
||||
protected internal object Decode(IChannelHandlerContext ctx, IByteBuffer buffer)
|
||||
{
|
||||
int eol = this.FindEndOfLine(buffer);
|
||||
if (!this.discarding)
|
||||
|
|
Загрузка…
Ссылка в новой задаче