public async ETVoid StartRecv() { if (this.IsDisposed) { return; } try { while (true) { try { #if SERVER ValueWebSocketReceiveResult receiveResult = await this.webSocket.ReceiveAsync(new Memory <byte>(this.recvStream.GetBuffer(), 0, this.recvStream.Capacity), cancellationTokenSource.Token); #else WebSocketReceiveResult receiveResult = await this.webSocket.ReceiveAsync(new ArraySegment <byte>(this.recvStream.GetBuffer(), 0, this.recvStream.Capacity), cancellationTokenSource.Token); #endif if (this.IsDisposed) { return; } if (receiveResult.MessageType == WebSocketMessageType.Close) { this.OnError(ErrorCode.ERR_WebsocketPeerReset); return; } if (receiveResult.Count > ushort.MaxValue) { await this.webSocket.CloseAsync(WebSocketCloseStatus.MessageTooBig, $"message too big: {receiveResult.Count}", cancellationTokenSource.Token); this.OnError(ErrorCode.ERR_WebsocketMessageTooBig); return; } this.recvStream.SetLength(receiveResult.Count); this.OnRead(this.recvStream); } catch (Exception) { this.OnError(ErrorCode.ERR_WebsocketRecvError); return; } } } catch (Exception e) { Log.Error(e); } }
public async Task Duplex(bool clientContextTakover, bool serverContextTakover) { WebSocketTestStream stream = new(); using WebSocket server = WebSocket.CreateFromStream(stream, new WebSocketCreationOptions { IsServer = true, DangerousDeflateOptions = new WebSocketDeflateOptions { ClientContextTakeover = clientContextTakover, ServerContextTakeover = serverContextTakover } }); using WebSocket client = WebSocket.CreateFromStream(stream.Remote, new WebSocketCreationOptions { DangerousDeflateOptions = new WebSocketDeflateOptions { ClientContextTakeover = clientContextTakover, ServerContextTakeover = serverContextTakover } }); var buffer = new byte[1024]; for (var i = 0; i < 10; ++i) { string message = $"Sending number {i} from server."; await SendTextAsync(message, server, disableCompression : i % 2 == 0); ValueWebSocketReceiveResult result = await client.ReceiveAsync(buffer.AsMemory(), CancellationToken); Assert.True(result.EndOfMessage); Assert.Equal(WebSocketMessageType.Text, result.MessageType); Assert.Equal(message, Encoding.UTF8.GetString(buffer.AsSpan(0, result.Count))); } for (var i = 0; i < 10; ++i) { string message = $"Sending number {i} from client."; await SendTextAsync(message, client, disableCompression : i % 2 == 0); ValueWebSocketReceiveResult result = await server.ReceiveAsync(buffer.AsMemory(), CancellationToken); Assert.True(result.EndOfMessage); Assert.Equal(WebSocketMessageType.Text, result.MessageType); Assert.Equal(message, Encoding.UTF8.GetString(buffer.AsSpan(0, result.Count))); } }
public async Task SendReceiveWithDifferentWindowBits(int clientWindowBits, int serverWindowBits) { WebSocketTestStream stream = new(); using WebSocket server = WebSocket.CreateFromStream(stream, new WebSocketCreationOptions { IsServer = true, DangerousDeflateOptions = new WebSocketDeflateOptions() { ClientContextTakeover = false, ClientMaxWindowBits = clientWindowBits, ServerContextTakeover = false, ServerMaxWindowBits = serverWindowBits } }); using WebSocket client = WebSocket.CreateFromStream(stream.Remote, new WebSocketCreationOptions { DangerousDeflateOptions = new WebSocketDeflateOptions() { ClientContextTakeover = false, ClientMaxWindowBits = clientWindowBits, ServerContextTakeover = false, ServerMaxWindowBits = serverWindowBits } }); Memory <byte> data = new byte[64 * 1024]; Memory <byte> buffer = new byte[data.Length]; new Random(0).NextBytes(data.Span.Slice(0, data.Length / 2)); await server.SendAsync(data, WebSocketMessageType.Binary, true, CancellationToken); ValueWebSocketReceiveResult result = await client.ReceiveAsync(buffer, CancellationToken); Assert.Equal(data.Length, result.Count); Assert.True(result.EndOfMessage); Assert.True(data.Span.SequenceEqual(buffer.Span)); buffer.Span.Clear(); await client.SendAsync(data, WebSocketMessageType.Binary, true, CancellationToken); result = await server.ReceiveAsync(buffer, CancellationToken); Assert.Equal(data.Length, result.Count); Assert.True(result.EndOfMessage); Assert.True(data.Span.SequenceEqual(buffer.Span)); }
private async Task <(WebSocketMessageType type, GatewayPacket packet)> DeserializeMultipleBuffer( ClientWebSocket socket, IMemoryOwner <byte> buf, ValueWebSocketReceiveResult res) { await using var stream = new MemoryStream(BufferSize * 4); stream.Write(buf.Memory.Span.Slice(0, res.Count)); while (!res.EndOfMessage) { res = await socket.ReceiveAsync(buf.Memory, default); stream.Write(buf.Memory.Span.Slice(0, res.Count)); } return(DeserializeObject(res, stream.GetBuffer().AsSpan(0, (int)stream.Length))); }
private async ValueTask <FlushResult> WriteNextMessageAsync(CancellationToken cancellationToken = default) { ValueWebSocketReceiveResult readResult = default; while (!readResult.EndOfMessage) { var memory = ReceiverWriter.GetMemory(); readResult = await _websocketClient.ReceiveAsync(memory, cancellationToken); ReceiverWriter.Advance(readResult.Count); } return(await ReceiverWriter.FlushAsync(cancellationToken)); }
public static async ValueTask ReceiveFullyAsync(this WebSocket ws, Memory <byte> buffer, CancellationToken token = default) { while (true) { ValueWebSocketReceiveResult result = await ws.ReceiveAsync(buffer, token); if (result.Count == buffer.Length) { return; } if (result.EndOfMessage) { throw new EndOfStreamException(); } buffer = buffer[result.Count..];
private static void LogFrame(ILogger logger, WebSocket webSocket, ValueWebSocketReceiveResult frame, byte[] buffer) { var close = frame.MessageType == WebSocketMessageType.Close; string message; if (close) { message = $"Close: {webSocket.CloseStatus.Value} {webSocket.CloseStatusDescription}"; } else { string content = "<<binary>>"; if (frame.MessageType == WebSocketMessageType.Text) { content = Encoding.UTF8.GetString(buffer, 0, frame.Count); } message = $"{frame.MessageType}: Len={frame.Count}, Fin={frame.EndOfMessage}: {content}"; } logger.LogDebug("Received Frame " + message); }
public async Task ReceiveUncompressedMessageWhenCompressionEnabled() { // We should be able to handle the situation where even if we have // deflate compression enabled, uncompressed messages are OK WebSocketTestStream stream = new(); using WebSocket server = WebSocket.CreateFromStream(stream, new WebSocketCreationOptions { IsServer = true, DangerousDeflateOptions = null }); using WebSocket client = WebSocket.CreateFromStream(stream.Remote, new WebSocketCreationOptions { DangerousDeflateOptions = new WebSocketDeflateOptions() }); // Server sends uncompressed await SendTextAsync("Hello", server); // Although client has deflate options, it should still be able // to handle uncompressed messages. Assert.Equal("Hello", await ReceiveTextAsync(client)); // Client sends compressed, but server compression is disabled and should throw on receive await SendTextAsync("Hello back", client); var exception = await Assert.ThrowsAsync <WebSocketException>(() => ReceiveTextAsync(server)); Assert.Equal("The WebSocket received compressed frame when compression is not enabled.", exception.Message); Assert.Equal(WebSocketState.Aborted, server.State); // The client should close if we try to receive ValueWebSocketReceiveResult result = await client.ReceiveAsync(Memory <byte> .Empty, CancellationToken); Assert.Equal(WebSocketMessageType.Close, result.MessageType); Assert.Equal(WebSocketCloseStatus.ProtocolError, client.CloseStatus); Assert.Equal(WebSocketState.CloseReceived, client.State); }
private Task WrapWriterAsync(CancellationToken cancellationToken) { return(Task.Run( async() => { try { while (true) { Memory <byte> memory = Writer.GetMemory(_options.SizeHint); ValueWebSocketReceiveResult readResult = await InternalWebSocket.ReceiveAsync(memory, cancellationToken); if (readResult.Count is 0) { break; } Writer.Advance(readResult.Count); FlushResult flushResult = await Writer.FlushAsync(cancellationToken); if (flushResult.IsCompleted) { break; } } await Writer.CompleteAsync(); } catch (Exception ex) { await Writer.CompleteAsync(ex); } }, cancellationToken )); }
private void HandleCloseMessage(ValueWebSocketReceiveResult receiveResult) { _closeStatus = WebSocketCloseStatus.NormalClosure; }
private (WebSocketMessageType type, GatewayPacket packet) DeserializeObject(ValueWebSocketReceiveResult res, Span <byte> span) { var packet = JsonSerializer.Deserialize <GatewayPacket>(span, _jsonSerializerOptions) !; return(res.MessageType, packet); }
public async Task AutobahnTestCase13_3_1() { // When running Autobahn Test Suite some tests failed with zlib error "invalid distance too far back". // Further investigation lead to a bug fix in zlib intel's implementation - https://github.com/dotnet/runtime/issues/50235. // This test replicates one of the Autobahn tests to make sure this issue doesn't appear again. byte[][] messages = new[] { new byte[] { 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x22, 0x41, 0x75, 0x74, 0x6F, 0x62, 0x61, 0x68, 0x6E, 0x50, 0x79 }, new byte[] { 0x74, 0x68, 0x6F, 0x6E, 0x2F, 0x30, 0x2E, 0x36, 0x2E, 0x30, 0x22, 0x3A, 0x20, 0x7B, 0x0A, 0x20 }, new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x31, 0x2E, 0x31, 0x2E, 0x31, 0x22, 0x3A, 0x20, 0x7B, 0x0A }, new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69 }, new byte[] { 0x6F, 0x72, 0x22, 0x3A, 0x20, 0x22, 0x4F, 0x4B, 0x22, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20 }, new byte[] { 0x20, 0x20, 0x20, 0x20, 0x22, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6F, 0x72, 0x43, 0x6C, 0x6F }, new byte[] { 0x73, 0x65, 0x22, 0x3A, 0x20, 0x22, 0x4F, 0x4B, 0x22, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20 }, new byte[] { 0x20, 0x20, 0x20, 0x20, 0x22, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x22, 0x3A, 0x20 }, new byte[] { 0x32, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x72, 0x65, 0x6D }, new byte[] { 0x6F, 0x74, 0x65, 0x43, 0x6C, 0x6F, 0x73, 0x65, 0x43, 0x6F, 0x64, 0x65, 0x22, 0x3A, 0x20, 0x31 }, new byte[] { 0x30, 0x30, 0x30, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x72 }, new byte[] { 0x65, 0x70, 0x6F, 0x72, 0x74, 0x66, 0x69, 0x6C, 0x65, 0x22, 0x3A, 0x20, 0x22, 0x61, 0x75, 0x74 }, new byte[] { 0x6F, 0x62, 0x61, 0x68, 0x6E, 0x70, 0x79, 0x74, 0x68, 0x6F, 0x6E, 0x5F, 0x30, 0x5F, 0x36, 0x5F }, new byte[] { 0x30, 0x5F, 0x63, 0x61, 0x73, 0x65, 0x5F, 0x31, 0x5F, 0x31, 0x5F, 0x31, 0x2E, 0x6A, 0x73, 0x6F }, new byte[] { 0x6E, 0x22, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20 }, new byte[] { 0x20, 0x20, 0x22, 0x31, 0x2E, 0x31, 0x2E, 0x32, 0x22, 0x3A, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20 }, new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6F, 0x72, 0x22 }, new byte[] { 0x3A, 0x20, 0x22, 0x4F, 0x4B, 0x22, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }, new byte[] { 0x20, 0x22, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6F, 0x72, 0x43, 0x6C, 0x6F, 0x73, 0x65, 0x22 }, new byte[] { 0x3A, 0x20, 0x22, 0x4F, 0x4B, 0x22, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }, new byte[] { 0x20, 0x22, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x22, 0x3A, 0x20, 0x32, 0x2C, 0x0A }, new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x72, 0x65, 0x6D, 0x6F, 0x74, 0x65 }, new byte[] { 0x43, 0x6C, 0x6F, 0x73, 0x65, 0x43, 0x6F, 0x64, 0x65, 0x22, 0x3A, 0x20, 0x31, 0x30, 0x30, 0x30 }, new byte[] { 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x72, 0x65, 0x70, 0x6F }, new byte[] { 0x72, 0x74, 0x66, 0x69, 0x6C, 0x65, 0x22, 0x3A, 0x20, 0x22, 0x61, 0x75, 0x74, 0x6F, 0x62, 0x61 }, new byte[] { 0x68, 0x6E, 0x70, 0x79, 0x74, 0x68, 0x6F, 0x6E, 0x5F, 0x30, 0x5F, 0x36, 0x5F, 0x30, 0x5F, 0x63 }, new byte[] { 0x61, 0x73, 0x65, 0x5F, 0x31, 0x5F, 0x31, 0x5F, 0x32, 0x2E, 0x6A, 0x73, 0x6F, 0x6E, 0x22, 0x0A }, new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x7D, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22 }, new byte[] { 0x31, 0x2E, 0x31, 0x2E, 0x33, 0x22, 0x3A, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }, new byte[] { 0x20, 0x20, 0x20, 0x22, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6F, 0x72, 0x22, 0x3A, 0x20, 0x22 }, new byte[] { 0x4F, 0x4B, 0x22, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x62 }, new byte[] { 0x65, 0x68, 0x61, 0x76, 0x69, 0x6F, 0x72, 0x43, 0x6C, 0x6F, 0x73, 0x65, 0x22, 0x3A, 0x20, 0x22 }, new byte[] { 0x4F, 0x4B, 0x22, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x64 }, new byte[] { 0x75, 0x72, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x22, 0x3A, 0x20, 0x32, 0x2C, 0x0A, 0x20, 0x20, 0x20 }, new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x72, 0x65, 0x6D, 0x6F, 0x74, 0x65, 0x43, 0x6C, 0x6F }, new byte[] { 0x73, 0x65, 0x43, 0x6F, 0x64, 0x65, 0x22, 0x3A, 0x20, 0x31, 0x30, 0x30, 0x30, 0x2C, 0x0A, 0x20 }, new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x72, 0x65, 0x70, 0x6F, 0x72, 0x74, 0x66 }, new byte[] { 0x69, 0x6C, 0x65, 0x22, 0x3A, 0x20, 0x22, 0x61, 0x75, 0x74, 0x6F, 0x62, 0x61, 0x68, 0x6E, 0x70 }, new byte[] { 0x79, 0x74, 0x68, 0x6F, 0x6E, 0x5F, 0x30, 0x5F, 0x36, 0x5F, 0x30, 0x5F, 0x63, 0x61, 0x73, 0x65 }, new byte[] { 0x5F, 0x31, 0x5F, 0x31, 0x5F, 0x33, 0x2E, 0x6A, 0x73, 0x6F, 0x6E, 0x22, 0x0A, 0x20, 0x20, 0x20 }, new byte[] { 0x20, 0x20, 0x20, 0x7D, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x31, 0x2E, 0x31 }, new byte[] { 0x2E, 0x34, 0x22, 0x3A, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 }, new byte[] { 0x22, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6F, 0x72, 0x22, 0x3A, 0x20, 0x22, 0x4F, 0x4B, 0x22 }, new byte[] { 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x62, 0x65, 0x68, 0x61 }, new byte[] { 0x76, 0x69, 0x6F, 0x72, 0x43, 0x6C, 0x6F, 0x73, 0x65, 0x22, 0x3A, 0x20, 0x22, 0x4F, 0x4B, 0x22 }, new byte[] { 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x64, 0x75, 0x72, 0x61 }, new byte[] { 0x74, 0x69, 0x6F, 0x6E, 0x22, 0x3A, 0x20, 0x32, 0x2C, 0x0A, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 } }; WebSocketTestStream stream = new(); using WebSocket server = WebSocket.CreateFromStream(stream, new WebSocketCreationOptions { IsServer = true, KeepAliveInterval = TimeSpan.Zero, DangerousDeflateOptions = new WebSocketDeflateOptions() { ClientMaxWindowBits = 9, ServerMaxWindowBits = 9 } }); using WebSocket client = WebSocket.CreateFromStream(stream.Remote, new WebSocketCreationOptions { KeepAliveInterval = TimeSpan.Zero, DangerousDeflateOptions = new WebSocketDeflateOptions() { ClientMaxWindowBits = 9, ServerMaxWindowBits = 9 } }); foreach (var message in messages) { await server.SendAsync(message, WebSocketMessageType.Text, true, CancellationToken); } Memory <byte> buffer = new byte[32]; for (int i = 0; i < messages.Length; ++i) { ValueWebSocketReceiveResult result = await client.ReceiveAsync(buffer, CancellationToken); Assert.True(result.EndOfMessage); Assert.Equal(messages[i].Length, result.Count); Assert.True(buffer.Span.Slice(0, result.Count).SequenceEqual(messages[i])); } }
public async Task LargeMessageSplitInMultipleFrames(int windowBits) { WebSocketTestStream stream = new(); using WebSocket server = WebSocket.CreateFromStream(stream, new WebSocketCreationOptions { IsServer = true, DangerousDeflateOptions = new WebSocketDeflateOptions() { ClientMaxWindowBits = windowBits } }); using WebSocket client = WebSocket.CreateFromStream(stream.Remote, new WebSocketCreationOptions { DangerousDeflateOptions = new WebSocketDeflateOptions() { ClientMaxWindowBits = windowBits } }); Memory <byte> testData = new byte[ushort.MaxValue]; Memory <byte> receivedData = new byte[testData.Length]; // Make the data incompressible to make sure that the output is larger than the input var rng = new Random(0); rng.NextBytes(testData.Span); // Test it a few times with different frame sizes for (var i = 0; i < 10; ++i) { var frameSize = rng.Next(1024, 2048); var position = 0; while (position < testData.Length) { var currentFrameSize = Math.Min(frameSize, testData.Length - position); var eof = position + currentFrameSize == testData.Length; await server.SendAsync(testData.Slice(position, currentFrameSize), WebSocketMessageType.Binary, eof, CancellationToken); position += currentFrameSize; } Assert.True(testData.Length < stream.Remote.Available, "The compressed data should be bigger."); Assert.Equal(testData.Length, position); // Receive the data from the client side receivedData.Span.Clear(); position = 0; // Intentionally receive with a frame size that is less than what the sender used frameSize /= 3; while (true) { int currentFrameSize = Math.Min(frameSize, testData.Length - position); ValueWebSocketReceiveResult result = await client.ReceiveAsync(receivedData.Slice(position, currentFrameSize), CancellationToken); Assert.Equal(WebSocketMessageType.Binary, result.MessageType); position += result.Count; if (result.EndOfMessage) { break; } } Assert.Equal(0, stream.Remote.Available); Assert.Equal(testData.Length, position); Assert.True(testData.Span.SequenceEqual(receivedData.Span)); } }
internal Task Receive(WebSocket socket, ValueWebSocketReceiveResult result, byte[] buffer) { throw new NotImplementedException(); }