public override async Task ConnectAsync() { await base.ConnectAsync(); if (_webSocketTransport != null) { _webSocketTransport.OnTextReceived -= OnTextReceived; _webSocketTransport.OnBinaryReceived -= OnBinaryReceived; _webSocketTransport.OnAborted -= OnAborted; _webSocketTransport.Dispose(); } Uri uri = UriConverter.GetServerUri(true, ServerUri, EIO, Options.Path, Options.Query); _clientWebSocket = ClientWebSocketProvider(); if (Options.ExtraHeaders != null) { foreach (var item in Options.ExtraHeaders) { _clientWebSocket.SetRequestHeader(item.Key, item.Value); } } _webSocketTransport = new WebSocketTransport(_clientWebSocket, EIO) { ConnectionTimeout = Options.ConnectionTimeout }; _webSocketTransport.OnTextReceived = OnTextReceived; _webSocketTransport.OnBinaryReceived = OnBinaryReceived; _webSocketTransport.OnAborted = OnAborted; Debug.WriteLine($"[Websocket] Connecting"); await _webSocketTransport.ConnectAsync(uri).ConfigureAwait(false); Debug.WriteLine($"[Websocket] Connected"); }
public void ClientTransportCanSendMessages() { var logger = XUnitLogger.CreateLogger(_testOutput); using (var connection = new TestWebSocketConnectionFeature()) { var server = connection.AcceptAsync().GetAwaiter().GetResult(); var client = connection.Client; var receiverRunning = ReceiveAsync(server); var fromTransport = new Pipe(PipeOptions.Default); var toTransport = new Pipe(PipeOptions.Default); var sut = new WebSocketTransport(client, new DuplexPipe(toTransport.Reader, fromTransport.Writer), logger); var clientTransportRunning = sut.ConnectAsync(CancellationToken.None); var messages = new List <byte[]> { Encoding.UTF8.GetBytes("foo") }; WriteAsync(toTransport.Writer, messages).Wait(); toTransport.Writer.CompleteAsync().GetAwaiter().GetResult(); clientTransportRunning.Wait(); var output = receiverRunning.GetAwaiter().GetResult(); Assert.Equal("foo", Encoding.UTF8.GetString(output[0])); } }
public async Task ServerTransportCanReceiveMessages() { var logger = XUnitLogger.CreateLogger(_testOutput); using (var connection = new TestWebSocketConnectionFeature()) { var server = connection.AcceptAsync(); var client = connection.Client; var fromTransport = new Pipe(PipeOptions.Default); var toTransport = new Pipe(PipeOptions.Default); var listenerRunning = ListenAsync(fromTransport.Reader); var webSocketManager = new Mock <WebSocketManager>(); webSocketManager.Setup(m => m.AcceptWebSocketAsync()).Returns(server); var httpContext = new Mock <HttpContext>(); httpContext.Setup(c => c.WebSockets).Returns(webSocketManager.Object); var sut = new WebSocketTransport(httpContext.Object.WebSockets.AcceptWebSocketAsync().GetAwaiter().GetResult(), new DuplexPipe(toTransport.Reader, fromTransport.Writer), logger); var serverTransportRunning = sut.ConnectAsync(CancellationToken.None); var messages = new List <byte[]> { Encoding.UTF8.GetBytes("foo"), Encoding.UTF8.GetBytes("bar") }; SendBinaryAsync(client, messages).Wait(); client.CloseAsync(WebSocketCloseStatus.NormalClosure, "Done sending.", CancellationToken.None).Wait(); serverTransportRunning.Wait(); var output = await listenerRunning; Assert.Equal("foo", Encoding.UTF8.GetString(output[0])); Assert.Equal("bar", Encoding.UTF8.GetString(output[1])); } }
public void ServerTransportCanSendMessages() { var logger = XUnitLogger.CreateLogger(_testOutput); using (var connection = new TestWebSocketConnectionFeature()) { var server = connection.AcceptAsync().GetAwaiter().GetResult(); var client = connection.Client; var receiverRunning = ReceiveAsync(client); var fromTransport = new Pipe(PipeOptions.Default); var toTransport = new Pipe(PipeOptions.Default); var webSocketManager = new Mock <WebSocketManager>(); webSocketManager.Setup(m => m.AcceptWebSocketAsync()).Returns(Task.FromResult(server)); var httpContext = new Mock <HttpContext>(); httpContext.Setup(c => c.WebSockets).Returns(webSocketManager.Object); var sut = new WebSocketTransport(httpContext.Object.WebSockets.AcceptWebSocketAsync().GetAwaiter().GetResult(), new DuplexPipe(toTransport.Reader, fromTransport.Writer), logger); var serverTransportRunning = sut.ConnectAsync(CancellationToken.None); var messages = new List <byte[]> { Encoding.UTF8.GetBytes("foo") }; WriteAsync(toTransport.Writer, messages).Wait(); toTransport.Writer.CompleteAsync().GetAwaiter().GetResult(); serverTransportRunning.Wait(); var output = receiverRunning.GetAwaiter().GetResult(); Assert.Equal("foo", Encoding.UTF8.GetString(output[0])); } }
public async Task ServerGracefullyClosesWhenClientSendsCloseFrameThenApplicationEnds() { //using (StartVerifiableLog()) { var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); using (var feature = new TestWebSocketConnectionFeature()) { var serverSocket = await feature.AcceptAsync(); var transport = new WebSocketTransport(serverSocket, pair.Application, NullLogger.Instance); // Accept web socket, start receiving / sending at the transport level var processTask = transport.ConnectAsync(CancellationToken.None); // Start a socket client that will capture traffic for posterior analysis var clientTask = feature.Client.ExecuteAndCaptureFramesAsync(); await feature.Client.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None).TimeoutAfterAsync(TimeSpan.FromSeconds(5)); // close the client to server channel pair.Transport.Output.Complete(); _ = await clientTask.TimeoutAfterAsync(TimeSpan.FromSeconds(5)); await processTask.TimeoutAfterAsync(TimeSpan.FromSeconds(5)); Assert.Equal(WebSocketCloseStatus.NormalClosure, serverSocket.CloseStatus); } } }
public async Task TransportFailsOnTimeoutWithErrorWhenApplicationFailsAndClientDoesNotSendCloseFrame() { //using (StartVerifiableLog()) { var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); using (var feature = new TestWebSocketConnectionFeature()) { var serverSocket = await feature.AcceptAsync(); var transport = new WebSocketTransport(serverSocket, pair.Application, NullLogger.Instance); // Accept web socket, start receiving / sending at the transport level var processTask = transport.ConnectAsync(CancellationToken.None); // Start a socket client that will capture traffic for posterior analysis var clientTask = feature.Client.ExecuteAndCaptureFramesAsync(); // fail the client to server channel pair.Transport.Output.Complete(new Exception()); await processTask.TimeoutAfterAsync(TimeSpan.FromSeconds(10)); Assert.Equal(WebSocketState.Aborted, serverSocket.State); } } }
public async Task ClientReceivesInternalServerErrorWhenTheApplicationFails() { //using (StartVerifiableLog()) { var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); using (var feature = new TestWebSocketConnectionFeature()) { var transport = new WebSocketTransport(await feature.AcceptAsync(), pair.Application, NullLogger.Instance); // Accept web socket, start receiving / sending at the transport level var processTask = transport.ConnectAsync(CancellationToken.None); // Start a socket client that will capture traffic for posterior analysis var clientTask = feature.Client.ExecuteAndCaptureFramesAsync(); // Fail in the app pair.Transport.Output.Complete(new InvalidOperationException("Catastrophic failure.")); var clientSummary = await clientTask.TimeoutAfterAsync <WebSocketConnectionSummary>(TimeSpan.FromSeconds(5)); Assert.Equal(WebSocketCloseStatus.InternalServerError, clientSummary.CloseResult.CloseStatus); // Close from the client await feature.Client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); await processTask.TimeoutAfterAsync(TimeSpan.FromSeconds(5)); } } }
public async Task TransportCommunicatesErrorToApplicationWhenClientDisconnectsAbnormally() { //using (StartVerifiableLog()) { var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); using (var feature = new TestWebSocketConnectionFeature()) { async Task CompleteApplicationAfterTransportCompletes() { try { // Wait until the transport completes so that we can end the application var result = await pair.Transport.Input.ReadAsync(); pair.Transport.Input.AdvanceTo(result.Buffer.End); } catch (Exception ex) { Assert.IsType <WebSocketError>(ex); } finally { // Complete the application so that the connection unwinds without aborting pair.Transport.Output.Complete(); } } var transport = new WebSocketTransport(await feature.AcceptAsync(), pair.Application, NullLogger.Instance); // Accept web socket, start receiving / sending at the transport level var processTask = transport.ConnectAsync(CancellationToken.None); // Start a socket client that will capture traffic for posterior analysis var clientTask = feature.Client.ExecuteAndCaptureFramesAsync(); // When the close frame is received, we complete the application so the send // loop unwinds _ = CompleteApplicationAfterTransportCompletes(); // Terminate the client to server channel with an exception feature.Client.SendAbort(); // Wait for the transport await processTask.TimeoutAfterAsync(TimeSpan.FromSeconds(5)); await clientTask.TimeoutAfterAsync(TimeSpan.FromSeconds(5)); } } }
public async Task WebSocketTransport_ClientServer_WhatIsSentIsReceived() { var serverPipePair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); var clientPipePair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); using (var webSocketFeature = new TestWebSocketConnectionFeature()) { // Build server transport var serverTransport = new WebSocketTransport(await webSocketFeature.AcceptAsync(), serverPipePair.Application, NullLogger.Instance); // Accept server web socket, start receiving / sending at the transport level var serverTask = serverTransport.ConnectAsync(CancellationToken.None); var clientTransport = new WebSocketTransport(webSocketFeature.Client, clientPipePair.Application, NullLogger.Instance); var clientTask = clientTransport.ConnectAsync(CancellationToken.None); // Send a frame client -> server await clientPipePair.Transport.Output.WriteAsync(new ArraySegment <byte>(Encoding.UTF8.GetBytes("Hello"))); await clientPipePair.Transport.Output.FlushAsync(); var result = await serverPipePair.Transport.Input.ReadAsync(); var buffer = result.Buffer; Assert.Equal("Hello", Encoding.UTF8.GetString(buffer.ToArray())); serverPipePair.Transport.Input.AdvanceTo(buffer.End); // Send a frame server -> client await serverPipePair.Transport.Output.WriteAsync(new ArraySegment <byte>(Encoding.UTF8.GetBytes("World"))); await serverPipePair.Transport.Output.FlushAsync(); var clientResult = await clientPipePair.Transport.Input.ReadAsync(); buffer = clientResult.Buffer; Assert.Equal("World", Encoding.UTF8.GetString(buffer.ToArray())); clientPipePair.Transport.Input.AdvanceTo(buffer.End); clientPipePair.Transport.Output.Complete(); serverPipePair.Transport.Output.Complete(); // The transport should finish now await serverTask; await clientTask; } }
public async Task WebSocketTransport_WhatIsReceivedIsWritten() { var pipePair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default); using (var webSocketFeature = new TestWebSocketConnectionFeature()) { // Build transport var transport = new WebSocketTransport(await webSocketFeature.AcceptAsync(), pipePair.Application, NullLogger.Instance); // Accept web socket, start receiving / sending at the transport level var processTask = transport.ConnectAsync(CancellationToken.None); // Start a socket client that will capture traffic for posterior analysis var clientTask = webSocketFeature.Client.ExecuteAndCaptureFramesAsync(); // Send a frame, then close await webSocketFeature.Client.SendAsync( buffer : new ArraySegment <byte>(Encoding.UTF8.GetBytes("Hello")), messageType : WebSocketMessageType.Binary, endOfMessage : true, cancellationToken : CancellationToken.None); await webSocketFeature.Client.CloseAsync(WebSocketCloseStatus.NormalClosure, string.Empty, CancellationToken.None); var result = await pipePair.Transport.Input.ReadAsync(); var buffer = result.Buffer; Assert.Equal("Hello", Encoding.UTF8.GetString(buffer.ToArray())); pipePair.Transport.Input.AdvanceTo(buffer.End); pipePair.Transport.Output.Complete(); // The transport should finish now await processTask; // The connection should close after this, which means the client will get a close frame. var clientSummary = await clientTask; Assert.Equal(WebSocketCloseStatus.NormalClosure, clientSummary.CloseResult.CloseStatus); } }