Implement continuation frames.
This commit is contained in:
parent
1d5b4582f1
commit
1bd9bf1714
|
|
@ -26,12 +26,15 @@ namespace Microsoft.Net.WebSockets
|
||||||
private WebSocketCloseStatus? _closeStatus;
|
private WebSocketCloseStatus? _closeStatus;
|
||||||
private string _closeStatusDescription;
|
private string _closeStatusDescription;
|
||||||
|
|
||||||
|
private bool _outgoingMessageInProgress;
|
||||||
|
|
||||||
private byte[] _receiveBuffer;
|
private byte[] _receiveBuffer;
|
||||||
private int _receiveOffset;
|
private int _receiveOffset;
|
||||||
private int _receiveCount;
|
private int _receiveCount;
|
||||||
|
|
||||||
private FrameHeader _frameInProgress;
|
private FrameHeader _frameInProgress;
|
||||||
private long _frameBytesRemaining = 0;
|
private long _frameBytesRemaining = 0;
|
||||||
|
private int? _firstDataOpCode;
|
||||||
|
|
||||||
public CommonWebSocket(Stream stream, string subProtocol, int receiveBufferSize, bool maskOutput, bool useZeroMask, bool unmaskInput)
|
public CommonWebSocket(Stream stream, string subProtocol, int receiveBufferSize, bool maskOutput, bool useZeroMask, bool unmaskInput)
|
||||||
{
|
{
|
||||||
|
|
@ -111,7 +114,7 @@ namespace Microsoft.Net.WebSockets
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
int mask = GetNextMask();
|
int mask = GetNextMask();
|
||||||
FrameHeader frameHeader = new FrameHeader(endOfMessage, GetOpCode(messageType), _maskOutput, mask, buffer.Count);
|
FrameHeader frameHeader = new FrameHeader(endOfMessage, _outgoingMessageInProgress ? Constants.OpCodes.ContinuationFrame : GetOpCode(messageType), _maskOutput, mask, buffer.Count);
|
||||||
ArraySegment<byte> segment = frameHeader.Buffer;
|
ArraySegment<byte> segment = frameHeader.Buffer;
|
||||||
if (_maskOutput && mask != 0)
|
if (_maskOutput && mask != 0)
|
||||||
{
|
{
|
||||||
|
|
@ -123,6 +126,7 @@ namespace Microsoft.Net.WebSockets
|
||||||
await _stream.WriteAsync(segment.Array, segment.Offset, segment.Count, cancellationToken);
|
await _stream.WriteAsync(segment.Array, segment.Offset, segment.Count, cancellationToken);
|
||||||
await _stream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count, cancellationToken);
|
await _stream.WriteAsync(buffer.Array, buffer.Offset, buffer.Count, cancellationToken);
|
||||||
}
|
}
|
||||||
|
_outgoingMessageInProgress = !endOfMessage;
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
@ -182,9 +186,32 @@ namespace Microsoft.Net.WebSockets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle fragmentation, remember the first frame type
|
||||||
|
int opCode = 0;
|
||||||
|
if (_frameInProgress.OpCode == Constants.OpCodes.BinaryFrame
|
||||||
|
|| _frameInProgress.OpCode == Constants.OpCodes.TextFrame
|
||||||
|
|| _frameInProgress.OpCode == Constants.OpCodes.CloseFrame)
|
||||||
|
{
|
||||||
|
opCode = _frameInProgress.OpCode;
|
||||||
|
_firstDataOpCode = opCode;
|
||||||
|
}
|
||||||
|
else if (_frameInProgress.OpCode == Constants.OpCodes.ContinuationFrame)
|
||||||
|
{
|
||||||
|
if (!_firstDataOpCode.HasValue)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("A continuation can't be the first frame");
|
||||||
|
}
|
||||||
|
opCode = _firstDataOpCode.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_frameInProgress.Fin)
|
||||||
|
{
|
||||||
|
_firstDataOpCode = null;
|
||||||
|
}
|
||||||
|
|
||||||
WebSocketReceiveResult result;
|
WebSocketReceiveResult result;
|
||||||
|
|
||||||
if (_frameInProgress.OpCode == Constants.OpCodes.CloseFrame)
|
if (opCode == Constants.OpCodes.CloseFrame)
|
||||||
{
|
{
|
||||||
// The close message should be less than 125 bytes and fit in the buffer.
|
// The close message should be less than 125 bytes and fit in the buffer.
|
||||||
await EnsureDataAvailableOrReadAsync((int)_frameBytesRemaining, CancellationToken.None);
|
await EnsureDataAvailableOrReadAsync((int)_frameBytesRemaining, CancellationToken.None);
|
||||||
|
|
@ -223,7 +250,7 @@ namespace Microsoft.Net.WebSockets
|
||||||
if (_frameBytesRemaining == 0)
|
if (_frameBytesRemaining == 0)
|
||||||
{
|
{
|
||||||
// End of an empty frame?
|
// End of an empty frame?
|
||||||
result = new WebSocketReceiveResult(0, GetMessageType(_frameInProgress.OpCode), true);
|
result = new WebSocketReceiveResult(0, GetMessageType(opCode), _frameInProgress.Fin);
|
||||||
_frameInProgress = null;
|
_frameInProgress = null;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
@ -241,12 +268,12 @@ namespace Microsoft.Net.WebSockets
|
||||||
}
|
}
|
||||||
if (bytesToCopy == _frameBytesRemaining)
|
if (bytesToCopy == _frameBytesRemaining)
|
||||||
{
|
{
|
||||||
result = new WebSocketReceiveResult(bytesToCopy, GetMessageType(_frameInProgress.OpCode), _frameInProgress.Fin);
|
result = new WebSocketReceiveResult(bytesToCopy, GetMessageType(opCode), _frameInProgress.Fin);
|
||||||
_frameInProgress = null;
|
_frameInProgress = null;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
result = new WebSocketReceiveResult(bytesToCopy, GetMessageType(_frameInProgress.OpCode), false);
|
result = new WebSocketReceiveResult(bytesToCopy, GetMessageType(opCode), false);
|
||||||
}
|
}
|
||||||
_frameBytesRemaining -= bytesToCopy;
|
_frameBytesRemaining -= bytesToCopy;
|
||||||
_receiveCount -= bytesToCopy;
|
_receiveCount -= bytesToCopy;
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ namespace Microsoft.Net.WebSockets
|
||||||
|
|
||||||
public static class OpCodes
|
public static class OpCodes
|
||||||
{
|
{
|
||||||
|
public const int ContinuationFrame = 0x0;
|
||||||
public const int TextFrame = 0x1;
|
public const int TextFrame = 0x1;
|
||||||
public const int BinaryFrame = 0x2;
|
public const int BinaryFrame = 0x2;
|
||||||
public const int CloseFrame = 0x8;
|
public const int CloseFrame = 0x8;
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,57 @@ namespace Microsoft.Net.WebSockets.Test
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SendFragmentedData_Success()
|
||||||
|
{
|
||||||
|
using (HttpListener listener = new HttpListener())
|
||||||
|
{
|
||||||
|
listener.Prefixes.Add(ServerAddress);
|
||||||
|
listener.Start();
|
||||||
|
Task<HttpListenerContext> serverAccept = listener.GetContextAsync();
|
||||||
|
|
||||||
|
WebSocketClient client = new WebSocketClient();
|
||||||
|
Task<WebSocket> clientConnect = client.ConnectAsync(new Uri(ClientAddress), CancellationToken.None);
|
||||||
|
|
||||||
|
HttpListenerContext serverContext = await serverAccept;
|
||||||
|
Assert.True(serverContext.Request.IsWebSocketRequest);
|
||||||
|
HttpListenerWebSocketContext serverWebSocketContext = await serverContext.AcceptWebSocketAsync(null);
|
||||||
|
WebSocket serverSocket = serverWebSocketContext.WebSocket;
|
||||||
|
|
||||||
|
WebSocket clientSocket = await clientConnect;
|
||||||
|
|
||||||
|
byte[] orriginalData = Encoding.UTF8.GetBytes("Hello World");
|
||||||
|
await clientSocket.SendAsync(new ArraySegment<byte>(orriginalData, 0, 2), WebSocketMessageType.Binary, false, CancellationToken.None);
|
||||||
|
await clientSocket.SendAsync(new ArraySegment<byte>(orriginalData, 2, 2), WebSocketMessageType.Binary, false, CancellationToken.None);
|
||||||
|
await clientSocket.SendAsync(new ArraySegment<byte>(orriginalData, 4, 7), WebSocketMessageType.Binary, true, CancellationToken.None);
|
||||||
|
|
||||||
|
byte[] serverBuffer = new byte[orriginalData.Length];
|
||||||
|
WebSocketReceiveResult result = await serverSocket.ReceiveAsync(new ArraySegment<byte>(serverBuffer), CancellationToken.None);
|
||||||
|
Assert.False(result.EndOfMessage);
|
||||||
|
Assert.Equal(2, result.Count);
|
||||||
|
int totalReceived = result.Count;
|
||||||
|
Assert.Equal(WebSocketMessageType.Binary, result.MessageType);
|
||||||
|
|
||||||
|
result = await serverSocket.ReceiveAsync(
|
||||||
|
new ArraySegment<byte>(serverBuffer, totalReceived, serverBuffer.Length - totalReceived), CancellationToken.None);
|
||||||
|
Assert.False(result.EndOfMessage);
|
||||||
|
Assert.Equal(2, result.Count);
|
||||||
|
totalReceived += result.Count;
|
||||||
|
Assert.Equal(WebSocketMessageType.Binary, result.MessageType);
|
||||||
|
|
||||||
|
result = await serverSocket.ReceiveAsync(
|
||||||
|
new ArraySegment<byte>(serverBuffer, totalReceived, serverBuffer.Length - totalReceived), CancellationToken.None);
|
||||||
|
Assert.True(result.EndOfMessage);
|
||||||
|
Assert.Equal(7, result.Count);
|
||||||
|
totalReceived += result.Count;
|
||||||
|
Assert.Equal(WebSocketMessageType.Binary, result.MessageType);
|
||||||
|
|
||||||
|
Assert.Equal(orriginalData, serverBuffer);
|
||||||
|
|
||||||
|
clientSocket.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task ReceiveShortData_Success()
|
public async Task ReceiveShortData_Success()
|
||||||
{
|
{
|
||||||
|
|
@ -281,6 +332,57 @@ namespace Microsoft.Net.WebSockets.Test
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ReceiveFragmentedData_Success()
|
||||||
|
{
|
||||||
|
using (HttpListener listener = new HttpListener())
|
||||||
|
{
|
||||||
|
listener.Prefixes.Add(ServerAddress);
|
||||||
|
listener.Start();
|
||||||
|
Task<HttpListenerContext> serverAccept = listener.GetContextAsync();
|
||||||
|
|
||||||
|
WebSocketClient client = new WebSocketClient();
|
||||||
|
Task<WebSocket> clientConnect = client.ConnectAsync(new Uri(ClientAddress), CancellationToken.None);
|
||||||
|
|
||||||
|
HttpListenerContext serverContext = await serverAccept;
|
||||||
|
Assert.True(serverContext.Request.IsWebSocketRequest);
|
||||||
|
HttpListenerWebSocketContext serverWebSocketContext = await serverContext.AcceptWebSocketAsync(null);
|
||||||
|
WebSocket serverSocket = serverWebSocketContext.WebSocket;
|
||||||
|
|
||||||
|
WebSocket clientSocket = await clientConnect;
|
||||||
|
|
||||||
|
byte[] orriginalData = Encoding.UTF8.GetBytes("Hello World");
|
||||||
|
await serverSocket.SendAsync(new ArraySegment<byte>(orriginalData, 0, 2), WebSocketMessageType.Binary, false, CancellationToken.None);
|
||||||
|
await serverSocket.SendAsync(new ArraySegment<byte>(orriginalData, 2, 2), WebSocketMessageType.Binary, false, CancellationToken.None);
|
||||||
|
await serverSocket.SendAsync(new ArraySegment<byte>(orriginalData, 4, 7), WebSocketMessageType.Binary, true, CancellationToken.None);
|
||||||
|
|
||||||
|
byte[] serverBuffer = new byte[orriginalData.Length];
|
||||||
|
WebSocketReceiveResult result = await clientSocket.ReceiveAsync(new ArraySegment<byte>(serverBuffer), CancellationToken.None);
|
||||||
|
Assert.False(result.EndOfMessage);
|
||||||
|
Assert.Equal(2, result.Count);
|
||||||
|
int totalReceived = result.Count;
|
||||||
|
Assert.Equal(WebSocketMessageType.Binary, result.MessageType);
|
||||||
|
|
||||||
|
result = await clientSocket.ReceiveAsync(
|
||||||
|
new ArraySegment<byte>(serverBuffer, totalReceived, serverBuffer.Length - totalReceived), CancellationToken.None);
|
||||||
|
Assert.False(result.EndOfMessage);
|
||||||
|
Assert.Equal(2, result.Count);
|
||||||
|
totalReceived += result.Count;
|
||||||
|
Assert.Equal(WebSocketMessageType.Binary, result.MessageType);
|
||||||
|
|
||||||
|
result = await clientSocket.ReceiveAsync(
|
||||||
|
new ArraySegment<byte>(serverBuffer, totalReceived, serverBuffer.Length - totalReceived), CancellationToken.None);
|
||||||
|
Assert.True(result.EndOfMessage);
|
||||||
|
Assert.Equal(7, result.Count);
|
||||||
|
totalReceived += result.Count;
|
||||||
|
Assert.Equal(WebSocketMessageType.Binary, result.MessageType);
|
||||||
|
|
||||||
|
Assert.Equal(orriginalData, serverBuffer);
|
||||||
|
|
||||||
|
clientSocket.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task SendClose_Success()
|
public async Task SendClose_Success()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue