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);
            }
        }