Example #1
0
        public async Task EventQueueTimeout()
        {
            using (StartLog(out var loggerFactory))
            {
                var logger = loggerFactory.CreateLogger <HttpConnectionTests>();

                var testTransport = new TestTransport();

                await WithConnectionAsync(
                    CreateConnection(transport : testTransport),
                    async (connection, closed) =>
                {
                    var onReceived = new SyncPoint();
                    connection.OnReceived(_ => onReceived.WaitToContinue().OrTimeout());

                    logger.LogInformation("Starting connection");
                    await connection.StartAsync().OrTimeout();
                    logger.LogInformation("Started connection");

                    await testTransport.Application.Output.WriteAsync(new byte[] { 1 });
                    await onReceived.WaitForSyncPoint().OrTimeout();

                    // Dispose should complete, even though the receive callbacks are completely blocked up.
                    logger.LogInformation("Disposing connection");
                    await connection.DisposeAsync().OrTimeout(TimeSpan.FromSeconds(10));
                    logger.LogInformation("Disposed connection");

                    // Clear up blocked tasks.
                    onReceived.Continue();
                });
            }
        }
            public async Task CanCancelStartingConnectionAfterNegotiate()
            {
                using (StartVerifiableLog())
                {
                    // Set up a SyncPoint within Negotiate, so we can verify
                    // that the call has gotten that far
                    var negotiateSyncPoint = new SyncPoint();
                    var testHttpHandler    = new TestHttpMessageHandler(autoNegotiate: false);
                    testHttpHandler.OnNegotiate(async(request, cancellationToken) =>
                    {
                        // Wait here for the test code to cancel the "outer" token
                        await negotiateSyncPoint.WaitToContinue().OrTimeout();

                        // Cancel
                        cancellationToken.ThrowIfCancellationRequested();

                        return(ResponseUtils.CreateResponse(HttpStatusCode.OK));
                    });

                    await WithConnectionAsync(
                        CreateConnection(testHttpHandler),
                        async (connection) =>
                    {
                        // Kick off StartAsync, but don't wait for it
                        var cts       = new CancellationTokenSource();
                        var startTask = connection.StartAsync(cts.Token);

                        // Wait for the connection to get to the "WaitToContinue" call above,
                        // which means it has gotten to Negotiate
                        await negotiateSyncPoint.WaitForSyncPoint().OrTimeout();

                        // Assert that StartAsync has not yet been canceled
                        Assert.False(startTask.IsCanceled);

                        // Cancel StartAsync, then "release" the SyncPoint
                        // so the negotiate handler can keep going
                        cts.Cancel();
                        negotiateSyncPoint.Continue();

                        // Assert that StartAsync was canceled
                        await Assert.ThrowsAsync <OperationCanceledException>(() => startTask).OrTimeout();
                    });
                }
            }
Example #3
0
        public async Task EventsAreNotRunningOnMainLoop()
        {
            var testTransport = new TestTransport();

            await WithConnectionAsync(
                CreateConnection(transport : testTransport),
                async (connection, closed) =>
            {
                // Block up the OnReceived callback until we finish the test.
                var onReceived = new SyncPoint();
                connection.OnReceived(_ => onReceived.WaitToContinue().OrTimeout());

                await connection.StartAsync().OrTimeout();

                // This will trigger the received callback
                await testTransport.Application.Output.WriteAsync(new byte[] { 1 });

                // Wait to hit the sync point. We are now blocking up the TaskQueue
                await onReceived.WaitForSyncPoint().OrTimeout();

                // Now we write something else and we want to test that the HttpConnection receive loop is still
                // removing items from the channel even though OnReceived is blocked up.
                await testTransport.Application.Output.WriteAsync(new byte[] { 1 });

                // Now that we've written, we wait for WaitToReadAsync to return an INCOMPLETE task. It will do so
                // once HttpConnection reads the message. We also use a CTS to timeout in case the loop is indeed blocked
                var cts = new CancellationTokenSource();
                cts.CancelAfter(TimeSpan.FromSeconds(5));
                while (testTransport.Application.Input.WaitToReadAsync().IsCompleted&& !cts.IsCancellationRequested)
                {
                    // Yield to allow the HttpConnection to dequeue the message
                    await Task.Yield();
                }

                // If we exited because we were cancelled, throw.
                cts.Token.ThrowIfCancellationRequested();

                // We're free! Unblock onreceived
                onReceived.Continue();
            });
        }
        public async Task SSETransportCancelsSendOnStop()
        {
            var eventStreamTcs = new TaskCompletionSource <object>();
            var copyToAsyncTcs = new TaskCompletionSource <object>();
            var sendSyncPoint  = new SyncPoint();

            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 <Stream, int, CancellationToken>(async(stream, bufferSize, t) =>
                    {
                        await copyToAsyncTcs.Task;

                        throw new TaskCanceledException();
                    });
                    mockStream.Setup(s => s.CanRead).Returns(true);
                    return(new HttpResponseMessage {
                        Content = new StreamContent(mockStream.Object)
                    });
                }

                // Throw TaskCanceledException from SSE send's SendAsync on stop
                cancellationToken.Register(s => ((SyncPoint)s).Continue(), sendSyncPoint);
                await sendSyncPoint.WaitToContinue();
                throw new TaskCanceledException();
            });

            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;

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

                    // For send request to be in progress
                    await sendSyncPoint.WaitForSyncPoint();

                    var stopTask = sseTransport.StopAsync();

                    copyToAsyncTcs.SetResult(null);
                    sendSyncPoint.Continue();

                    await stopTask;
                }
        }