227 lines
12 KiB
C#
227 lines
12 KiB
C#
// 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 System.IO.Pipelines;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Xunit;
|
|
|
|
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)
|
|
{
|
|
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: 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(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")]
|
|
|
|
[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[] { 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[] { 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())
|
|
{
|
|
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();
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|