Esempio n. 1
0
        public async Task SSETransportDoesNotSupportBinary()
        {
            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) =>
            {
                await Task.Yield();
                return(new HttpResponseMessage {
                    Content = new StringContent(string.Empty)
                });
            });

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
                using (StartVerifiableLog(out var loggerFactory))
                {
                    var sseTransport = new ServerSentEventsTransport(httpClient, loggerFactory);

                    var ex = await Assert.ThrowsAsync <ArgumentException>(() => sseTransport.StartAsync(new Uri("http://fakeuri.org"), TransferFormat.Binary).OrTimeout());

                    Assert.Equal("transferFormat", ex.ParamName);
                    Assert.Equal($"The 'Binary' transfer format is not supported by this transport.", ex.GetLocalizationSafeMessage());
                }
        }
Esempio n. 2
0
        [InlineData((TransferFormat)42)]                          // Unexpected value
        public async Task SSETransportThrowsForInvalidTransferFormat(TransferFormat transferFormat)
        {
            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) =>
            {
                await Task.Yield();
                return(new HttpResponseMessage {
                    Content = new StringContent(string.Empty)
                });
            });

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
                using (StartVerifiableLog(out var loggerFactory, $"{nameof(SSETransportThrowsForInvalidTransferFormat)}_{transferFormat}"))
                {
                    var sseTransport = new ServerSentEventsTransport(httpClient, loggerFactory);
                    var exception    = await Assert.ThrowsAsync <ArgumentException>(() =>
                                                                                    sseTransport.StartAsync(new Uri("http://fakeuri.org"), transferFormat));

                    Assert.Contains($"The '{transferFormat}' transfer format is not supported by this transport.", exception.Message);
                    Assert.Equal("transferFormat", exception.ParamName);
                }
        }
        public async Task SSETransportThrowsForInvalidTransferMode()
        {
            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) =>
            {
                await Task.Yield();
                return(new HttpResponseMessage {
                    Content = new StringContent(string.Empty)
                });
            });

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
            {
                var sseTransport          = new ServerSentEventsTransport(httpClient);
                var connectionToTransport = Channel.CreateUnbounded <SendMessage>();
                var transportToConnection = Channel.CreateUnbounded <byte[]>();
                var channelConnection     = new ChannelConnection <SendMessage, byte[]>(connectionToTransport, transportToConnection);
                var exception             = await Assert.ThrowsAsync <ArgumentException>(() =>
                                                                                         sseTransport.StartAsync(new Uri("http://fakeuri.org"), null, TransferMode.Text | TransferMode.Binary, connectionId: string.Empty, connection: Mock.Of <IConnection>()));

                Assert.Contains("Invalid transfer mode.", exception.Message);
                Assert.Equal("requestedTransferMode", exception.ParamName);
            }
        }
Esempio n. 4
0
        public async Task SSETransportStopsIfTheServerClosesTheStream()
        {
            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) =>
            {
                await Task.Yield();
                return(new HttpResponseMessage {
                    Content = new StringContent("data: 3:abc\r\n\r\n")
                });
            });

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
                using (StartVerifiableLog(out var loggerFactory))
                {
                    var sseTransport = new ServerSentEventsTransport(httpClient, loggerFactory);

                    await sseTransport.StartAsync(
                        new Uri("http://fakeuri.org"), TransferFormat.Text).OrTimeout();

                    var message = await sseTransport.Input.ReadSingleAsync().OrTimeout();

                    Assert.Equal("3:abc", Encoding.ASCII.GetString(message));

                    await sseTransport.Running.OrTimeout();
                }
        }
        public async Task SSETransportSetsTransferMode(TransferMode transferMode)
        {
            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) =>
            {
                await Task.Yield();
                return(new HttpResponseMessage {
                    Content = new StringContent(string.Empty)
                });
            });

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
            {
                var sseTransport          = new ServerSentEventsTransport(httpClient);
                var connectionToTransport = Channel.CreateUnbounded <SendMessage>();
                var transportToConnection = Channel.CreateUnbounded <byte[]>();
                var channelConnection     = new ChannelConnection <SendMessage, byte[]>(connectionToTransport, transportToConnection);
                Assert.Null(sseTransport.Mode);
                await sseTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection, transferMode, connectionId : string.Empty, connection : Mock.Of <IConnection>()).OrTimeout();

                Assert.Equal(TransferMode.Text, sseTransport.Mode);
                await sseTransport.StopAsync().OrTimeout();
            }
        }
        public async Task SSETransportStopsIfTheServerClosesTheStream()
        {
            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) =>
            {
                await Task.Yield();
                return(new HttpResponseMessage {
                    Content = new StringContent("data: 3:abc\r\n\r\n")
                });
            });

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
            {
                var sseTransport = new ServerSentEventsTransport(httpClient);

                var connectionToTransport = Channel.CreateUnbounded <SendMessage>();
                var transportToConnection = Channel.CreateUnbounded <byte[]>();
                var channelConnection     = new ChannelConnection <SendMessage, byte[]>(connectionToTransport, transportToConnection);
                await sseTransport.StartAsync(
                    new Uri("http://fakeuri.org"), channelConnection, TransferMode.Text, connectionId : string.Empty, connection : Mock.Of <IConnection>()).OrTimeout();

                var message = await transportToConnection.Reader.ReadAsync().AsTask().OrTimeout();

                Assert.Equal("3:abc", Encoding.ASCII.GetString(message));

                await sseTransport.Running.OrTimeout();
            }
        }
            public async Task SSECanBeCanceled()
            {
                bool ExpectedErrors(WriteContext writeContext)
                {
                    return(writeContext.LoggerName == typeof(HttpConnection).FullName &&
                           writeContext.EventId.Name == "ErrorStartingTransport");
                }

                using (StartVerifiableLog(expectedErrorsFilter: ExpectedErrors))
                {
                    var httpHandler = new TestHttpMessageHandler();
                    httpHandler.OnGet("/?id=00000000-0000-0000-0000-000000000000", (_, __) =>
                    {
                        // Simulating a cancellationToken canceling this request.
                        throw new OperationCanceledException("Cancel SSE Start.");
                    });

                    var sse = new ServerSentEventsTransport(new HttpClient(httpHandler), LoggerFactory);

                    await WithConnectionAsync(
                        CreateConnection(httpHandler, loggerFactory : LoggerFactory, transport : sse, transportType : HttpTransportType.ServerSentEvents),
                        async (connection) =>
                    {
                        var ex = await Assert.ThrowsAsync <AggregateException>(async() => await connection.StartAsync()).DefaultTimeout();
                    });
                }
            }
            public async Task SSEWaitsForResponseToStart()
            {
                using (StartVerifiableLog())
                {
                    var httpHandler = new TestHttpMessageHandler();

                    var connectResponseTcs = new TaskCompletionSource();
                    httpHandler.OnGet("/?id=00000000-0000-0000-0000-000000000000", async(_, __) =>
                    {
                        await connectResponseTcs.Task;
                        return(ResponseUtils.CreateResponse(HttpStatusCode.Accepted));
                    });

                    var sse = new ServerSentEventsTransport(new HttpClient(httpHandler), LoggerFactory);

                    await WithConnectionAsync(
                        CreateConnection(httpHandler, loggerFactory : LoggerFactory, transport : sse),
                        async (connection) =>
                    {
                        var startTask = connection.StartAsync();
                        Assert.False(connectResponseTcs.Task.IsCompleted);
                        Assert.False(startTask.IsCompleted);
                        connectResponseTcs.TrySetResult();
                        await startTask.DefaultTimeout();
                    });
                }
            }
            public async Task SSEWontStartIfSuccessfulConnectionIsNotEstablished()
            {
                bool ExpectedErrors(WriteContext writeContext)
                {
                    return(writeContext.LoggerName == typeof(HttpConnection).FullName &&
                           writeContext.EventId.Name == "ErrorStartingTransport");
                }

                using (StartVerifiableLog(expectedErrorsFilter: ExpectedErrors))
                {
                    var httpHandler = new TestHttpMessageHandler();

                    httpHandler.OnGet("/?id=00000000-0000-0000-0000-000000000000", (_, __) =>
                    {
                        return(Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.InternalServerError)));
                    });

                    var sse = new ServerSentEventsTransport(new HttpClient(httpHandler), LoggerFactory);

                    await WithConnectionAsync(
                        CreateConnection(httpHandler, loggerFactory : LoggerFactory, transport : sse),
                        async (connection) =>
                    {
                        await Assert.ThrowsAsync <AggregateException>(
                            () => connection.StartAsync().DefaultTimeout());
                    });
                }
            }
        public async Task SSETransportStopsWithErrorIfSendingMessageFails()
        {
            bool ExpectedErrors(WriteContext writeContext)
            {
                return(writeContext.LoggerName == typeof(ServerSentEventsTransport).FullName &&
                       writeContext.EventId.Name == "ErrorSending");
            }

            var eventStreamTcs = new TaskCompletionSource <object>();
            var copyToAsyncTcs = new TaskCompletionSource <int>();

            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) =>
            {
                await Task.Yield();

                if (request.Headers.Accept?.Contains(new MediaTypeWithQualityHeaderValue("text/event-stream")) == true)
                {
                    // Receive loop started - allow stopping the transport
                    eventStreamTcs.SetResult(null);

                    // returns unfinished task to block pipelines
                    var mockStream = new Mock <Stream>();
                    mockStream
                    .Setup(s => s.CopyToAsync(It.IsAny <Stream>(), It.IsAny <int>(), It.IsAny <CancellationToken>()))
                    .Returns(copyToAsyncTcs.Task);
                    mockStream.Setup(s => s.CanRead).Returns(true);
                    return(new HttpResponseMessage {
                        Content = new StreamContent(mockStream.Object)
                    });
                }

                return(ResponseUtils.CreateResponse(System.Net.HttpStatusCode.InternalServerError));
            });

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
                using (StartVerifiableLog(out var loggerFactory, expectedErrorsFilter: ExpectedErrors))
                {
                    var sseTransport = new ServerSentEventsTransport(httpClient, loggerFactory);

                    await sseTransport.StartAsync(
                        new Uri("http://fakeuri.org"), TransferFormat.Text).OrTimeout();

                    await eventStreamTcs.Task;

                    await sseTransport.Output.WriteAsync(new byte[] { 0x42 });

                    var exception = await Assert.ThrowsAsync <HttpRequestException>(() => sseTransport.Input.ReadAllAsync().OrTimeout());

                    Assert.Contains("500", exception.Message);

                    // Errors are only communicated through the pipe
                    await sseTransport.Running.OrTimeout();
                }
        }
Esempio n. 11
0
        public async Task SSETransportStopsSendAndReceiveLoopsWhenTransportStopped()
        {
            var eventStreamCts  = new CancellationTokenSource();
            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>((request, cancellationToken) =>
            {
                var mockStream = new Mock <Stream>();
                mockStream
                .Setup(s => s.CopyToAsync(It.IsAny <Stream>(), It.IsAny <int>(), It.IsAny <CancellationToken>()))
                .Returns <Stream, int, CancellationToken>(async(stream, bufferSize, t) =>
                {
                    await Task.Yield();
                    var buffer = Encoding.ASCII.GetBytes("data: 3:abc\r\n\r\n");
                    while (!eventStreamCts.IsCancellationRequested)
                    {
                        await stream.WriteAsync(buffer, 0, buffer.Length).OrTimeout();
                    }
                });
                mockStream.Setup(s => s.CanRead).Returns(true);

                return(Task.FromResult(new HttpResponseMessage {
                    Content = new StreamContent(mockStream.Object)
                }));
            });

            Task transportActiveTask;

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
            {
                var sseTransport = new ServerSentEventsTransport(httpClient);

                try
                {
                    var connectionToTransport = Channel.CreateUnbounded <SendMessage>();
                    var transportToConnection = Channel.CreateUnbounded <byte[]>();
                    var channelConnection     = new ChannelConnection <SendMessage, byte[]>(connectionToTransport, transportToConnection);
                    await sseTransport.StartAsync(
                        new Uri("http://fakeuri.org"), channelConnection, TransferMode.Text, connection : Mock.Of <IConnection>()).OrTimeout();

                    transportActiveTask = sseTransport.Running;
                    Assert.False(transportActiveTask.IsCompleted);
                    var message = await transportToConnection.Reader.ReadAsync().AsTask().OrTimeout();

                    Assert.Equal("3:abc", Encoding.ASCII.GetString(message));
                }
                finally
                {
                    await sseTransport.StopAsync().OrTimeout();
                }

                await transportActiveTask.OrTimeout();

                eventStreamCts.Cancel();
            }
        }
        public async Task SSETransportStopsWithErrorIfSendingMessageFails()
        {
            var eventStreamTcs = new TaskCompletionSource <object>();
            var copyToAsyncTcs = new TaskCompletionSource <int>();

            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) =>
            {
                await Task.Yield();

                if (request.Headers.Accept?.Contains(new MediaTypeWithQualityHeaderValue("text/event-stream")) == true)
                {
                    // Receive loop started - allow stopping the transport
                    eventStreamTcs.SetResult(null);

                    // returns unfinished task to block pipelines
                    var mockStream = new Mock <Stream>();
                    mockStream
                    .Setup(s => s.CopyToAsync(It.IsAny <Stream>(), It.IsAny <int>(), It.IsAny <CancellationToken>()))
                    .Returns(copyToAsyncTcs.Task);
                    mockStream.Setup(s => s.CanRead).Returns(true);
                    return(new HttpResponseMessage {
                        Content = new StreamContent(mockStream.Object)
                    });
                }

                return(ResponseUtils.CreateResponse(System.Net.HttpStatusCode.InternalServerError));
            });

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
            {
                var sseTransport = new ServerSentEventsTransport(httpClient);

                var connectionToTransport = Channel.CreateUnbounded <SendMessage>();
                var transportToConnection = Channel.CreateUnbounded <byte[]>();
                var channelConnection     = new ChannelConnection <SendMessage, byte[]>(connectionToTransport, transportToConnection);

                await sseTransport.StartAsync(
                    new Uri("http://fakeuri.org"), channelConnection, TransferMode.Text, connectionId : string.Empty).OrTimeout();

                await eventStreamTcs.Task;

                var sendTcs = new TaskCompletionSource <object>();
                Assert.True(connectionToTransport.Writer.TryWrite(new SendMessage(new byte[] { 0x42 }, sendTcs)));

                var exception = await Assert.ThrowsAsync <HttpRequestException>(() => sendTcs.Task.OrTimeout());

                Assert.Contains("500", exception.Message);

                Assert.Same(exception, await Assert.ThrowsAsync <HttpRequestException>(() => sseTransport.Running.OrTimeout()));
            }
        }
Esempio n. 13
0
        public async Task SSETransportStopsSendAndReceiveLoopsWhenTransportStopped()
        {
            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>((request, cancellationToken) =>
            {
                var mockStream = new Mock <Stream>();
                mockStream
                .Setup(s => s.ReadAsync(It.IsAny <Memory <byte> >(), It.IsAny <CancellationToken>()))
                .Returns <Memory <byte>, CancellationToken>(async(data, t) =>
                {
                    if (t.IsCancellationRequested)
                    {
                        return(0);
                    }

                    int count = Encoding.ASCII.GetBytes("data: 3:abc\r\n\r\n", data.Span);
                    await Task.Delay(100);
                    return(count);
                });
                mockStream.Setup(s => s.CanRead).Returns(true);

                return(Task.FromResult(new HttpResponseMessage {
                    Content = new StreamContent(mockStream.Object)
                }));
            });

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
                using (StartVerifiableLog())
                {
                    var sseTransport = new ServerSentEventsTransport(httpClient, LoggerFactory);

                    Task transportActiveTask;
                    try
                    {
                        await sseTransport.StartAsync(
                            new Uri("http://fakeuri.org"), TransferFormat.Text).DefaultTimeout();

                        transportActiveTask = sseTransport.Running;
                        Assert.False(transportActiveTask.IsCompleted);
                        var message = await sseTransport.Input.ReadSingleAsync().DefaultTimeout();

                        Assert.StartsWith("3:abc", Encoding.ASCII.GetString(message));
                    }
                    finally
                    {
                        await sseTransport.StopAsync().DefaultTimeout();
                    }

                    await transportActiveTask.DefaultTimeout();
                }
        }
Esempio n. 14
0
        /// <summary>
        /// Protocol negotiation finished successfully.
        /// </summary>
        private void OnNegotiationDataReceived(NegotiationData data)
        {
            // Find out what supported protocol the server speak
            int protocolIdx = -1;

            for (int i = 0; i < ClientProtocols.Length && protocolIdx == -1; ++i)
            {
                if (data.ProtocolVersion == ClientProtocols[i])
                {
                    protocolIdx = i;
                }
            }

            // No supported protocol found? Try using the latest one.
            if (protocolIdx == -1)
            {
                protocolIdx = (byte)ProtocolVersions.Protocol_2_2;
                HTTPManager.Logger.Warning("SignalR Connection", "Unknown protocol version: " + data.ProtocolVersion);
            }

            this.Protocol = (ProtocolVersions)protocolIdx;

#if !BESTHTTP_DISABLE_WEBSOCKET
            if (data.TryWebSockets)
            {
                Transport = new WebSocketTransport(this);

                #if !BESTHTTP_DISABLE_SERVERSENT_EVENTS
                NextProtocolToTry = SupportedProtocols.ServerSentEvents;
                #else
                NextProtocolToTry = SupportedProtocols.HTTP;
                #endif
            }
            else
#endif
            {
                #if !BESTHTTP_DISABLE_SERVERSENT_EVENTS
                Transport = new ServerSentEventsTransport(this);

                // Long-Poll
                NextProtocolToTry = SupportedProtocols.HTTP;
                #else
                Transport = new PollingTransport(this);

                NextProtocolToTry = SupportedProtocols.Unknown;
                #endif
            }

            this.State = ConnectionStates.Connecting;
            TransportConnectionStartedAt = DateTime.UtcNow;

            Transport.Connect();
        }
Esempio n. 15
0
        public async Task SSETransportStopsSendAndReceiveLoopsWhenTransportStopped()
        {
            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>((request, cancellationToken) =>
            {
                var mockStream = new Mock <Stream>();
                mockStream
                .Setup(s => s.CopyToAsync(It.IsAny <Stream>(), It.IsAny <int>(), It.IsAny <CancellationToken>()))
                .Returns <Stream, int, CancellationToken>(async(stream, bufferSize, t) =>
                {
                    var buffer = Encoding.ASCII.GetBytes("data: 3:abc\r\n\r\n");
                    while (!t.IsCancellationRequested)
                    {
                        await stream.WriteAsync(buffer, 0, buffer.Length).OrTimeout();
                        await Task.Delay(100);
                    }
                });
                mockStream.Setup(s => s.CanRead).Returns(true);

                return(Task.FromResult(new HttpResponseMessage {
                    Content = new StreamContent(mockStream.Object)
                }));
            });

            Task transportActiveTask;

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
            {
                var sseTransport = new ServerSentEventsTransport(httpClient);

                try
                {
                    await sseTransport.StartAsync(
                        new Uri("http://fakeuri.org"), TransferFormat.Text).OrTimeout();

                    transportActiveTask = sseTransport.Running;
                    Assert.False(transportActiveTask.IsCompleted);
                    var message = await sseTransport.Input.ReadSingleAsync().OrTimeout();

                    Assert.StartsWith("3:abc", Encoding.ASCII.GetString(message));
                }
                finally
                {
                    await sseTransport.StopAsync().OrTimeout();
                }

                await transportActiveTask.OrTimeout();
            }
        }
Esempio n. 16
0
        public async Task SSESetsContentType()
        {
            var channel = Channel.CreateUnbounded <byte[]>();
            var context = new DefaultHttpContext();
            var sse     = new ServerSentEventsTransport(channel, connectionId: string.Empty, loggerFactory: new LoggerFactory());

            Assert.True(channel.Writer.TryComplete());

            await sse.ProcessRequestAsync(context, context.RequestAborted);

            Assert.Equal("text/event-stream", context.Response.ContentType);
            Assert.Equal("no-cache", context.Response.Headers["Cache-Control"]);
        }
Esempio n. 17
0
        /// <summary>
        /// Try to fall back to next transport. If no more transport to try, it will return false.
        /// </summary>
        private bool TryFallbackTransport()
        {
            if (this.State == ConnectionStates.Connecting)
            {
                if (BufferedMessages != null)
                {
                    BufferedMessages.Clear();
                }

                // stop the current transport
                Transport.Stop();
                Transport = null;

                switch (NextProtocolToTry)
                {
#if !BESTHTTP_DISABLE_WEBSOCKET
                case SupportedProtocols.WebSocket:
                    Transport = new WebSocketTransport(this);
                    break;
#endif

#if !BESTHTTP_DISABLE_SERVERSENT_EVENTS
                case SupportedProtocols.ServerSentEvents:
                    Transport         = new ServerSentEventsTransport(this);
                    NextProtocolToTry = SupportedProtocols.HTTP;
                    break;
#endif

                case SupportedProtocols.HTTP:
                    Transport         = new PollingTransport(this);
                    NextProtocolToTry = SupportedProtocols.Unknown;
                    break;

                case SupportedProtocols.Unknown:
                    return(false);
                }

                TransportConnectionStartedAt = DateTime.UtcNow;

                Transport.Connect();

                if (PingRequest != null)
                {
                    PingRequest.Abort();
                }

                return(true);
            }

            return(false);
        }
        public async Task CanStartStopSSETransport()
        {
            var eventStreamTcs = new TaskCompletionSource <object>();
            var copyToAsyncTcs = new TaskCompletionSource <int>();

            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) =>
            {
                await Task.Yield();
                // Receive loop started - allow stopping the transport
                eventStreamTcs.SetResult(null);

                // returns unfinished task to block pipelines
                var mockStream = new Mock <Stream>();
                mockStream
                .Setup(s => s.CopyToAsync(It.IsAny <Stream>(), It.IsAny <int>(), It.IsAny <CancellationToken>()))
                .Returns(copyToAsyncTcs.Task);
                mockStream.Setup(s => s.CanRead).Returns(true);
                return(new HttpResponseMessage {
                    Content = new StreamContent(mockStream.Object)
                });
            });

            try
            {
                using (var httpClient = new HttpClient(mockHttpHandler.Object))
                {
                    var sseTransport          = new ServerSentEventsTransport(httpClient);
                    var connectionToTransport = Channel.CreateUnbounded <SendMessage>();
                    var transportToConnection = Channel.CreateUnbounded <byte[]>();
                    var channelConnection     = new ChannelConnection <SendMessage, byte[]>(connectionToTransport, transportToConnection);
                    await sseTransport.StartAsync(
                        new Uri("http://fakeuri.org"), channelConnection, TransferMode.Text, connectionId : string.Empty).OrTimeout();

                    await eventStreamTcs.Task.OrTimeout();

                    await sseTransport.StopAsync().OrTimeout();

                    await sseTransport.Running.OrTimeout();
                }
            }
            finally
            {
                copyToAsyncTcs.SetResult(0);
            }
        }
Esempio n. 19
0
        public async Task SSESetsContentType()
        {
            var pair       = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
            var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application);
            var context    = new DefaultHttpContext();

            var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: string.Empty, loggerFactory: new LoggerFactory());

            connection.Transport.Output.Complete();

            await sse.ProcessRequestAsync(context, context.RequestAborted);

            Assert.Equal("text/event-stream", context.Response.ContentType);
            Assert.Equal("no-cache", context.Response.Headers["Cache-Control"]);
        }
Esempio n. 20
0
        public async Task SSETurnsResponseBufferingOff()
        {
            var channel = Channel.CreateUnbounded <byte[]>();
            var context = new DefaultHttpContext();
            var feature = new HttpBufferingFeature();

            context.Features.Set <IHttpBufferingFeature>(feature);
            var sse = new ServerSentEventsTransport(channel, connectionId: string.Empty, loggerFactory: new LoggerFactory());

            Assert.True(channel.Writer.TryComplete());

            await sse.ProcessRequestAsync(context, context.RequestAborted);

            Assert.True(feature.ResponseBufferingDisabled);
        }
Esempio n. 21
0
        public async Task SSETransportStopsIfChannelClosed()
        {
            var eventStreamTcs = new TaskCompletionSource();
            var readTcs        = new TaskCompletionSource <int>();

            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) =>
            {
                await Task.Yield();

                // Receive loop started - allow stopping the transport
                eventStreamTcs.SetResult();

                // returns unfinished task to block pipelines
                var mockStream = new Mock <Stream>();
                mockStream
                .Setup(s => s.ReadAsync(It.IsAny <Memory <byte> >(), It.IsAny <CancellationToken>()))
                .Returns <Memory <byte>, CancellationToken>(async(data, ct) =>
                {
                    using (ct.Register(() => readTcs.TrySetCanceled()))
                    {
                        return(await readTcs.Task);
                    }
                });
                mockStream.Setup(s => s.CanRead).Returns(true);
                return(new HttpResponseMessage {
                    Content = new StreamContent(mockStream.Object)
                });
            });

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
                using (StartVerifiableLog())
                {
                    var sseTransport = new ServerSentEventsTransport(httpClient, LoggerFactory);

                    await sseTransport.StartAsync(
                        new Uri("http://fakeuri.org"), TransferFormat.Text).DefaultTimeout();

                    await eventStreamTcs.Task.DefaultTimeout();

                    sseTransport.Output.Complete();

                    await sseTransport.Running.DefaultTimeout();
                }
        }
        public async Task CanStartStopSSETransport()
        {
            var eventStreamTcs = new TaskCompletionSource <object>();
            var copyToAsyncTcs = new TaskCompletionSource <int>();

            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) =>
            {
                await Task.Yield();
                // Receive loop started - allow stopping the transport
                eventStreamTcs.SetResult(null);

                // returns unfinished task to block pipelines
                var mockStream = new Mock <Stream>();
                mockStream
                .Setup(s => s.CopyToAsync(It.IsAny <Stream>(), It.IsAny <int>(), It.IsAny <CancellationToken>()))
                .Returns(copyToAsyncTcs.Task);
                mockStream.Setup(s => s.CanRead).Returns(true);
                return(new HttpResponseMessage {
                    Content = new StreamContent(mockStream.Object)
                });
            });

            try
            {
                using (var httpClient = new HttpClient(mockHttpHandler.Object))
                    using (StartVerifiableLog())
                    {
                        var sseTransport = new ServerSentEventsTransport(httpClient, LoggerFactory);
                        await sseTransport.StartAsync(
                            new Uri("http://fakeuri.org"), TransferFormat.Text).OrTimeout();

                        await eventStreamTcs.Task.OrTimeout();

                        await sseTransport.StopAsync().OrTimeout();

                        await sseTransport.Running.OrTimeout();
                    }
            }
            finally
            {
                copyToAsyncTcs.SetResult(0);
            }
        }
Esempio n. 23
0
        public async Task SSETurnsResponseBufferingOff()
        {
            var pair       = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
            var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application);
            var context    = new DefaultHttpContext();

            var feature = new HttpBufferingFeature();

            context.Features.Set <IHttpBufferingFeature>(feature);
            var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: connection.ConnectionId, loggerFactory: new LoggerFactory());

            connection.Transport.Output.Complete();

            await sse.ProcessRequestAsync(context, context.RequestAborted);

            Assert.True(feature.ResponseBufferingDisabled);
        }
Esempio n. 24
0
        public async Task SSEAddsAppropriateFraming(string message, string expected)
        {
            var channel = Channel.CreateUnbounded <byte[]>();
            var context = new DefaultHttpContext();
            var sse     = new ServerSentEventsTransport(channel, connectionId: string.Empty, loggerFactory: new LoggerFactory());
            var ms      = new MemoryStream();

            context.Response.Body = ms;

            await channel.Writer.WriteAsync(Encoding.UTF8.GetBytes(message));

            Assert.True(channel.Writer.TryComplete());

            await sse.ProcessRequestAsync(context, context.RequestAborted);

            Assert.Equal(expected, Encoding.UTF8.GetString(ms.ToArray()));
        }
Esempio n. 25
0
        public async Task SSETransportStopsWithErrorIfServerSendsIncompleteResults()
        {
            var mockHttpHandler = new Mock <HttpMessageHandler>();
            var calls           = 0;

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) =>
            {
                await Task.Yield();

                var mockStream = new Mock <Stream>();
                mockStream
                .Setup(s => s.ReadAsync(It.IsAny <Memory <byte> >(), It.IsAny <CancellationToken>()))
                .Returns <Memory <byte>, CancellationToken>((data, t) =>
                {
                    if (calls == 0)
                    {
                        calls++;
                        return(new ValueTask <int>(Encoding.ASCII.GetBytes("data: 3:a", data.Span)));
                    }
                    return(new ValueTask <int>(0));
                });
                mockStream.Setup(s => s.CanRead).Returns(true);

                return(new HttpResponseMessage {
                    Content = new StreamContent(mockStream.Object)
                });
            });

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
                using (StartVerifiableLog())
                {
                    var sseTransport = new ServerSentEventsTransport(httpClient, LoggerFactory);

                    await sseTransport.StartAsync(
                        new Uri("http://fakeuri.org"), TransferFormat.Text).DefaultTimeout();

                    var exception = await Assert.ThrowsAsync <FormatException>(() => sseTransport.Input.ReadAllAsync());

                    await sseTransport.Running.DefaultTimeout();

                    Assert.Equal("Incomplete message.", exception.Message);
                }
        }
Esempio n. 26
0
        public async Task SSETransportStopsIfChannelClosed()
        {
            var eventStreamTcs = new TaskCompletionSource <object>();
            var copyToAsyncTcs = new TaskCompletionSource <int>();

            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) =>
            {
                await Task.Yield();

                // Receive loop started - allow stopping the transport
                eventStreamTcs.SetResult(null);

                // returns unfinished task to block pipelines
                var mockStream = new Mock <Stream>();
                mockStream
                .Setup(s => s.CopyToAsync(It.IsAny <Stream>(), It.IsAny <int>(), It.IsAny <CancellationToken>()))
                .Returns(copyToAsyncTcs.Task);
                mockStream.Setup(s => s.CanRead).Returns(true);
                return(new HttpResponseMessage {
                    Content = new StreamContent(mockStream.Object)
                });
            });

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
            {
                var sseTransport = new ServerSentEventsTransport(httpClient);

                var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);

                await sseTransport.StartAsync(
                    new Uri("http://fakeuri.org"), pair.Application, TransferFormat.Text, connection : Mock.Of <IConnection>()).OrTimeout();

                await eventStreamTcs.Task.OrTimeout();

                pair.Transport.Output.Complete();

                await sseTransport.Running.OrTimeout();
            }
        }
Esempio n. 27
0
        public async Task SSEAddsAppropriateFraming(string message, string expected)
        {
            var pair       = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
            var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application);
            var context    = new DefaultHttpContext();

            var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: string.Empty, loggerFactory: new LoggerFactory());
            var ms  = new MemoryStream();

            context.Response.Body = ms;

            await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes(message));

            connection.Transport.Output.Complete();

            await sse.ProcessRequestAsync(context, context.RequestAborted);

            Assert.Equal(expected, Encoding.UTF8.GetString(ms.ToArray()));
        }
            public async Task SSEWontStartIfSuccessfulConnectionIsNotEstablished()
            {
                // TODO: Add logging https://github.com/aspnet/SignalR/issues/2879
                var httpHandler = new TestHttpMessageHandler();

                httpHandler.OnGet("/?id=00000000-0000-0000-0000-000000000000", (_, __) =>
                {
                    return(Task.FromResult(ResponseUtils.CreateResponse(HttpStatusCode.InternalServerError)));
                });

                var sse = new ServerSentEventsTransport(new HttpClient(httpHandler));

                await WithConnectionAsync(
                    CreateConnection(httpHandler, transport : sse),
                    async (connection) =>
                {
                    await Assert.ThrowsAsync <InvalidOperationException>(
                        () => connection.StartAsync(TransferFormat.Text).OrTimeout());
                });
            }
Esempio n. 29
0
        public async Task SSEWritesMessages()
        {
            var pair       = DuplexPipe.CreateConnectionPair(PipeOptions.Default, new PipeOptions(readerScheduler: PipeScheduler.Inline));
            var connection = new DefaultConnectionContext("foo", pair.Transport, pair.Application);
            var context    = new DefaultHttpContext();

            var ms = new MemoryStream();

            context.Response.Body = ms;
            var sse = new ServerSentEventsTransport(connection.Application.Input, connectionId: string.Empty, loggerFactory: new LoggerFactory());

            var task = sse.ProcessRequestAsync(context, context.RequestAborted);

            await connection.Transport.Output.WriteAsync(Encoding.ASCII.GetBytes("Hello"));

            connection.Transport.Output.Complete();
            await task.OrTimeout();

            Assert.Equal(":\r\ndata: Hello\r\n\r\n", Encoding.ASCII.GetString(ms.ToArray()));
        }
        public async Task SSETransportStopsWithErrorIfServerSendsIncompleteResults()
        {
            var mockHttpHandler = new Mock <HttpMessageHandler>();

            mockHttpHandler.Protected()
            .Setup <Task <HttpResponseMessage> >("SendAsync", ItExpr.IsAny <HttpRequestMessage>(), ItExpr.IsAny <CancellationToken>())
            .Returns <HttpRequestMessage, CancellationToken>(async(request, cancellationToken) =>
            {
                await Task.Yield();

                var mockStream = new Mock <Stream>();
                mockStream
                .Setup(s => s.CopyToAsync(It.IsAny <Stream>(), It.IsAny <int>(), It.IsAny <CancellationToken>()))
                .Returns <Stream, int, CancellationToken>(async(stream, bufferSize, t) =>
                {
                    var buffer = Encoding.ASCII.GetBytes("data: 3:a");
                    await stream.WriteAsync(buffer, 0, buffer.Length);
                });
                mockStream.Setup(s => s.CanRead).Returns(true);

                return(new HttpResponseMessage {
                    Content = new StreamContent(mockStream.Object)
                });
            });

            using (var httpClient = new HttpClient(mockHttpHandler.Object))
            {
                var sseTransport = new ServerSentEventsTransport(httpClient);

                var connectionToTransport = Channel.CreateUnbounded <SendMessage>();
                var transportToConnection = Channel.CreateUnbounded <byte[]>();
                var channelConnection     = new ChannelConnection <SendMessage, byte[]>(connectionToTransport, transportToConnection);
                await sseTransport.StartAsync(
                    new Uri("http://fakeuri.org"), channelConnection, TransferMode.Text, connectionId : string.Empty).OrTimeout();

                var exception = await Assert.ThrowsAsync <FormatException>(() => sseTransport.Running.OrTimeout());

                Assert.Equal("Incomplete message.", exception.Message);
            }
        }