diff --git a/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.ConnectionLifecycle.cs b/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.ConnectionLifecycle.cs index 43fc25ba9a..597841f8f7 100644 --- a/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.ConnectionLifecycle.cs +++ b/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.ConnectionLifecycle.cs @@ -14,157 +14,154 @@ namespace Microsoft.Extensions.WebSockets.Internal.Tests { public partial class WebSocketConnectionTests { - public class ConnectionLifecycle + [Fact] + public async Task SendReceiveFrames() { - [Fact] - public async Task SendReceiveFrames() + using (var pair = WebSocketPair.Create()) { - using (var pair = WebSocketPair.Create()) + var client = pair.ClientSocket.ExecuteAsync(_ => { - var client = pair.ClientSocket.ExecuteAsync(_ => - { - Assert.False(true, "did not expect the client to receive any frames!"); - return TaskCache.CompletedTask; - }); + Assert.False(true, "did not expect the client to receive any frames!"); + return TaskCache.CompletedTask; + }); - // Send Frames - await pair.ClientSocket.SendAsync(CreateTextFrame("Hello")).OrTimeout(); - await pair.ClientSocket.SendAsync(CreateTextFrame("World")).OrTimeout(); - await pair.ClientSocket.SendAsync(CreateBinaryFrame(new byte[] { 0xDE, 0xAD, 0xBE, 0xEF })).OrTimeout(); - await pair.ClientSocket.CloseAsync(new WebSocketCloseResult(WebSocketCloseStatus.NormalClosure)).OrTimeout(); + // Send Frames + await pair.ClientSocket.SendAsync(CreateTextFrame("Hello")).OrTimeout(); + await pair.ClientSocket.SendAsync(CreateTextFrame("World")).OrTimeout(); + await pair.ClientSocket.SendAsync(CreateBinaryFrame(new byte[] { 0xDE, 0xAD, 0xBE, 0xEF })).OrTimeout(); + await pair.ClientSocket.CloseAsync(new WebSocketCloseResult(WebSocketCloseStatus.NormalClosure)).OrTimeout(); - var summary = await pair.ServerSocket.ExecuteAndCaptureFramesAsync().OrTimeout(); - Assert.Equal(3, summary.Received.Count); - Assert.Equal("Hello", Encoding.UTF8.GetString(summary.Received[0].Payload.ToArray())); - Assert.Equal("World", Encoding.UTF8.GetString(summary.Received[1].Payload.ToArray())); - Assert.Equal(new byte[] { 0xDE, 0xAD, 0xBE, 0xEF }, summary.Received[2].Payload.ToArray()); + var summary = await pair.ServerSocket.ExecuteAndCaptureFramesAsync().OrTimeout(); + Assert.Equal(3, summary.Received.Count); + Assert.Equal("Hello", Encoding.UTF8.GetString(summary.Received[0].Payload.ToArray())); + Assert.Equal("World", Encoding.UTF8.GetString(summary.Received[1].Payload.ToArray())); + Assert.Equal(new byte[] { 0xDE, 0xAD, 0xBE, 0xEF }, summary.Received[2].Payload.ToArray()); - await pair.ServerSocket.CloseAsync(new WebSocketCloseResult(WebSocketCloseStatus.NormalClosure)).OrTimeout(); - await client.OrTimeout(); - } + await pair.ServerSocket.CloseAsync(new WebSocketCloseResult(WebSocketCloseStatus.NormalClosure)).OrTimeout(); + await client.OrTimeout(); } + } - [Fact] - public async Task ExecuteReturnsWhenCloseFrameReceived() + [Fact] + public async Task ExecuteReturnsWhenCloseFrameReceived() + { + using (var pair = WebSocketPair.Create()) { - using (var pair = WebSocketPair.Create()) - { - var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); - await pair.ClientSocket.CloseAsync(new WebSocketCloseResult(WebSocketCloseStatus.InvalidMessageType, "Abc")).OrTimeout(); - var serverSummary = await pair.ServerSocket.ExecuteAndCaptureFramesAsync().OrTimeout(); - await pair.ServerSocket.CloseAsync(new WebSocketCloseResult(WebSocketCloseStatus.NormalClosure, "Ok")).OrTimeout(); - var clientSummary = await client.OrTimeout(); + var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); + await pair.ClientSocket.CloseAsync(new WebSocketCloseResult(WebSocketCloseStatus.InvalidMessageType, "Abc")).OrTimeout(); + var serverSummary = await pair.ServerSocket.ExecuteAndCaptureFramesAsync().OrTimeout(); + await pair.ServerSocket.CloseAsync(new WebSocketCloseResult(WebSocketCloseStatus.NormalClosure, "Ok")).OrTimeout(); + var clientSummary = await client.OrTimeout(); - Assert.Equal(0, serverSummary.Received.Count); - Assert.Equal(WebSocketCloseStatus.InvalidMessageType, serverSummary.CloseResult.Status); - Assert.Equal("Abc", serverSummary.CloseResult.Description); + Assert.Equal(0, serverSummary.Received.Count); + Assert.Equal(WebSocketCloseStatus.InvalidMessageType, serverSummary.CloseResult.Status); + Assert.Equal("Abc", serverSummary.CloseResult.Description); - Assert.Equal(0, clientSummary.Received.Count); - Assert.Equal(WebSocketCloseStatus.NormalClosure, clientSummary.CloseResult.Status); - Assert.Equal("Ok", clientSummary.CloseResult.Description); - } + Assert.Equal(0, clientSummary.Received.Count); + Assert.Equal(WebSocketCloseStatus.NormalClosure, clientSummary.CloseResult.Status); + Assert.Equal("Ok", clientSummary.CloseResult.Description); } + } - [Fact] - public async Task AbnormalTerminationOfInboundChannelCausesExecuteToThrow() + [Fact] + public async Task AbnormalTerminationOfInboundChannelCausesExecuteToThrow() + { + using (var pair = WebSocketPair.Create()) { - using (var pair = WebSocketPair.Create()) - { - var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); - var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); - pair.TerminateFromClient(new InvalidOperationException("It broke!")); + var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); + var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); + pair.TerminateFromClient(new InvalidOperationException("It broke!")); - await Assert.ThrowsAsync(() => server); - } + await Assert.ThrowsAsync(() => server); } + } - [Fact] - public async Task StateTransitions() + [Fact] + public async Task StateTransitions() + { + using (var pair = WebSocketPair.Create()) { - using (var pair = WebSocketPair.Create()) - { - // Initial State - Assert.Equal(WebSocketConnectionState.Created, pair.ServerSocket.State); - Assert.Equal(WebSocketConnectionState.Created, pair.ClientSocket.State); + // Initial State + Assert.Equal(WebSocketConnectionState.Created, pair.ServerSocket.State); + Assert.Equal(WebSocketConnectionState.Created, pair.ClientSocket.State); - // Start the sockets - var serverReceiving = new TaskCompletionSource(); - var clientReceiving = new TaskCompletionSource(); - var server = pair.ServerSocket.ExecuteAsync(frame => serverReceiving.TrySetResult(null)); - var client = pair.ClientSocket.ExecuteAsync(frame => clientReceiving.TrySetResult(null)); + // Start the sockets + var serverReceiving = new TaskCompletionSource(); + var clientReceiving = new TaskCompletionSource(); + var server = pair.ServerSocket.ExecuteAsync(frame => serverReceiving.TrySetResult(null)); + var client = pair.ClientSocket.ExecuteAsync(frame => clientReceiving.TrySetResult(null)); - // Send a frame from each and verify that the state transitioned. - // We need to do this because it's the only way to correctly wait for the state transition (which happens asynchronously in ExecuteAsync) - await pair.ClientSocket.SendAsync(CreateTextFrame("Hello")).OrTimeout(); - await pair.ServerSocket.SendAsync(CreateTextFrame("Hello")).OrTimeout(); + // Send a frame from each and verify that the state transitioned. + // We need to do this because it's the only way to correctly wait for the state transition (which happens asynchronously in ExecuteAsync) + await pair.ClientSocket.SendAsync(CreateTextFrame("Hello")).OrTimeout(); + await pair.ServerSocket.SendAsync(CreateTextFrame("Hello")).OrTimeout(); - await Task.WhenAll(serverReceiving.Task, clientReceiving.Task).OrTimeout(); + await Task.WhenAll(serverReceiving.Task, clientReceiving.Task).OrTimeout(); - // Check state - Assert.Equal(WebSocketConnectionState.Connected, pair.ServerSocket.State); - Assert.Equal(WebSocketConnectionState.Connected, pair.ClientSocket.State); + // Check state + Assert.Equal(WebSocketConnectionState.Connected, pair.ServerSocket.State); + Assert.Equal(WebSocketConnectionState.Connected, pair.ClientSocket.State); - // Close the server socket - await pair.ServerSocket.CloseAsync(new WebSocketCloseResult(WebSocketCloseStatus.NormalClosure)).OrTimeout(); - await client.OrTimeout(); + // Close the server socket + await pair.ServerSocket.CloseAsync(new WebSocketCloseResult(WebSocketCloseStatus.NormalClosure)).OrTimeout(); + await client.OrTimeout(); - // Check state - Assert.Equal(WebSocketConnectionState.CloseSent, pair.ServerSocket.State); - Assert.Equal(WebSocketConnectionState.CloseReceived, pair.ClientSocket.State); + // Check state + Assert.Equal(WebSocketConnectionState.CloseSent, pair.ServerSocket.State); + Assert.Equal(WebSocketConnectionState.CloseReceived, pair.ClientSocket.State); - // Close the client socket - await pair.ClientSocket.CloseAsync(new WebSocketCloseResult(WebSocketCloseStatus.NormalClosure)).OrTimeout(); - await server.OrTimeout(); + // Close the client socket + await pair.ClientSocket.CloseAsync(new WebSocketCloseResult(WebSocketCloseStatus.NormalClosure)).OrTimeout(); + await server.OrTimeout(); - // Check state - Assert.Equal(WebSocketConnectionState.Closed, pair.ServerSocket.State); - Assert.Equal(WebSocketConnectionState.Closed, pair.ClientSocket.State); + // Check state + Assert.Equal(WebSocketConnectionState.Closed, pair.ServerSocket.State); + Assert.Equal(WebSocketConnectionState.Closed, pair.ClientSocket.State); - // Verify we can't restart the connection or send a message - await Assert.ThrowsAsync(async () => await pair.ServerSocket.ExecuteAsync(f => { })); - await Assert.ThrowsAsync(async () => await pair.ClientSocket.SendAsync(CreateTextFrame("Nope"))); - } + // Verify we can't restart the connection or send a message + await Assert.ThrowsAsync(async () => await pair.ServerSocket.ExecuteAsync(f => { })); + await Assert.ThrowsAsync(async () => await pair.ClientSocket.SendAsync(CreateTextFrame("Nope"))); } + } - [Fact] - public async Task CanReceiveControlFrameInTheMiddleOfFragmentedMessage() + [Fact] + public async Task CanReceiveControlFrameInTheMiddleOfFragmentedMessage() + { + using (var pair = WebSocketPair.Create()) { - using (var pair = WebSocketPair.Create()) - { - // Start the sockets - var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); - var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); + // Start the sockets + var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); + var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); - // Send (Fin=false, "Hello"), (Ping), (Fin=true, "World") - await pair.ClientSocket.SendAsync(new WebSocketFrame( - endOfMessage: false, - opcode: WebSocketOpcode.Text, - payload: ReadableBuffer.Create(Encoding.UTF8.GetBytes("Hello")))); - await pair.ClientSocket.SendAsync(new WebSocketFrame( - endOfMessage: true, - opcode: WebSocketOpcode.Ping, - payload: ReadableBuffer.Create(Encoding.UTF8.GetBytes("ping")))); - await pair.ClientSocket.SendAsync(new WebSocketFrame( - endOfMessage: true, - opcode: WebSocketOpcode.Continuation, - payload: ReadableBuffer.Create(Encoding.UTF8.GetBytes("World")))); + // Send (Fin=false, "Hello"), (Ping), (Fin=true, "World") + await pair.ClientSocket.SendAsync(new WebSocketFrame( + endOfMessage: false, + opcode: WebSocketOpcode.Text, + payload: ReadableBuffer.Create(Encoding.UTF8.GetBytes("Hello")))); + await pair.ClientSocket.SendAsync(new WebSocketFrame( + endOfMessage: true, + opcode: WebSocketOpcode.Ping, + payload: ReadableBuffer.Create(Encoding.UTF8.GetBytes("ping")))); + await pair.ClientSocket.SendAsync(new WebSocketFrame( + endOfMessage: true, + opcode: WebSocketOpcode.Continuation, + payload: ReadableBuffer.Create(Encoding.UTF8.GetBytes("World")))); - // Close the socket - await pair.ClientSocket.CloseAsync(WebSocketCloseStatus.NormalClosure); - var serverSummary = await server; - await pair.ServerSocket.CloseAsync(WebSocketCloseStatus.NormalClosure); - var clientSummary = await client; + // Close the socket + await pair.ClientSocket.CloseAsync(WebSocketCloseStatus.NormalClosure); + var serverSummary = await server; + await pair.ServerSocket.CloseAsync(WebSocketCloseStatus.NormalClosure); + var clientSummary = await client; - // Assert - var nonControlFrames = serverSummary.Received.Where(f => f.Opcode < WebSocketOpcode.Close).ToList(); - Assert.Equal(2, nonControlFrames.Count); - Assert.False(nonControlFrames[0].EndOfMessage); - Assert.True(nonControlFrames[1].EndOfMessage); - Assert.Equal(WebSocketOpcode.Text, nonControlFrames[0].Opcode); - Assert.Equal(WebSocketOpcode.Continuation, nonControlFrames[1].Opcode); - Assert.Equal("Hello", Encoding.UTF8.GetString(nonControlFrames[0].Payload.ToArray())); - Assert.Equal("World", Encoding.UTF8.GetString(nonControlFrames[1].Payload.ToArray())); - } + // Assert + var nonControlFrames = serverSummary.Received.Where(f => f.Opcode < WebSocketOpcode.Close).ToList(); + Assert.Equal(2, nonControlFrames.Count); + Assert.False(nonControlFrames[0].EndOfMessage); + Assert.True(nonControlFrames[1].EndOfMessage); + Assert.Equal(WebSocketOpcode.Text, nonControlFrames[0].Opcode); + Assert.Equal(WebSocketOpcode.Continuation, nonControlFrames[1].Opcode); + Assert.Equal("Hello", Encoding.UTF8.GetString(nonControlFrames[0].Payload.ToArray())); + Assert.Equal("World", Encoding.UTF8.GetString(nonControlFrames[1].Payload.ToArray())); } } } diff --git a/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.PingPong.cs b/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.PingPong.cs index dfa4b97adc..dd6a27c166 100644 --- a/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.PingPong.cs +++ b/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.PingPong.cs @@ -1,106 +1,101 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.AspNetCore.SignalR.Tests.Common; using System; using System.Globalization; using System.IO.Pipelines; using System.Linq; using System.Text; using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR.Tests.Common; using Xunit; -using Microsoft.AspNetCore.Testing.xunit; namespace Microsoft.Extensions.WebSockets.Internal.Tests { public partial class WebSocketConnectionTests { - public class PingPongBehavior + [Fact] + public async Task AutomaticPingTransmission() { - [ConditionalFact] - [SkipIfEnvVarPresent("TRAVIS", "This test times out a lot on Travis because of slow performance/threading issues")] - public async Task AutomaticPingTransmission() + var startTime = DateTime.UtcNow; + // Arrange + using (var pair = WebSocketPair.Create( + serverOptions: new WebSocketOptions().WithAllFramesPassedThrough().WithPingInterval(TimeSpan.FromMilliseconds(10)), + clientOptions: new WebSocketOptions().WithAllFramesPassedThrough())) { - var startTime = DateTime.UtcNow; - // Arrange - using (var pair = WebSocketPair.Create( - serverOptions: new WebSocketOptions().WithAllFramesPassedThrough().WithPingInterval(TimeSpan.FromMilliseconds(10)), - clientOptions: new WebSocketOptions().WithAllFramesPassedThrough())) + var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); + var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); + + // Act + // Wait for pings to be sent + await Task.Delay(500); + + await pair.ServerSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); + var clientSummary = await client.OrTimeout(); + await pair.ClientSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); + var serverSummary = await server.OrTimeout(); + + // Assert + Assert.NotEqual(0, clientSummary.Received.Count); + + Assert.True(clientSummary.Received.All(f => f.EndOfMessage)); + Assert.True(clientSummary.Received.All(f => f.Opcode == WebSocketOpcode.Ping)); + Assert.True(clientSummary.Received.All(f => { - var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); - var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); + var str = Encoding.UTF8.GetString(f.Payload.ToArray()); - // Act - // Wait for pings to be sent - await Task.Delay(500); - - await pair.ServerSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); - var clientSummary = await client.OrTimeout(); - await pair.ClientSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); - var serverSummary = await server.OrTimeout(); - - // Assert - Assert.NotEqual(0, clientSummary.Received.Count); - - Assert.True(clientSummary.Received.All(f => f.EndOfMessage)); - Assert.True(clientSummary.Received.All(f => f.Opcode == WebSocketOpcode.Ping)); - Assert.True(clientSummary.Received.All(f => + // We can't verify the exact timestamp, but we can verify that it is a timestamp created after we started. + if (DateTime.TryParseExact(str, "O", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var dt)) { - var str = Encoding.UTF8.GetString(f.Payload.ToArray()); - - // We can't verify the exact timestamp, but we can verify that it is a timestamp created after we started. - if (DateTime.TryParseExact(str, "O", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal, out var dt)) - { - return dt >= startTime; - } - return false; - })); - } + return dt >= startTime; + } + return false; + })); } + } - [Fact] - public async Task AutomaticPingResponse() + [Fact] + public async Task AutomaticPingResponse() + { + // Arrange + using (var pair = WebSocketPair.Create( + serverOptions: new WebSocketOptions().WithAllFramesPassedThrough(), + clientOptions: new WebSocketOptions().WithAllFramesPassedThrough())) { - // Arrange - using (var pair = WebSocketPair.Create( - serverOptions: new WebSocketOptions().WithAllFramesPassedThrough(), - clientOptions: new WebSocketOptions().WithAllFramesPassedThrough())) + var payload = Encoding.UTF8.GetBytes("ping payload"); + + var pongTcs = new TaskCompletionSource(); + + var client = pair.ClientSocket.ExecuteAsync(f => { - var payload = Encoding.UTF8.GetBytes("ping payload"); - - var pongTcs = new TaskCompletionSource(); - - var client = pair.ClientSocket.ExecuteAsync(f => + if (f.Opcode == WebSocketOpcode.Pong) { - if (f.Opcode == WebSocketOpcode.Pong) - { - pongTcs.TrySetResult(f.Copy()); - } - else - { - Assert.False(true, "Received non-pong frame from server!"); - } - }); - var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); + pongTcs.TrySetResult(f.Copy()); + } + else + { + Assert.False(true, "Received non-pong frame from server!"); + } + }); + var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); - // Act - await pair.ClientSocket.SendAsync(new WebSocketFrame( - endOfMessage: true, - opcode: WebSocketOpcode.Ping, - payload: ReadableBuffer.Create(payload))); + // Act + await pair.ClientSocket.SendAsync(new WebSocketFrame( + endOfMessage: true, + opcode: WebSocketOpcode.Ping, + payload: ReadableBuffer.Create(payload))); - var pongFrame = await pongTcs.Task.OrTimeout(); + var pongFrame = await pongTcs.Task.OrTimeout(); - await pair.ClientSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); - await server.OrTimeout(); - await pair.ServerSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); - await client.OrTimeout(); + await pair.ClientSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); + await server.OrTimeout(); + await pair.ServerSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); + await client.OrTimeout(); - // Assert - Assert.True(pongFrame.EndOfMessage); - Assert.Equal(WebSocketOpcode.Pong, pongFrame.Opcode); - Assert.Equal(payload, pongFrame.Payload.ToArray()); - } + // Assert + Assert.True(pongFrame.EndOfMessage); + Assert.Equal(WebSocketOpcode.Pong, pongFrame.Opcode); + Assert.Equal(payload, pongFrame.Payload.ToArray()); } } } diff --git a/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.ProtocolErrors.cs b/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.ProtocolErrors.cs index 18dc4dd42a..dfd38cb67c 100644 --- a/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.ProtocolErrors.cs +++ b/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.ProtocolErrors.cs @@ -12,237 +12,234 @@ namespace Microsoft.Extensions.WebSockets.Internal.Tests { public partial class WebSocketConnectionTests { - public class ProtocolErrors + [Theory] + [InlineData(new byte[] { 0x11, 0x00 })] + [InlineData(new byte[] { 0x21, 0x00 })] + [InlineData(new byte[] { 0x31, 0x00 })] + [InlineData(new byte[] { 0x41, 0x00 })] + [InlineData(new byte[] { 0x51, 0x00 })] + [InlineData(new byte[] { 0x61, 0x00 })] + [InlineData(new byte[] { 0x71, 0x00 })] + public Task TerminatesConnectionOnReservedBitSet(byte[] rawFrame) { - [Theory] - [InlineData(new byte[] { 0x11, 0x00 })] - [InlineData(new byte[] { 0x21, 0x00 })] - [InlineData(new byte[] { 0x31, 0x00 })] - [InlineData(new byte[] { 0x41, 0x00 })] - [InlineData(new byte[] { 0x51, 0x00 })] - [InlineData(new byte[] { 0x61, 0x00 })] - [InlineData(new byte[] { 0x71, 0x00 })] - public Task TerminatesConnectionOnReservedBitSet(byte[] rawFrame) - { - return WriteFrameAndExpectClose(rawFrame, WebSocketCloseStatus.ProtocolError, "Reserved bits, which are required to be zero, were set."); - } + return WriteFrameAndExpectClose(rawFrame, WebSocketCloseStatus.ProtocolError, "Reserved bits, which are required to be zero, were set."); + } - [Theory] - [InlineData(0x03)] - [InlineData(0x04)] - [InlineData(0x05)] - [InlineData(0x06)] - [InlineData(0x07)] - [InlineData(0x0B)] - [InlineData(0x0C)] - [InlineData(0x0D)] - [InlineData(0x0E)] - [InlineData(0x0F)] - public Task ReservedOpcodes(byte opcode) + [Theory] + [InlineData(0x03)] + [InlineData(0x04)] + [InlineData(0x05)] + [InlineData(0x06)] + [InlineData(0x07)] + [InlineData(0x0B)] + [InlineData(0x0C)] + [InlineData(0x0D)] + [InlineData(0x0E)] + [InlineData(0x0F)] + public Task ReservedOpcodes(byte opcode) + { + var payload = Encoding.UTF8.GetBytes("hello"); + var frame = new WebSocketFrame( + endOfMessage: true, + opcode: (WebSocketOpcode)opcode, + payload: ReadableBuffer.Create(payload)); + return SendFrameAndExpectClose(frame, WebSocketCloseStatus.ProtocolError, $"Received frame using reserved opcode: 0x{opcode:X}"); + } + + [Theory] + [InlineData(new byte[] { 0x88, 0x01, 0xAB })] + + // Invalid UTF-8 reason + [InlineData(new byte[] { 0x88, 0x07, 0x03, 0xE8, 0x48, 0x65, 0x80, 0x6C, 0x6F })] + [InlineData(new byte[] { 0x88, 0x07, 0x03, 0xE8, 0x48, 0x65, 0x99, 0x6C, 0x6F })] + [InlineData(new byte[] { 0x88, 0x07, 0x03, 0xE8, 0x48, 0x65, 0xAB, 0x6C, 0x6F })] + [InlineData(new byte[] { 0x88, 0x07, 0x03, 0xE8, 0x48, 0x65, 0xB0, 0x6C, 0x6F })] + [InlineData(new byte[] { 0x88, 0x03, 0x03, 0xE8, 0xC2 })] + [InlineData(new byte[] { 0x88, 0x03, 0x03, 0xE8, 0xE0 })] + [InlineData(new byte[] { 0x88, 0x04, 0x03, 0xE8, 0xE0, 0xA0 })] + [InlineData(new byte[] { 0x88, 0x04, 0x03, 0xE8, 0xE0, 0xA4 })] + [InlineData(new byte[] { 0x88, 0x05, 0x03, 0xE8, 0xF0, 0x90, 0x80 })] + [InlineData(new byte[] { 0x88, 0x04, 0x03, 0xE8, 0xC1, 0x88 })] + [InlineData(new byte[] { 0x88, 0x05, 0x03, 0xE8, 0xE0, 0x81, 0x88 })] + [InlineData(new byte[] { 0x88, 0x06, 0x03, 0xE8, 0xF0, 0x80, 0x81, 0x88 })] + [InlineData(new byte[] { 0x88, 0x05, 0x03, 0xE8, 0xE0, 0x82, 0xA7 })] + [InlineData(new byte[] { 0x88, 0x06, 0x03, 0xE8, 0xF0, 0x80, 0x82, 0xA7 })] + [InlineData(new byte[] { 0x88, 0x06, 0x03, 0xE8, 0xF0, 0x80, 0xA0, 0x80 })] + public Task InvalidCloseFrames(byte[] rawFrame) + { + return WriteFrameAndExpectClose(rawFrame, WebSocketCloseStatus.ProtocolError, "Close frame payload invalid"); + } + + [Fact] + public Task CloseFrameTooLong() + { + var rawFrame = new byte[256]; + new Random().NextBytes(rawFrame); + + // Put a WebSocket frame header in front + rawFrame[0] = 0x88; // Close frame, FIN=true + rawFrame[1] = 0x7E; // Mask=false, LEN=126 + rawFrame[2] = 0x00; // Extended Len = 252 (256 - 4 bytes for header) + rawFrame[3] = 0xFC; + + return WriteFrameAndExpectClose(rawFrame, WebSocketCloseStatus.ProtocolError, "Close frame payload too long. Maximum size is 125 bytes"); + } + + [Theory] + // 0-999 reserved + [InlineData(0)] + [InlineData(999)] + // Specifically reserved status codes, or codes that should not be sent in frames. + [InlineData(1004)] + [InlineData(1005)] + [InlineData(1006)] + [InlineData(1012)] + [InlineData(1013)] + [InlineData(1014)] + [InlineData(1015)] + // Undefined status codes + [InlineData(1016)] + [InlineData(1100)] + [InlineData(2000)] + [InlineData(2999)] + public Task InvalidCloseStatuses(ushort status) + { + var rawFrame = new byte[] { 0x88, 0x02, (byte)(status >> 8), (byte)(status) }; + return WriteFrameAndExpectClose(rawFrame, WebSocketCloseStatus.ProtocolError, $"Invalid close status: {status}."); + } + + [Theory] + [InlineData(new byte[] { 0x08, 0x00 })] + [InlineData(new byte[] { 0x09, 0x00 })] + [InlineData(new byte[] { 0x0A, 0x00 })] + public Task TerminatesConnectionOnFragmentedControlFrame(byte[] rawFrame) + { + return WriteFrameAndExpectClose(rawFrame, WebSocketCloseStatus.ProtocolError, "Control frames may not be fragmented"); + } + + [Fact] + public async Task TerminatesConnectionOnNonContinuationFrameFollowingFragmentedMessageStart() + { + // Arrange + using (var pair = WebSocketPair.Create( + serverOptions: new WebSocketOptions().WithAllFramesPassedThrough(), + clientOptions: new WebSocketOptions().WithAllFramesPassedThrough())) { var payload = Encoding.UTF8.GetBytes("hello"); - var frame = new WebSocketFrame( - endOfMessage: true, - opcode: (WebSocketOpcode)opcode, - payload: ReadableBuffer.Create(payload)); - return SendFrameAndExpectClose(frame, WebSocketCloseStatus.ProtocolError, $"Received frame using reserved opcode: 0x{opcode:X}"); + + var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); + var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); + + // Act + await pair.ClientSocket.SendAsync(new WebSocketFrame( + endOfMessage: false, + opcode: WebSocketOpcode.Text, + payload: ReadableBuffer.Create(payload))); + await pair.ClientSocket.SendAsync(new WebSocketFrame( + endOfMessage: true, + opcode: WebSocketOpcode.Text, + payload: ReadableBuffer.Create(payload))); + + // Server should terminate + var clientSummary = await client.OrTimeout(); + + Assert.Equal(WebSocketCloseStatus.ProtocolError, clientSummary.CloseResult.Status); + Assert.Equal("Received non-continuation frame during a fragmented message", clientSummary.CloseResult.Description); + + await server.OrTimeout(); } + } - [Theory] - [InlineData(new byte[] { 0x88, 0x01, 0xAB })] - - // Invalid UTF-8 reason - [InlineData(new byte[] { 0x88, 0x07, 0x03, 0xE8, 0x48, 0x65, 0x80, 0x6C, 0x6F })] - [InlineData(new byte[] { 0x88, 0x07, 0x03, 0xE8, 0x48, 0x65, 0x99, 0x6C, 0x6F })] - [InlineData(new byte[] { 0x88, 0x07, 0x03, 0xE8, 0x48, 0x65, 0xAB, 0x6C, 0x6F })] - [InlineData(new byte[] { 0x88, 0x07, 0x03, 0xE8, 0x48, 0x65, 0xB0, 0x6C, 0x6F })] - [InlineData(new byte[] { 0x88, 0x03, 0x03, 0xE8, 0xC2 })] - [InlineData(new byte[] { 0x88, 0x03, 0x03, 0xE8, 0xE0 })] - [InlineData(new byte[] { 0x88, 0x04, 0x03, 0xE8, 0xE0, 0xA0 })] - [InlineData(new byte[] { 0x88, 0x04, 0x03, 0xE8, 0xE0, 0xA4 })] - [InlineData(new byte[] { 0x88, 0x05, 0x03, 0xE8, 0xF0, 0x90, 0x80 })] - [InlineData(new byte[] { 0x88, 0x04, 0x03, 0xE8, 0xC1, 0x88 })] - [InlineData(new byte[] { 0x88, 0x05, 0x03, 0xE8, 0xE0, 0x81, 0x88 })] - [InlineData(new byte[] { 0x88, 0x06, 0x03, 0xE8, 0xF0, 0x80, 0x81, 0x88 })] - [InlineData(new byte[] { 0x88, 0x05, 0x03, 0xE8, 0xE0, 0x82, 0xA7 })] - [InlineData(new byte[] { 0x88, 0x06, 0x03, 0xE8, 0xF0, 0x80, 0x82, 0xA7 })] - [InlineData(new byte[] { 0x88, 0x06, 0x03, 0xE8, 0xF0, 0x80, 0xA0, 0x80 })] - public Task InvalidCloseFrames(byte[] rawFrame) + [Fact] + public async Task TerminatesConnectionOnUnsolicitedContinuationFrame() + { + // Arrange + using (var pair = WebSocketPair.Create( + serverOptions: new WebSocketOptions().WithAllFramesPassedThrough(), + clientOptions: new WebSocketOptions().WithAllFramesPassedThrough())) { - return WriteFrameAndExpectClose(rawFrame, WebSocketCloseStatus.ProtocolError, "Close frame payload invalid"); + var payload = Encoding.UTF8.GetBytes("hello"); + + var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); + var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); + + // Act + await pair.ClientSocket.SendAsync(new WebSocketFrame( + endOfMessage: true, + opcode: WebSocketOpcode.Text, + payload: ReadableBuffer.Create(payload))); + await pair.ClientSocket.SendAsync(new WebSocketFrame( + endOfMessage: true, + opcode: WebSocketOpcode.Continuation, + payload: ReadableBuffer.Create(payload))); + + // Server should terminate + var clientSummary = await client.OrTimeout(); + + Assert.Equal(WebSocketCloseStatus.ProtocolError, clientSummary.CloseResult.Status); + Assert.Equal("Continuation Frame was received when expecting a new message", clientSummary.CloseResult.Description); + + await server.OrTimeout(); } + } - [Fact] - public Task CloseFrameTooLong() + [Fact] + public Task TerminatesConnectionOnPingFrameLargerThan125Bytes() + { + var payload = new byte[126]; + new Random().NextBytes(payload); + return SendFrameAndExpectClose( + new WebSocketFrame( + endOfMessage: true, + opcode: WebSocketOpcode.Ping, + payload: ReadableBuffer.Create(payload)), + WebSocketCloseStatus.ProtocolError, + "Ping frame exceeded maximum size of 125 bytes"); + } + + private static async Task SendFrameAndExpectClose(WebSocketFrame frame, WebSocketCloseStatus closeStatus, string closeReason) + { + // Arrange + using (var pair = WebSocketPair.Create( + serverOptions: new WebSocketOptions().WithAllFramesPassedThrough(), + clientOptions: new WebSocketOptions().WithAllFramesPassedThrough())) { - var rawFrame = new byte[256]; - new Random().NextBytes(rawFrame); + var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); + var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); - // Put a WebSocket frame header in front - rawFrame[0] = 0x88; // Close frame, FIN=true - rawFrame[1] = 0x7E; // Mask=false, LEN=126 - rawFrame[2] = 0x00; // Extended Len = 252 (256 - 4 bytes for header) - rawFrame[3] = 0xFC; + // Act + await pair.ClientSocket.SendAsync(frame); - return WriteFrameAndExpectClose(rawFrame, WebSocketCloseStatus.ProtocolError, "Close frame payload too long. Maximum size is 125 bytes"); + // Server should terminate + var clientSummary = await client.OrTimeout(); + + Assert.Equal(closeStatus, clientSummary.CloseResult.Status); + Assert.Equal(closeReason, clientSummary.CloseResult.Description); + + await server.OrTimeout(); } + } - [Theory] - // 0-999 reserved - [InlineData(0)] - [InlineData(999)] - // Specifically reserved status codes, or codes that should not be sent in frames. - [InlineData(1004)] - [InlineData(1005)] - [InlineData(1006)] - [InlineData(1012)] - [InlineData(1013)] - [InlineData(1014)] - [InlineData(1015)] - // Undefined status codes - [InlineData(1016)] - [InlineData(1100)] - [InlineData(2000)] - [InlineData(2999)] - public Task InvalidCloseStatuses(ushort status) + private static async Task WriteFrameAndExpectClose(byte[] rawFrame, WebSocketCloseStatus closeStatus, string closeReason) + { + // Arrange + using (var pair = WebSocketPair.Create( + serverOptions: new WebSocketOptions().WithAllFramesPassedThrough(), + clientOptions: new WebSocketOptions().WithAllFramesPassedThrough())) { - var rawFrame = new byte[] { 0x88, 0x02, (byte)(status >> 8), (byte)(status) }; - return WriteFrameAndExpectClose(rawFrame, WebSocketCloseStatus.ProtocolError, $"Invalid close status: {status}."); - } + var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); + var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); - [Theory] - [InlineData(new byte[] { 0x08, 0x00 })] - [InlineData(new byte[] { 0x09, 0x00 })] - [InlineData(new byte[] { 0x0A, 0x00 })] - public Task TerminatesConnectionOnFragmentedControlFrame(byte[] rawFrame) - { - return WriteFrameAndExpectClose(rawFrame, WebSocketCloseStatus.ProtocolError, "Control frames may not be fragmented"); - } + // Act + await pair.ClientToServer.Writer.WriteAsync(rawFrame); - [Fact] - public async Task TerminatesConnectionOnNonContinuationFrameFollowingFragmentedMessageStart() - { - // Arrange - using (var pair = WebSocketPair.Create( - serverOptions: new WebSocketOptions().WithAllFramesPassedThrough(), - clientOptions: new WebSocketOptions().WithAllFramesPassedThrough())) - { - var payload = Encoding.UTF8.GetBytes("hello"); + // Server should terminate + var clientSummary = await client.OrTimeout(); - var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); - var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); + Assert.Equal(closeStatus, clientSummary.CloseResult.Status); + Assert.Equal(closeReason, clientSummary.CloseResult.Description); - // Act - await pair.ClientSocket.SendAsync(new WebSocketFrame( - endOfMessage: false, - opcode: WebSocketOpcode.Text, - payload: ReadableBuffer.Create(payload))); - await pair.ClientSocket.SendAsync(new WebSocketFrame( - endOfMessage: true, - opcode: WebSocketOpcode.Text, - payload: ReadableBuffer.Create(payload))); - - // Server should terminate - var clientSummary = await client.OrTimeout(); - - Assert.Equal(WebSocketCloseStatus.ProtocolError, clientSummary.CloseResult.Status); - Assert.Equal("Received non-continuation frame during a fragmented message", clientSummary.CloseResult.Description); - - await server.OrTimeout(); - } - } - - [Fact] - public async Task TerminatesConnectionOnUnsolicitedContinuationFrame() - { - // Arrange - using (var pair = WebSocketPair.Create( - serverOptions: new WebSocketOptions().WithAllFramesPassedThrough(), - clientOptions: new WebSocketOptions().WithAllFramesPassedThrough())) - { - var payload = Encoding.UTF8.GetBytes("hello"); - - var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); - var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); - - // Act - await pair.ClientSocket.SendAsync(new WebSocketFrame( - endOfMessage: true, - opcode: WebSocketOpcode.Text, - payload: ReadableBuffer.Create(payload))); - await pair.ClientSocket.SendAsync(new WebSocketFrame( - endOfMessage: true, - opcode: WebSocketOpcode.Continuation, - payload: ReadableBuffer.Create(payload))); - - // Server should terminate - var clientSummary = await client.OrTimeout(); - - Assert.Equal(WebSocketCloseStatus.ProtocolError, clientSummary.CloseResult.Status); - Assert.Equal("Continuation Frame was received when expecting a new message", clientSummary.CloseResult.Description); - - await server.OrTimeout(); - } - } - - [Fact] - public Task TerminatesConnectionOnPingFrameLargerThan125Bytes() - { - var payload = new byte[126]; - new Random().NextBytes(payload); - return SendFrameAndExpectClose( - new WebSocketFrame( - endOfMessage: true, - opcode: WebSocketOpcode.Ping, - payload: ReadableBuffer.Create(payload)), - WebSocketCloseStatus.ProtocolError, - "Ping frame exceeded maximum size of 125 bytes"); - } - - private static async Task SendFrameAndExpectClose(WebSocketFrame frame, WebSocketCloseStatus closeStatus, string closeReason) - { - // Arrange - using (var pair = WebSocketPair.Create( - serverOptions: new WebSocketOptions().WithAllFramesPassedThrough(), - clientOptions: new WebSocketOptions().WithAllFramesPassedThrough())) - { - var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); - var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); - - // Act - await pair.ClientSocket.SendAsync(frame); - - // Server should terminate - var clientSummary = await client.OrTimeout(); - - Assert.Equal(closeStatus, clientSummary.CloseResult.Status); - Assert.Equal(closeReason, clientSummary.CloseResult.Description); - - await server.OrTimeout(); - } - } - - private static async Task WriteFrameAndExpectClose(byte[] rawFrame, WebSocketCloseStatus closeStatus, string closeReason) - { - // Arrange - using (var pair = WebSocketPair.Create( - serverOptions: new WebSocketOptions().WithAllFramesPassedThrough(), - clientOptions: new WebSocketOptions().WithAllFramesPassedThrough())) - { - var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); - var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); - - // Act - await pair.ClientToServer.Writer.WriteAsync(rawFrame); - - // Server should terminate - var clientSummary = await client.OrTimeout(); - - Assert.Equal(closeStatus, clientSummary.CloseResult.Status); - Assert.Equal(closeReason, clientSummary.CloseResult.Description); - - await server.OrTimeout(); - } + await server.OrTimeout(); } } } diff --git a/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.ReceiveAsync.cs b/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.ReceiveAsync.cs index 5559d40510..79e38d7eaf 100644 --- a/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.ReceiveAsync.cs +++ b/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.ReceiveAsync.cs @@ -13,184 +13,181 @@ namespace Microsoft.Extensions.WebSockets.Internal.Tests { public partial class WebSocketConnectionTests { - public class TheReceiveAsyncMethod + [Theory] + [InlineData(new byte[] { 0x81, 0x00 }, "", true)] + [InlineData(new byte[] { 0x81, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F }, "Hello", true)] + [InlineData(new byte[] { 0x81, 0x85, 0x1, 0x2, 0x3, 0x4, 0x48 ^ 0x1, 0x65 ^ 0x2, 0x6C ^ 0x3, 0x6C ^ 0x4, 0x6F ^ 0x1 }, "Hello", true)] + [InlineData(new byte[] { 0x01, 0x00 }, "", false)] + [InlineData(new byte[] { 0x01, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F }, "Hello", false)] + [InlineData(new byte[] { 0x01, 0x85, 0x1, 0x2, 0x3, 0x4, 0x48 ^ 0x1, 0x65 ^ 0x2, 0x6C ^ 0x3, 0x6C ^ 0x4, 0x6F ^ 0x1 }, "Hello", false)] + public Task ReadTextFrames(byte[] rawFrame, string message, bool endOfMessage) { - [Theory] - [InlineData(new byte[] { 0x81, 0x00 }, "", true)] - [InlineData(new byte[] { 0x81, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F }, "Hello", true)] - [InlineData(new byte[] { 0x81, 0x85, 0x1, 0x2, 0x3, 0x4, 0x48 ^ 0x1, 0x65 ^ 0x2, 0x6C ^ 0x3, 0x6C ^ 0x4, 0x6F ^ 0x1 }, "Hello", true)] - [InlineData(new byte[] { 0x01, 0x00 }, "", false)] - [InlineData(new byte[] { 0x01, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F }, "Hello", false)] - [InlineData(new byte[] { 0x01, 0x85, 0x1, 0x2, 0x3, 0x4, 0x48 ^ 0x1, 0x65 ^ 0x2, 0x6C ^ 0x3, 0x6C ^ 0x4, 0x6F ^ 0x1 }, "Hello", false)] - public Task ReadTextFrames(byte[] rawFrame, string message, bool endOfMessage) - { - return RunSingleFrameTest( - rawFrame, - endOfMessage, - WebSocketOpcode.Text, - b => Assert.Equal(message, Encoding.UTF8.GetString(b))); - } + return RunSingleFrameTest( + rawFrame, + endOfMessage, + WebSocketOpcode.Text, + b => Assert.Equal(message, Encoding.UTF8.GetString(b))); + } - [Theory] - // Opcode = Binary - [InlineData(new byte[] { 0x82, 0x00 }, new byte[0], WebSocketOpcode.Binary, true)] - [InlineData(new byte[] { 0x82, 0x05, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Binary, true)] - [InlineData(new byte[] { 0x82, 0x85, 0x1, 0x2, 0x3, 0x4, 0xDE ^ 0x1, 0xAD ^ 0x2, 0xBE ^ 0x3, 0xEF ^ 0x4, 0xAB ^ 0x1 }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Binary, true)] - [InlineData(new byte[] { 0x02, 0x00 }, new byte[0], WebSocketOpcode.Binary, false)] - [InlineData(new byte[] { 0x02, 0x05, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Binary, false)] - [InlineData(new byte[] { 0x02, 0x85, 0x1, 0x2, 0x3, 0x4, 0xDE ^ 0x1, 0xAD ^ 0x2, 0xBE ^ 0x3, 0xEF ^ 0x4, 0xAB ^ 0x1 }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Binary, false)] + [Theory] + // Opcode = Binary + [InlineData(new byte[] { 0x82, 0x00 }, new byte[0], WebSocketOpcode.Binary, true)] + [InlineData(new byte[] { 0x82, 0x05, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Binary, true)] + [InlineData(new byte[] { 0x82, 0x85, 0x1, 0x2, 0x3, 0x4, 0xDE ^ 0x1, 0xAD ^ 0x2, 0xBE ^ 0x3, 0xEF ^ 0x4, 0xAB ^ 0x1 }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Binary, true)] + [InlineData(new byte[] { 0x02, 0x00 }, new byte[0], WebSocketOpcode.Binary, false)] + [InlineData(new byte[] { 0x02, 0x05, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Binary, false)] + [InlineData(new byte[] { 0x02, 0x85, 0x1, 0x2, 0x3, 0x4, 0xDE ^ 0x1, 0xAD ^ 0x2, 0xBE ^ 0x3, 0xEF ^ 0x4, 0xAB ^ 0x1 }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Binary, false)] - // Opcode = Ping - [InlineData(new byte[] { 0x89, 0x00 }, new byte[0], WebSocketOpcode.Ping, true)] - [InlineData(new byte[] { 0x89, 0x05, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Ping, true)] - [InlineData(new byte[] { 0x89, 0x85, 0x1, 0x2, 0x3, 0x4, 0xDE ^ 0x1, 0xAD ^ 0x2, 0xBE ^ 0x3, 0xEF ^ 0x4, 0xAB ^ 0x1 }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Ping, true)] - // Control frames can't have fin=false + // Opcode = Ping + [InlineData(new byte[] { 0x89, 0x00 }, new byte[0], WebSocketOpcode.Ping, true)] + [InlineData(new byte[] { 0x89, 0x05, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Ping, true)] + [InlineData(new byte[] { 0x89, 0x85, 0x1, 0x2, 0x3, 0x4, 0xDE ^ 0x1, 0xAD ^ 0x2, 0xBE ^ 0x3, 0xEF ^ 0x4, 0xAB ^ 0x1 }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Ping, true)] + // Control frames can't have fin=false - // Opcode = Pong - [InlineData(new byte[] { 0x8A, 0x00 }, new byte[0], WebSocketOpcode.Pong, true)] - [InlineData(new byte[] { 0x8A, 0x05, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Pong, true)] - [InlineData(new byte[] { 0x8A, 0x85, 0x1, 0x2, 0x3, 0x4, 0xDE ^ 0x1, 0xAD ^ 0x2, 0xBE ^ 0x3, 0xEF ^ 0x4, 0xAB ^ 0x1 }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Pong, true)] - // Control frames can't have fin=false - public Task ReadBinaryFormattedFrames(byte[] rawFrame, byte[] payload, WebSocketOpcode opcode, bool endOfMessage) - { - return RunSingleFrameTest( - rawFrame, - endOfMessage, - opcode, - b => Assert.Equal(payload, b)); - } + // Opcode = Pong + [InlineData(new byte[] { 0x8A, 0x00 }, new byte[0], WebSocketOpcode.Pong, true)] + [InlineData(new byte[] { 0x8A, 0x05, 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Pong, true)] + [InlineData(new byte[] { 0x8A, 0x85, 0x1, 0x2, 0x3, 0x4, 0xDE ^ 0x1, 0xAD ^ 0x2, 0xBE ^ 0x3, 0xEF ^ 0x4, 0xAB ^ 0x1 }, new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, WebSocketOpcode.Pong, true)] + // Control frames can't have fin=false + public Task ReadBinaryFormattedFrames(byte[] rawFrame, byte[] payload, WebSocketOpcode opcode, bool endOfMessage) + { + return RunSingleFrameTest( + rawFrame, + endOfMessage, + opcode, + b => Assert.Equal(payload, b)); + } - [Fact] - public async Task ReadMultipleFramesAcrossMultipleBuffers() - { - var result = await RunReceiveTest( - producer: async (channel, cancellationToken) => - { - await channel.WriteAsync(new byte[] { 0x02, 0x05 }.Slice()).OrTimeout(); - await Task.Yield(); - await channel.WriteAsync(new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB, 0x80, 0x05 }.Slice()).OrTimeout(); - await Task.Yield(); - await channel.WriteAsync(new byte[] { 0xDE, 0xAD, 0xBE, 0xEF }.Slice()).OrTimeout(); - await Task.Yield(); - await channel.WriteAsync(new byte[] { 0xAB }.Slice()).OrTimeout(); - await Task.Yield(); - }); - - Assert.Equal(2, result.Received.Count); - - Assert.False(result.Received[0].EndOfMessage); - Assert.Equal(WebSocketOpcode.Binary, result.Received[0].Opcode); - Assert.Equal(new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, result.Received[0].Payload.ToArray()); - - Assert.True(result.Received[1].EndOfMessage); - Assert.Equal(WebSocketOpcode.Continuation, result.Received[1].Opcode); - Assert.Equal(new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, result.Received[1].Payload.ToArray()); - } - - [Fact] - public async Task ReadLargeMaskedPayload() - { - // This test was added to ensure we don't break a behavior discovered while running the Autobahn test suite. - - // Larger than one page, which means it will span blocks in the memory pool. - var expectedPayload = new byte[4192]; - for (int i = 0; i < expectedPayload.Length; i++) + [Fact] + public async Task ReadMultipleFramesAcrossMultipleBuffers() + { + var result = await RunReceiveTest( + producer: async (channel, cancellationToken) => { - expectedPayload[i] = (byte)(i % byte.MaxValue); - } - var maskingKey = new byte[] { 0x01, 0x02, 0x03, 0x04 }; - var sendPayload = new byte[4192]; - for (int i = 0; i < expectedPayload.Length; i++) + await channel.WriteAsync(new byte[] { 0x02, 0x05 }.Slice()).OrTimeout(); + await Task.Yield(); + await channel.WriteAsync(new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB, 0x80, 0x05 }.Slice()).OrTimeout(); + await Task.Yield(); + await channel.WriteAsync(new byte[] { 0xDE, 0xAD, 0xBE, 0xEF }.Slice()).OrTimeout(); + await Task.Yield(); + await channel.WriteAsync(new byte[] { 0xAB }.Slice()).OrTimeout(); + await Task.Yield(); + }); + + Assert.Equal(2, result.Received.Count); + + Assert.False(result.Received[0].EndOfMessage); + Assert.Equal(WebSocketOpcode.Binary, result.Received[0].Opcode); + Assert.Equal(new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, result.Received[0].Payload.ToArray()); + + Assert.True(result.Received[1].EndOfMessage); + Assert.Equal(WebSocketOpcode.Continuation, result.Received[1].Opcode); + Assert.Equal(new byte[] { 0xDE, 0xAD, 0xBE, 0xEF, 0xAB }, result.Received[1].Payload.ToArray()); + } + + [Fact] + public async Task ReadLargeMaskedPayload() + { + // This test was added to ensure we don't break a behavior discovered while running the Autobahn test suite. + + // Larger than one page, which means it will span blocks in the memory pool. + var expectedPayload = new byte[4192]; + for (int i = 0; i < expectedPayload.Length; i++) + { + expectedPayload[i] = (byte)(i % byte.MaxValue); + } + var maskingKey = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + var sendPayload = new byte[4192]; + for (int i = 0; i < expectedPayload.Length; i++) + { + sendPayload[i] = (byte)(expectedPayload[i] ^ maskingKey[i % 4]); + } + + var result = await RunReceiveTest( + producer: async (channel, cancellationToken) => { - sendPayload[i] = (byte)(expectedPayload[i] ^ maskingKey[i % 4]); - } + // We use a 64-bit length because we want to ensure that the first page of data ends at an + // offset within the frame that is NOT divisible by 4. This ensures that when the unmasking + // moves from one buffer to the other, we are at a non-zero position within the masking key. + // This ensures that we're tracking the masking key offset properly. - var result = await RunReceiveTest( - producer: async (channel, cancellationToken) => - { - // We use a 64-bit length because we want to ensure that the first page of data ends at an - // offset within the frame that is NOT divisible by 4. This ensures that when the unmasking - // moves from one buffer to the other, we are at a non-zero position within the masking key. - // This ensures that we're tracking the masking key offset properly. + // Header: (Opcode=Binary, Fin=true), (Mask=false, Len=126), (64-bit big endian length) + await channel.WriteAsync(new byte[] { 0x82, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x60 }).OrTimeout(); + await channel.WriteAsync(maskingKey).OrTimeout(); + await Task.Yield(); + await channel.WriteAsync(sendPayload).OrTimeout(); + }); - // Header: (Opcode=Binary, Fin=true), (Mask=false, Len=126), (64-bit big endian length) - await channel.WriteAsync(new byte[] { 0x82, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x60 }).OrTimeout(); - await channel.WriteAsync(maskingKey).OrTimeout(); - await Task.Yield(); - await channel.WriteAsync(sendPayload).OrTimeout(); - }); + Assert.Equal(1, result.Received.Count); - Assert.Equal(1, result.Received.Count); + var frame = result.Received[0]; + Assert.True(frame.EndOfMessage); + Assert.Equal(WebSocketOpcode.Binary, frame.Opcode); + Assert.Equal(expectedPayload, frame.Payload.ToArray()); + } - var frame = result.Received[0]; - Assert.True(frame.EndOfMessage); - Assert.Equal(WebSocketOpcode.Binary, frame.Opcode); - Assert.Equal(expectedPayload, frame.Payload.ToArray()); - } + [Fact] + public async Task Read16BitPayloadLength() + { + var expectedPayload = new byte[1024]; + new Random().NextBytes(expectedPayload); - [Fact] - public async Task Read16BitPayloadLength() - { - var expectedPayload = new byte[1024]; - new Random().NextBytes(expectedPayload); + var result = await RunReceiveTest( + producer: async (channel, cancellationToken) => + { + // Header: (Opcode=Binary, Fin=true), (Mask=false, Len=126), (16-bit big endian length) + await channel.WriteAsync(new byte[] { 0x82, 0x7E, 0x04, 0x00 }).OrTimeout(); + await Task.Yield(); + await channel.WriteAsync(expectedPayload).OrTimeout(); + }); - var result = await RunReceiveTest( - producer: async (channel, cancellationToken) => - { - // Header: (Opcode=Binary, Fin=true), (Mask=false, Len=126), (16-bit big endian length) - await channel.WriteAsync(new byte[] { 0x82, 0x7E, 0x04, 0x00 }).OrTimeout(); - await Task.Yield(); - await channel.WriteAsync(expectedPayload).OrTimeout(); - }); + Assert.Equal(1, result.Received.Count); - Assert.Equal(1, result.Received.Count); + var frame = result.Received[0]; + Assert.True(frame.EndOfMessage); + Assert.Equal(WebSocketOpcode.Binary, frame.Opcode); + Assert.Equal(expectedPayload, frame.Payload.ToArray()); + } - var frame = result.Received[0]; - Assert.True(frame.EndOfMessage); - Assert.Equal(WebSocketOpcode.Binary, frame.Opcode); - Assert.Equal(expectedPayload, frame.Payload.ToArray()); - } + [Fact] + public async Task Read64bitPayloadLength() + { + // Allocating an actual (2^32 + 1) byte payload is crazy for this test. We just need to test that we can USE a 64-bit length + var expectedPayload = new byte[1024]; + new Random().NextBytes(expectedPayload); - [Fact] - public async Task Read64bitPayloadLength() - { - // Allocating an actual (2^32 + 1) byte payload is crazy for this test. We just need to test that we can USE a 64-bit length - var expectedPayload = new byte[1024]; - new Random().NextBytes(expectedPayload); + var result = await RunReceiveTest( + producer: async (channel, cancellationToken) => + { + // Header: (Opcode=Binary, Fin=true), (Mask=false, Len=127), (64-bit big endian length) + await channel.WriteAsync(new byte[] { 0x82, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00 }).OrTimeout(); + await Task.Yield(); + await channel.WriteAsync(expectedPayload).OrTimeout(); + }); - var result = await RunReceiveTest( - producer: async (channel, cancellationToken) => - { - // Header: (Opcode=Binary, Fin=true), (Mask=false, Len=127), (64-bit big endian length) - await channel.WriteAsync(new byte[] { 0x82, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00 }).OrTimeout(); - await Task.Yield(); - await channel.WriteAsync(expectedPayload).OrTimeout(); - }); + Assert.Equal(1, result.Received.Count); - Assert.Equal(1, result.Received.Count); + var frame = result.Received[0]; + Assert.True(frame.EndOfMessage); + Assert.Equal(WebSocketOpcode.Binary, frame.Opcode); + Assert.Equal(expectedPayload, frame.Payload.ToArray()); + } - var frame = result.Received[0]; - Assert.True(frame.EndOfMessage); - Assert.Equal(WebSocketOpcode.Binary, frame.Opcode); - Assert.Equal(expectedPayload, frame.Payload.ToArray()); - } + private static async Task RunSingleFrameTest(byte[] rawFrame, bool endOfMessage, WebSocketOpcode expectedOpcode, Action payloadAssert) + { + var result = await RunReceiveTest( + producer: async (channel, cancellationToken) => + { + await channel.WriteAsync(rawFrame.Slice()).OrTimeout(); + }); + var frames = result.Received; + Assert.Equal(1, frames.Count); - private static async Task RunSingleFrameTest(byte[] rawFrame, bool endOfMessage, WebSocketOpcode expectedOpcode, Action payloadAssert) - { - var result = await RunReceiveTest( - producer: async (channel, cancellationToken) => - { - await channel.WriteAsync(rawFrame.Slice()).OrTimeout(); - }); - var frames = result.Received; - Assert.Equal(1, frames.Count); + var frame = frames[0]; - var frame = frames[0]; - - Assert.Equal(endOfMessage, frame.EndOfMessage); - Assert.Equal(expectedOpcode, frame.Opcode); - payloadAssert(frame.Payload.ToArray()); - } + Assert.Equal(endOfMessage, frame.EndOfMessage); + Assert.Equal(expectedOpcode, frame.Opcode); + payloadAssert(frame.Payload.ToArray()); } private static async Task RunReceiveTest(Func producer) diff --git a/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.SendAsync.cs b/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.SendAsync.cs index 5ddae6662c..7460ebf238 100644 --- a/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.SendAsync.cs +++ b/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.SendAsync.cs @@ -13,196 +13,193 @@ namespace Microsoft.Extensions.WebSockets.Internal.Tests { public partial class WebSocketConnectionTests { - public class TheSendAsyncMethod + // No auto-pinging for us! + private readonly static WebSocketOptions DefaultTestOptions = new WebSocketOptions().WithAllFramesPassedThrough(); + + [Theory] + [InlineData("", true, new byte[] { 0x81, 0x00 })] + [InlineData("Hello", true, new byte[] { 0x81, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F })] + [InlineData("", false, new byte[] { 0x01, 0x00 })] + [InlineData("Hello", false, new byte[] { 0x01, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F })] + public async Task WriteTextFrames(string message, bool endOfMessage, byte[] expectedRawFrame) { - // No auto-pinging for us! - private readonly static WebSocketOptions DefaultTestOptions = new WebSocketOptions().WithAllFramesPassedThrough(); - - [Theory] - [InlineData("", true, new byte[] { 0x81, 0x00 })] - [InlineData("Hello", true, new byte[] { 0x81, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F })] - [InlineData("", false, new byte[] { 0x01, 0x00 })] - [InlineData("Hello", false, new byte[] { 0x01, 0x05, 0x48, 0x65, 0x6C, 0x6C, 0x6F })] - public async Task WriteTextFrames(string message, bool endOfMessage, byte[] expectedRawFrame) - { - var data = await RunSendTest( - producer: async (socket) => - { - var payload = Encoding.UTF8.GetBytes(message); - await socket.SendAsync(CreateFrame( - endOfMessage, - opcode: WebSocketOpcode.Text, - payload: payload)).OrTimeout(); - }, options: DefaultTestOptions); - Assert.Equal(expectedRawFrame, data); - } - - [Theory] - // Opcode = Binary - [InlineData(new byte[0], WebSocketOpcode.Binary, true, new byte[] { 0x82, 0x00 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Binary, true, new byte[] { 0x82, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] - [InlineData(new byte[0], WebSocketOpcode.Binary, false, new byte[] { 0x02, 0x00 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Binary, false, new byte[] { 0x02, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] - - // Opcode = Continuation - [InlineData(new byte[0], WebSocketOpcode.Continuation, true, new byte[] { 0x80, 0x00 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Continuation, true, new byte[] { 0x80, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] - [InlineData(new byte[0], WebSocketOpcode.Continuation, false, new byte[] { 0x00, 0x00 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Continuation, false, new byte[] { 0x00, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] - - // Opcode = Ping - [InlineData(new byte[0], WebSocketOpcode.Ping, true, new byte[] { 0x89, 0x00 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Ping, true, new byte[] { 0x89, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] - [InlineData(new byte[0], WebSocketOpcode.Ping, false, new byte[] { 0x09, 0x00 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Ping, false, new byte[] { 0x09, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] - - // Opcode = Pong - [InlineData(new byte[0], WebSocketOpcode.Pong, true, new byte[] { 0x8A, 0x00 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Pong, true, new byte[] { 0x8A, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] - [InlineData(new byte[0], WebSocketOpcode.Pong, false, new byte[] { 0x0A, 0x00 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Pong, false, new byte[] { 0x0A, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] - public async Task WriteBinaryFormattedFrames(byte[] payload, WebSocketOpcode opcode, bool endOfMessage, byte[] expectedRawFrame) - { - var data = await RunSendTest( - producer: async (socket) => - { - await socket.SendAsync(CreateFrame( - endOfMessage, - opcode, - payload: payload)).OrTimeout(); - }, options: DefaultTestOptions); - Assert.Equal(expectedRawFrame, data); - } - - [Theory] - [InlineData("", new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x81, 0x80, 0x01, 0x02, 0x03, 0x04 })] - [InlineData("Hello", new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x81, 0x85, 0x01, 0x02, 0x03, 0x04, 0x48 ^ 0x01, 0x65 ^ 0x02, 0x6C ^ 0x03, 0x6C ^ 0x04, 0x6F ^ 0x01 })] - public async Task WriteMaskedTextFrames(string message, byte[] maskingKey, byte[] expectedRawFrame) - { - var data = await RunSendTest( - producer: async (socket) => - { - var payload = Encoding.UTF8.GetBytes(message); - await socket.SendAsync(CreateFrame( - endOfMessage: true, - opcode: WebSocketOpcode.Text, - payload: payload)).OrTimeout(); - }, options: DefaultTestOptions.WithFixedMaskingKey(maskingKey)); - Assert.Equal(expectedRawFrame, data); - } - - [Theory] - // Opcode = Binary - [InlineData(new byte[0], WebSocketOpcode.Binary, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x82, 0x80, 0x01, 0x02, 0x03, 0x04 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Binary, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x82, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] - [InlineData(new byte[0], WebSocketOpcode.Binary, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x02, 0x80, 0x01, 0x02, 0x03, 0x04 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Binary, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x02, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] - - // Opcode = Continuation - [InlineData(new byte[0], WebSocketOpcode.Continuation, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x80, 0x80, 0x01, 0x02, 0x03, 0x04 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Continuation, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x80, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] - [InlineData(new byte[0], WebSocketOpcode.Continuation, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x00, 0x80, 0x01, 0x02, 0x03, 0x04 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Continuation, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x00, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] - - // Opcode = Ping - [InlineData(new byte[0], WebSocketOpcode.Ping, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x89, 0x80, 0x01, 0x02, 0x03, 0x04 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Ping, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x89, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] - [InlineData(new byte[0], WebSocketOpcode.Ping, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x09, 0x80, 0x01, 0x02, 0x03, 0x04 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Ping, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x09, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] - - // Opcode = Pong - [InlineData(new byte[0], WebSocketOpcode.Pong, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x8A, 0x80, 0x01, 0x02, 0x03, 0x04 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Pong, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x8A, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] - [InlineData(new byte[0], WebSocketOpcode.Pong, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x0A, 0x80, 0x01, 0x02, 0x03, 0x04 })] - [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Pong, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x0A, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] - public async Task WriteMaskedBinaryFormattedFrames(byte[] payload, WebSocketOpcode opcode, bool endOfMessage, byte[] maskingKey, byte[] expectedRawFrame) - { - var data = await RunSendTest( - producer: async (socket) => - { - await socket.SendAsync(CreateFrame( - endOfMessage, - opcode, - payload: payload)).OrTimeout(); - }, options: DefaultTestOptions.WithFixedMaskingKey(maskingKey)); - Assert.Equal(expectedRawFrame, data); - } - - [Fact] - public async Task WriteRandomMaskedFrame() - { - var data = await RunSendTest( - producer: async (socket) => - { - await socket.SendAsync(CreateFrame( - endOfMessage: true, - opcode: WebSocketOpcode.Binary, - payload: new byte[] { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E })).OrTimeout(); - }, options: DefaultTestOptions.WithRandomMasking()); - - // Verify the header - Assert.Equal(0x82, data[0]); - Assert.Equal(0x85, data[1]); - - // We don't know the mask, so we have to read it in order to verify this frame - var mask = data.Slice(2, 4); - var actualPayload = data.Slice(6); - - // Unmask the payload - for (int i = 0; i < actualPayload.Length; i++) + var data = await RunSendTest( + producer: async (socket) => { - actualPayload[i] = (byte)(mask[i % 4] ^ actualPayload[i]); - } - Assert.Equal(new byte[] { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E }, actualPayload.ToArray()); - } + var payload = Encoding.UTF8.GetBytes(message); + await socket.SendAsync(CreateFrame( + endOfMessage, + opcode: WebSocketOpcode.Text, + payload: payload)).OrTimeout(); + }, options: DefaultTestOptions); + Assert.Equal(expectedRawFrame, data); + } - [Theory] - [InlineData(WebSocketCloseStatus.MandatoryExtension, "Hi", null, new byte[] { 0x88, 0x04, 0x03, 0xF2, (byte)'H', (byte)'i' })] - [InlineData(WebSocketCloseStatus.PolicyViolation, "", null, new byte[] { 0x88, 0x02, 0x03, 0xF0 })] - [InlineData(WebSocketCloseStatus.MandatoryExtension, "Hi", new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x88, 0x84, 0x01, 0x02, 0x03, 0x04, 0x03 ^ 0x01, 0xF2 ^ 0x02, (byte)'H' ^ 0x03, (byte)'i' ^ 0x04 })] - [InlineData(WebSocketCloseStatus.PolicyViolation, "", new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x88, 0x82, 0x01, 0x02, 0x03, 0x04, 0x03 ^ 0x01, 0xF0 ^ 0x02 })] - public async Task WriteCloseFrames(WebSocketCloseStatus status, string description, byte[] maskingKey, byte[] expectedRawFrame) - { - var data = await RunSendTest( - producer: async (socket) => - { - await socket.CloseAsync(new WebSocketCloseResult(status, description)).OrTimeout(); - }, options: maskingKey == null ? DefaultTestOptions : DefaultTestOptions.WithFixedMaskingKey(maskingKey)); - Assert.Equal(expectedRawFrame, data); - } + [Theory] + // Opcode = Binary + [InlineData(new byte[0], WebSocketOpcode.Binary, true, new byte[] { 0x82, 0x00 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Binary, true, new byte[] { 0x82, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] + [InlineData(new byte[0], WebSocketOpcode.Binary, false, new byte[] { 0x02, 0x00 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Binary, false, new byte[] { 0x02, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] - private static async Task RunSendTest(Func producer, WebSocketOptions options) - { - using (var factory = new PipeFactory()) + // Opcode = Continuation + [InlineData(new byte[0], WebSocketOpcode.Continuation, true, new byte[] { 0x80, 0x00 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Continuation, true, new byte[] { 0x80, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] + [InlineData(new byte[0], WebSocketOpcode.Continuation, false, new byte[] { 0x00, 0x00 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Continuation, false, new byte[] { 0x00, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] + + // Opcode = Ping + [InlineData(new byte[0], WebSocketOpcode.Ping, true, new byte[] { 0x89, 0x00 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Ping, true, new byte[] { 0x89, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] + [InlineData(new byte[0], WebSocketOpcode.Ping, false, new byte[] { 0x09, 0x00 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Ping, false, new byte[] { 0x09, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] + + // Opcode = Pong + [InlineData(new byte[0], WebSocketOpcode.Pong, true, new byte[] { 0x8A, 0x00 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Pong, true, new byte[] { 0x8A, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] + [InlineData(new byte[0], WebSocketOpcode.Pong, false, new byte[] { 0x0A, 0x00 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Pong, false, new byte[] { 0x0A, 0x05, 0xA, 0xB, 0xC, 0xD, 0xE })] + public async Task WriteBinaryFormattedFrames(byte[] payload, WebSocketOpcode opcode, bool endOfMessage, byte[] expectedRawFrame) + { + var data = await RunSendTest( + producer: async (socket) => { - var outbound = factory.Create(); - var inbound = factory.Create(); + await socket.SendAsync(CreateFrame( + endOfMessage, + opcode, + payload: payload)).OrTimeout(); + }, options: DefaultTestOptions); + Assert.Equal(expectedRawFrame, data); + } - using (var connection = new WebSocketConnection(inbound.Reader, outbound.Writer, options)) - { - var executeTask = connection.ExecuteAndCaptureFramesAsync(); - await producer(connection).OrTimeout(); - connection.Abort(); - inbound.Writer.Complete(); - await executeTask.OrTimeout(); - } - - var buffer = await outbound.Reader.ReadToEndAsync(); - var data = buffer.ToArray(); - outbound.Reader.Advance(buffer.End); - inbound.Reader.Complete(); - CompleteChannels(outbound); - return data; - } - } - - private static void CompleteChannels(params IPipe[] readerWriters) - { - foreach (var readerWriter in readerWriters) + [Theory] + [InlineData("", new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x81, 0x80, 0x01, 0x02, 0x03, 0x04 })] + [InlineData("Hello", new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x81, 0x85, 0x01, 0x02, 0x03, 0x04, 0x48 ^ 0x01, 0x65 ^ 0x02, 0x6C ^ 0x03, 0x6C ^ 0x04, 0x6F ^ 0x01 })] + public async Task WriteMaskedTextFrames(string message, byte[] maskingKey, byte[] expectedRawFrame) + { + var data = await RunSendTest( + producer: async (socket) => { - readerWriter.Reader.Complete(); - readerWriter.Writer.Complete(); + var payload = Encoding.UTF8.GetBytes(message); + await socket.SendAsync(CreateFrame( + endOfMessage: true, + opcode: WebSocketOpcode.Text, + payload: payload)).OrTimeout(); + }, options: DefaultTestOptions.WithFixedMaskingKey(maskingKey)); + Assert.Equal(expectedRawFrame, data); + } + + [Theory] + // Opcode = Binary + [InlineData(new byte[0], WebSocketOpcode.Binary, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x82, 0x80, 0x01, 0x02, 0x03, 0x04 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Binary, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x82, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] + [InlineData(new byte[0], WebSocketOpcode.Binary, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x02, 0x80, 0x01, 0x02, 0x03, 0x04 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Binary, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x02, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] + + // Opcode = Continuation + [InlineData(new byte[0], WebSocketOpcode.Continuation, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x80, 0x80, 0x01, 0x02, 0x03, 0x04 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Continuation, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x80, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] + [InlineData(new byte[0], WebSocketOpcode.Continuation, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x00, 0x80, 0x01, 0x02, 0x03, 0x04 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Continuation, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x00, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] + + // Opcode = Ping + [InlineData(new byte[0], WebSocketOpcode.Ping, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x89, 0x80, 0x01, 0x02, 0x03, 0x04 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Ping, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x89, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] + [InlineData(new byte[0], WebSocketOpcode.Ping, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x09, 0x80, 0x01, 0x02, 0x03, 0x04 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Ping, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x09, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] + + // Opcode = Pong + [InlineData(new byte[0], WebSocketOpcode.Pong, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x8A, 0x80, 0x01, 0x02, 0x03, 0x04 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Pong, true, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x8A, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] + [InlineData(new byte[0], WebSocketOpcode.Pong, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x0A, 0x80, 0x01, 0x02, 0x03, 0x04 })] + [InlineData(new byte[] { 0xA, 0xB, 0xC, 0xD, 0xE }, WebSocketOpcode.Pong, false, new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x0A, 0x85, 0x01, 0x02, 0x03, 0x04, 0x0A ^ 0x01, 0x0B ^ 0x02, 0x0C ^ 0x03, 0x0D ^ 0x04, 0x0E ^ 0x01 })] + public async Task WriteMaskedBinaryFormattedFrames(byte[] payload, WebSocketOpcode opcode, bool endOfMessage, byte[] maskingKey, byte[] expectedRawFrame) + { + var data = await RunSendTest( + producer: async (socket) => + { + await socket.SendAsync(CreateFrame( + endOfMessage, + opcode, + payload: payload)).OrTimeout(); + }, options: DefaultTestOptions.WithFixedMaskingKey(maskingKey)); + Assert.Equal(expectedRawFrame, data); + } + + [Fact] + public async Task WriteRandomMaskedFrame() + { + var data = await RunSendTest( + producer: async (socket) => + { + await socket.SendAsync(CreateFrame( + endOfMessage: true, + opcode: WebSocketOpcode.Binary, + payload: new byte[] { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E })).OrTimeout(); + }, options: DefaultTestOptions.WithRandomMasking()); + + // Verify the header + Assert.Equal(0x82, data[0]); + Assert.Equal(0x85, data[1]); + + // We don't know the mask, so we have to read it in order to verify this frame + var mask = data.Slice(2, 4); + var actualPayload = data.Slice(6); + + // Unmask the payload + for (int i = 0; i < actualPayload.Length; i++) + { + actualPayload[i] = (byte)(mask[i % 4] ^ actualPayload[i]); + } + Assert.Equal(new byte[] { 0x0A, 0x0B, 0x0C, 0x0D, 0x0E }, actualPayload.ToArray()); + } + + [Theory] + [InlineData(WebSocketCloseStatus.MandatoryExtension, "Hi", null, new byte[] { 0x88, 0x04, 0x03, 0xF2, (byte)'H', (byte)'i' })] + [InlineData(WebSocketCloseStatus.PolicyViolation, "", null, new byte[] { 0x88, 0x02, 0x03, 0xF0 })] + [InlineData(WebSocketCloseStatus.MandatoryExtension, "Hi", new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x88, 0x84, 0x01, 0x02, 0x03, 0x04, 0x03 ^ 0x01, 0xF2 ^ 0x02, (byte)'H' ^ 0x03, (byte)'i' ^ 0x04 })] + [InlineData(WebSocketCloseStatus.PolicyViolation, "", new byte[] { 0x01, 0x02, 0x03, 0x04 }, new byte[] { 0x88, 0x82, 0x01, 0x02, 0x03, 0x04, 0x03 ^ 0x01, 0xF0 ^ 0x02 })] + public async Task WriteCloseFrames(WebSocketCloseStatus status, string description, byte[] maskingKey, byte[] expectedRawFrame) + { + var data = await RunSendTest( + producer: async (socket) => + { + await socket.CloseAsync(new WebSocketCloseResult(status, description)).OrTimeout(); + }, options: maskingKey == null ? DefaultTestOptions : DefaultTestOptions.WithFixedMaskingKey(maskingKey)); + Assert.Equal(expectedRawFrame, data); + } + + private static async Task RunSendTest(Func producer, WebSocketOptions options) + { + using (var factory = new PipeFactory()) + { + var outbound = factory.Create(); + var inbound = factory.Create(); + + using (var connection = new WebSocketConnection(inbound.Reader, outbound.Writer, options)) + { + var executeTask = connection.ExecuteAndCaptureFramesAsync(); + await producer(connection).OrTimeout(); + connection.Abort(); + inbound.Writer.Complete(); + await executeTask.OrTimeout(); } + + var buffer = await outbound.Reader.ReadToEndAsync(); + var data = buffer.ToArray(); + outbound.Reader.Advance(buffer.End); + inbound.Reader.Complete(); + CompleteChannels(outbound); + return data; + } + } + + private static void CompleteChannels(params IPipe[] readerWriters) + { + foreach (var readerWriter in readerWriters) + { + readerWriter.Reader.Complete(); + readerWriter.Writer.Complete(); } } } diff --git a/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.Utf8Validation.cs b/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.Utf8Validation.cs index dc484a3e3d..075f40e116 100644 --- a/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.Utf8Validation.cs +++ b/test/Microsoft.Extensions.WebSockets.Internal.Tests/WebSocketConnectionTests.Utf8Validation.cs @@ -12,72 +12,186 @@ namespace Microsoft.Extensions.WebSockets.Internal.Tests { public partial class WebSocketConnectionTests { - public class Utf8Validation + [Theory] + [InlineData(new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F }, "Hello")] + [InlineData(new byte[] { 0xC2, 0xA7, 0x31, 0x2C, 0x20, 0x39, 0x35, 0xC2, 0xA2 }, "§1, 95¢")] + [InlineData(new byte[] { 0xE0, 0xA0, 0x80, 0xE0, 0xA4, 0x80 }, "\u0800\u0900")] + [InlineData(new byte[] { 0xF0, 0x90, 0x80, 0x80 }, "\U00010000")] + public async Task ValidSingleFramePayloads(byte[] payload, string decoded) { - [Theory] - [InlineData(new byte[] { 0x48, 0x65, 0x6C, 0x6C, 0x6F }, "Hello")] - [InlineData(new byte[] { 0xC2, 0xA7, 0x31, 0x2C, 0x20, 0x39, 0x35, 0xC2, 0xA2 }, "§1, 95¢")] - [InlineData(new byte[] { 0xE0, 0xA0, 0x80, 0xE0, 0xA4, 0x80 }, "\u0800\u0900")] - [InlineData(new byte[] { 0xF0, 0x90, 0x80, 0x80 }, "\U00010000")] - public async Task ValidSingleFramePayloads(byte[] payload, string decoded) + using (var pair = WebSocketPair.Create()) { - using (var pair = WebSocketPair.Create()) + var timeoutToken = TestUtil.CreateTimeoutToken(); + using (timeoutToken.Register(() => pair.Dispose())) { - var timeoutToken = TestUtil.CreateTimeoutToken(); - using (timeoutToken.Register(() => pair.Dispose())) - { - var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); - var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); + var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); + var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); - var frame = new WebSocketFrame( - endOfMessage: true, - opcode: WebSocketOpcode.Text, - payload: ReadableBuffer.Create(payload)); - await pair.ClientSocket.SendAsync(frame).OrTimeout(); - await pair.ClientSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); - var serverSummary = await server.OrTimeout(); - await pair.ServerSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); - var clientSummary = await client.OrTimeout(); + var frame = new WebSocketFrame( + endOfMessage: true, + opcode: WebSocketOpcode.Text, + payload: ReadableBuffer.Create(payload)); + await pair.ClientSocket.SendAsync(frame).OrTimeout(); + await pair.ClientSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); + var serverSummary = await server.OrTimeout(); + await pair.ServerSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); + var clientSummary = await client.OrTimeout(); - Assert.Equal(0, clientSummary.Received.Count); + Assert.Equal(0, clientSummary.Received.Count); - Assert.Equal(1, serverSummary.Received.Count); - Assert.True(serverSummary.Received[0].EndOfMessage); - Assert.Equal(WebSocketOpcode.Text, serverSummary.Received[0].Opcode); - Assert.Equal(decoded, Encoding.UTF8.GetString(serverSummary.Received[0].Payload.ToArray())); - } + Assert.Equal(1, serverSummary.Received.Count); + Assert.True(serverSummary.Received[0].EndOfMessage); + Assert.Equal(WebSocketOpcode.Text, serverSummary.Received[0].Opcode); + Assert.Equal(decoded, Encoding.UTF8.GetString(serverSummary.Received[0].Payload.ToArray())); } } + } - [Theory] - [InlineData(new byte[] { 0x48, 0x65 }, new byte[] { 0x6C, 0x6C, 0x6F }, "Hello")] + [Theory] + [InlineData(new byte[] { 0x48, 0x65 }, new byte[] { 0x6C, 0x6C, 0x6F }, "Hello")] - [InlineData(new byte[0], new byte[] { 0xC2, 0xA7 }, "§")] - [InlineData(new byte[] { 0xC2 }, new byte[] { 0xA7 }, "§")] - [InlineData(new byte[] { 0xC2, 0xA7 }, new byte[0], "§")] + [InlineData(new byte[0], new byte[] { 0xC2, 0xA7 }, "§")] + [InlineData(new byte[] { 0xC2 }, new byte[] { 0xA7 }, "§")] + [InlineData(new byte[] { 0xC2, 0xA7 }, new byte[0], "§")] - [InlineData(new byte[0], new byte[] { 0xC2, 0xA2 }, "¢")] - [InlineData(new byte[] { 0xC2 }, new byte[] { 0xA2 }, "¢")] - [InlineData(new byte[] { 0xC2, 0xA2 }, new byte[0], "¢")] + [InlineData(new byte[0], new byte[] { 0xC2, 0xA2 }, "¢")] + [InlineData(new byte[] { 0xC2 }, new byte[] { 0xA2 }, "¢")] + [InlineData(new byte[] { 0xC2, 0xA2 }, new byte[0], "¢")] - [InlineData(new byte[0], new byte[] { 0xE0, 0xA0, 0x80 }, "\u0800")] - [InlineData(new byte[] { 0xE0 }, new byte[] { 0xA0, 0x80 }, "\u0800")] - [InlineData(new byte[] { 0xE0, 0xA0 }, new byte[] { 0x80 }, "\u0800")] - [InlineData(new byte[] { 0xE0, 0xA0, 0x80 }, new byte[0], "\u0800")] + [InlineData(new byte[0], new byte[] { 0xE0, 0xA0, 0x80 }, "\u0800")] + [InlineData(new byte[] { 0xE0 }, new byte[] { 0xA0, 0x80 }, "\u0800")] + [InlineData(new byte[] { 0xE0, 0xA0 }, new byte[] { 0x80 }, "\u0800")] + [InlineData(new byte[] { 0xE0, 0xA0, 0x80 }, new byte[0], "\u0800")] - [InlineData(new byte[0], new byte[] { 0xE0, 0xA4, 0x80 }, "\u0900")] - [InlineData(new byte[] { 0xE0 }, new byte[] { 0xA4, 0x80 }, "\u0900")] - [InlineData(new byte[] { 0xE0, 0xA4 }, new byte[] { 0x80 }, "\u0900")] - [InlineData(new byte[] { 0xE0, 0xA4, 0x80 }, new byte[0], "\u0900")] + [InlineData(new byte[0], new byte[] { 0xE0, 0xA4, 0x80 }, "\u0900")] + [InlineData(new byte[] { 0xE0 }, new byte[] { 0xA4, 0x80 }, "\u0900")] + [InlineData(new byte[] { 0xE0, 0xA4 }, new byte[] { 0x80 }, "\u0900")] + [InlineData(new byte[] { 0xE0, 0xA4, 0x80 }, new byte[0], "\u0900")] - [InlineData(new byte[0], new byte[] { 0xF0, 0x90, 0x80, 0x80 }, "\U00010000")] - [InlineData(new byte[] { 0xF0 }, new byte[] { 0x90, 0x80, 0x80 }, "\U00010000")] - [InlineData(new byte[] { 0xF0, 0x90 }, new byte[] { 0x80, 0x80 }, "\U00010000")] - [InlineData(new byte[] { 0xF0, 0x90, 0x80 }, new byte[] { 0x80 }, "\U00010000")] - [InlineData(new byte[] { 0xF0, 0x90, 0x80, 0x80 }, new byte[0], "\U00010000")] - public async Task ValidMultiFramePayloads(byte[] payload1, byte[] payload2, string decoded) + [InlineData(new byte[0], new byte[] { 0xF0, 0x90, 0x80, 0x80 }, "\U00010000")] + [InlineData(new byte[] { 0xF0 }, new byte[] { 0x90, 0x80, 0x80 }, "\U00010000")] + [InlineData(new byte[] { 0xF0, 0x90 }, new byte[] { 0x80, 0x80 }, "\U00010000")] + [InlineData(new byte[] { 0xF0, 0x90, 0x80 }, new byte[] { 0x80 }, "\U00010000")] + [InlineData(new byte[] { 0xF0, 0x90, 0x80, 0x80 }, new byte[0], "\U00010000")] + public async Task ValidMultiFramePayloads(byte[] payload1, byte[] payload2, string decoded) + { + using (var pair = WebSocketPair.Create()) { - using (var pair = WebSocketPair.Create()) + var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); + var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); + + var frame = new WebSocketFrame( + endOfMessage: false, + opcode: WebSocketOpcode.Text, + payload: ReadableBuffer.Create(payload1)); + await pair.ClientSocket.SendAsync(frame).OrTimeout(); + frame = new WebSocketFrame( + endOfMessage: true, + opcode: WebSocketOpcode.Continuation, + payload: ReadableBuffer.Create(payload2)); + await pair.ClientSocket.SendAsync(frame).OrTimeout(); + await pair.ClientSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); + var serverSummary = await server.OrTimeout(); + await pair.ServerSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); + var clientSummary = await client.OrTimeout(); + + Assert.Equal(0, clientSummary.Received.Count); + + Assert.Equal(2, serverSummary.Received.Count); + Assert.False(serverSummary.Received[0].EndOfMessage); + Assert.Equal(WebSocketOpcode.Text, serverSummary.Received[0].Opcode); + Assert.True(serverSummary.Received[1].EndOfMessage); + Assert.Equal(WebSocketOpcode.Continuation, serverSummary.Received[1].Opcode); + + var finalPayload = serverSummary.Received.SelectMany(f => f.Payload.ToArray()).ToArray(); + Assert.Equal(decoded, Encoding.UTF8.GetString(finalPayload)); + } + } + + [Theory] + + // Continuation byte as first byte of code point + [InlineData(new byte[] { 0x48, 0x65, 0x80, 0x6C, 0x6F })] + [InlineData(new byte[] { 0x48, 0x65, 0x99, 0x6C, 0x6F })] + [InlineData(new byte[] { 0x48, 0x65, 0xAB, 0x6C, 0x6F })] + [InlineData(new byte[] { 0x48, 0x65, 0xB0, 0x6C, 0x6F })] + + // Incomplete Code Point + [InlineData(new byte[] { 0xC2 })] + [InlineData(new byte[] { 0xE0 })] + [InlineData(new byte[] { 0xE0, 0xA0 })] + [InlineData(new byte[] { 0xE0, 0xA4 })] + [InlineData(new byte[] { 0xF0, 0x90, 0x80 })] + + // Overlong Encoding + + // 'H' (1 byte char) encoded with 2, 3 and 4 bytes + [InlineData(new byte[] { 0xC1, 0x88 })] + [InlineData(new byte[] { 0xE0, 0x81, 0x88 })] + [InlineData(new byte[] { 0xF0, 0x80, 0x81, 0x88 })] + + // '§' (2 byte char) encoded with 3 and 4 bytes + [InlineData(new byte[] { 0xE0, 0x82, 0xA7 })] + [InlineData(new byte[] { 0xF0, 0x80, 0x82, 0xA7 })] + + // '\u0800' (3 byte char) encoded with 4 bytes + [InlineData(new byte[] { 0xF0, 0x80, 0xA0, 0x80 })] + public async Task InvalidSingleFramePayloads(byte[] payload) + { + using (var pair = WebSocketPair.Create()) + { + var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); + var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); + + var frame = new WebSocketFrame( + endOfMessage: true, + opcode: WebSocketOpcode.Text, + payload: ReadableBuffer.Create(payload)); + await pair.ClientSocket.SendAsync(frame).OrTimeout(); + var clientSummary = await client.OrTimeout(); + var serverSummary = await server.OrTimeout(); + + Assert.Equal(0, serverSummary.Received.Count); + Assert.Equal(0, clientSummary.Received.Count); + Assert.Equal(WebSocketCloseStatus.InvalidPayloadData, clientSummary.CloseResult.Status); + Assert.Equal("An invalid Text frame payload was received", clientSummary.CloseResult.Description); + } + } + + [Theory] + + // Continuation byte as first byte of code point + [InlineData(new byte[] { 0x48, 0x65 }, new byte[] { 0x80, 0x6C, 0x6F })] + [InlineData(new byte[] { 0x48, 0x65 }, new byte[] { 0x99, 0x6C, 0x6F })] + [InlineData(new byte[] { 0x48, 0x65 }, new byte[] { 0xAB, 0x6C, 0x6F })] + [InlineData(new byte[] { 0x48, 0x65 }, new byte[] { 0xB0, 0x6C, 0x6F })] + + // Incomplete Code Point + [InlineData(new byte[] { 0xC2 }, new byte[0])] + [InlineData(new byte[] { 0xE0 }, new byte[0])] + [InlineData(new byte[] { 0xE0, 0xA0 }, new byte[0])] + [InlineData(new byte[] { 0xE0, 0xA4 }, new byte[0])] + [InlineData(new byte[] { 0xF0, 0x90, 0x80 }, new byte[0])] + + // Overlong Encoding + + // 'H' (1 byte char) encoded with 2, 3 and 4 bytes + [InlineData(new byte[] { 0xC1 }, new byte[] { 0x88 })] + [InlineData(new byte[] { 0xE0 }, new byte[] { 0x81, 0x88 })] + [InlineData(new byte[] { 0xF0 }, new byte[] { 0x80, 0x81, 0x88 })] + + // '§' (2 byte char) encoded with 3 and 4 bytes + [InlineData(new byte[] { 0xE0, 0x82 }, new byte[] { 0xA7 })] + [InlineData(new byte[] { 0xF0, 0x80 }, new byte[] { 0x82, 0xA7 })] + + // '\u0800' (3 byte char) encoded with 4 bytes + [InlineData(new byte[] { 0xF0, 0x80 }, new byte[] { 0xA0, 0x80 })] + public async Task InvalidMultiFramePayloads(byte[] payload1, byte[] payload2) + { + using (var pair = WebSocketPair.Create()) + { + var timeoutToken = TestUtil.CreateTimeoutToken(); + using (timeoutToken.Register(() => pair.Dispose())) { var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); @@ -92,136 +206,19 @@ namespace Microsoft.Extensions.WebSockets.Internal.Tests opcode: WebSocketOpcode.Continuation, payload: ReadableBuffer.Create(payload2)); await pair.ClientSocket.SendAsync(frame).OrTimeout(); - await pair.ClientSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); - var serverSummary = await server.OrTimeout(); - await pair.ServerSocket.CloseAsync(WebSocketCloseStatus.NormalClosure).OrTimeout(); var clientSummary = await client.OrTimeout(); + var serverSummary = await server.OrTimeout(); - Assert.Equal(0, clientSummary.Received.Count); - - Assert.Equal(2, serverSummary.Received.Count); + Assert.Equal(1, serverSummary.Received.Count); Assert.False(serverSummary.Received[0].EndOfMessage); Assert.Equal(WebSocketOpcode.Text, serverSummary.Received[0].Opcode); - Assert.True(serverSummary.Received[1].EndOfMessage); - Assert.Equal(WebSocketOpcode.Continuation, serverSummary.Received[1].Opcode); + Assert.Equal(payload1, serverSummary.Received[0].Payload.ToArray()); - var finalPayload = serverSummary.Received.SelectMany(f => f.Payload.ToArray()).ToArray(); - Assert.Equal(decoded, Encoding.UTF8.GetString(finalPayload)); - } - } - - [Theory] - - // Continuation byte as first byte of code point - [InlineData(new byte[] { 0x48, 0x65, 0x80, 0x6C, 0x6F })] - [InlineData(new byte[] { 0x48, 0x65, 0x99, 0x6C, 0x6F })] - [InlineData(new byte[] { 0x48, 0x65, 0xAB, 0x6C, 0x6F })] - [InlineData(new byte[] { 0x48, 0x65, 0xB0, 0x6C, 0x6F })] - - // Incomplete Code Point - [InlineData(new byte[] { 0xC2 })] - [InlineData(new byte[] { 0xE0 })] - [InlineData(new byte[] { 0xE0, 0xA0 })] - [InlineData(new byte[] { 0xE0, 0xA4 })] - [InlineData(new byte[] { 0xF0, 0x90, 0x80 })] - - // Overlong Encoding - - // 'H' (1 byte char) encoded with 2, 3 and 4 bytes - [InlineData(new byte[] { 0xC1, 0x88 })] - [InlineData(new byte[] { 0xE0, 0x81, 0x88 })] - [InlineData(new byte[] { 0xF0, 0x80, 0x81, 0x88 })] - - // '§' (2 byte char) encoded with 3 and 4 bytes - [InlineData(new byte[] { 0xE0, 0x82, 0xA7 })] - [InlineData(new byte[] { 0xF0, 0x80, 0x82, 0xA7 })] - - // '\u0800' (3 byte char) encoded with 4 bytes - [InlineData(new byte[] { 0xF0, 0x80, 0xA0, 0x80 })] - public async Task InvalidSingleFramePayloads(byte[] payload) - { - using (var pair = WebSocketPair.Create()) - { - var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); - var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); - - var frame = new WebSocketFrame( - endOfMessage: true, - opcode: WebSocketOpcode.Text, - payload: ReadableBuffer.Create(payload)); - await pair.ClientSocket.SendAsync(frame).OrTimeout(); - var clientSummary = await client.OrTimeout(); - var serverSummary = await server.OrTimeout(); - - Assert.Equal(0, serverSummary.Received.Count); Assert.Equal(0, clientSummary.Received.Count); Assert.Equal(WebSocketCloseStatus.InvalidPayloadData, clientSummary.CloseResult.Status); Assert.Equal("An invalid Text frame payload was received", clientSummary.CloseResult.Description); } } - - [Theory] - - // Continuation byte as first byte of code point - [InlineData(new byte[] { 0x48, 0x65 }, new byte[] { 0x80, 0x6C, 0x6F })] - [InlineData(new byte[] { 0x48, 0x65 }, new byte[] { 0x99, 0x6C, 0x6F })] - [InlineData(new byte[] { 0x48, 0x65 }, new byte[] { 0xAB, 0x6C, 0x6F })] - [InlineData(new byte[] { 0x48, 0x65 }, new byte[] { 0xB0, 0x6C, 0x6F })] - - // Incomplete Code Point - [InlineData(new byte[] { 0xC2 }, new byte[0])] - [InlineData(new byte[] { 0xE0 }, new byte[0])] - [InlineData(new byte[] { 0xE0, 0xA0 }, new byte[0])] - [InlineData(new byte[] { 0xE0, 0xA4 }, new byte[0])] - [InlineData(new byte[] { 0xF0, 0x90, 0x80 }, new byte[0])] - - // Overlong Encoding - - // 'H' (1 byte char) encoded with 2, 3 and 4 bytes - [InlineData(new byte[] { 0xC1 }, new byte[] { 0x88 })] - [InlineData(new byte[] { 0xE0 }, new byte[] { 0x81, 0x88 })] - [InlineData(new byte[] { 0xF0 }, new byte[] { 0x80, 0x81, 0x88 })] - - // '§' (2 byte char) encoded with 3 and 4 bytes - [InlineData(new byte[] { 0xE0, 0x82 }, new byte[] { 0xA7 })] - [InlineData(new byte[] { 0xF0, 0x80 }, new byte[] { 0x82, 0xA7 })] - - // '\u0800' (3 byte char) encoded with 4 bytes - [InlineData(new byte[] { 0xF0, 0x80 }, new byte[] { 0xA0, 0x80 })] - public async Task InvalidMultiFramePayloads(byte[] payload1, byte[] payload2) - { - using (var pair = WebSocketPair.Create()) - { - var timeoutToken = TestUtil.CreateTimeoutToken(); - using (timeoutToken.Register(() => pair.Dispose())) - { - var server = pair.ServerSocket.ExecuteAndCaptureFramesAsync(); - var client = pair.ClientSocket.ExecuteAndCaptureFramesAsync(); - - var frame = new WebSocketFrame( - endOfMessage: false, - opcode: WebSocketOpcode.Text, - payload: ReadableBuffer.Create(payload1)); - await pair.ClientSocket.SendAsync(frame).OrTimeout(); - frame = new WebSocketFrame( - endOfMessage: true, - opcode: WebSocketOpcode.Continuation, - payload: ReadableBuffer.Create(payload2)); - await pair.ClientSocket.SendAsync(frame).OrTimeout(); - var clientSummary = await client.OrTimeout(); - var serverSummary = await server.OrTimeout(); - - Assert.Equal(1, serverSummary.Received.Count); - Assert.False(serverSummary.Received[0].EndOfMessage); - Assert.Equal(WebSocketOpcode.Text, serverSummary.Received[0].Opcode); - Assert.Equal(payload1, serverSummary.Received[0].Payload.ToArray()); - - Assert.Equal(0, clientSummary.Received.Count); - Assert.Equal(WebSocketCloseStatus.InvalidPayloadData, clientSummary.CloseResult.Status); - Assert.Equal("An invalid Text frame payload was received", clientSummary.CloseResult.Description); - } - } - } } } }