From e4b4476b37d2122f4a4f9d5a539dc465ceb3f63d Mon Sep 17 00:00:00 2001 From: Mikael Mengistu Date: Thu, 8 Aug 2019 20:14:56 -0700 Subject: [PATCH] Add test for canceling start in transport (#12846) --- ...HttpConnectionTests.ConnectionLifecycle.cs | 83 +++++++++++++++++++ .../test/UnitTests/TestHttpMessageHandler.cs | 2 + .../Client/test/UnitTests/TestTransport.cs | 3 + 3 files changed, 88 insertions(+) diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.ConnectionLifecycle.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.ConnectionLifecycle.cs index 142063ab05..1c2280e42d 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.ConnectionLifecycle.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.ConnectionLifecycle.cs @@ -427,6 +427,89 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests } } + [Fact] + public async Task CancellationTokenFromStartPassedToTransport() + { + using (StartVerifiableLog()) + { + var cts = new CancellationTokenSource(); + var httpHandler = new TestHttpMessageHandler(); + + await WithConnectionAsync( + CreateConnection(httpHandler, + transport: new TestTransport(onTransportStart: () => { + // Cancel the token when the transport is starting which will fail the startTask. + cts.Cancel(); + return Task.CompletedTask; + })), + async (connection) => + { + // We aggregate failures that happen when we start the transport. The operation cancelled exception will + // be an inner exception. + var ex = await Assert.ThrowsAsync(async () => await connection.StartAsync(cts.Token)).OrTimeout(); + Assert.Equal(3, ex.InnerExceptions.Count); + var innerEx = ex.InnerExceptions[2]; + var innerInnerEx = innerEx.InnerException; + Assert.IsType(innerInnerEx); + }); + } + } + + [Fact] + 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(async () => await connection.StartAsync()).OrTimeout(); + }); + } + } + + [Fact] + public async Task LongPollingTransportCanBeCanceled() + { + using (StartVerifiableLog()) + { + var cts = new CancellationTokenSource(); + + var httpHandler = new TestHttpMessageHandler(autoNegotiate: false); + httpHandler.OnNegotiate((request, cancellationToken) => + { + // Cancel token so that the first request poll will throw + cts.Cancel(); + return ResponseUtils.CreateResponse(HttpStatusCode.OK, ResponseUtils.CreateNegotiationContent()); + }); + + var lp = new LongPollingTransport(new HttpClient(httpHandler)); + + await WithConnectionAsync( + CreateConnection(httpHandler, transport: lp, transportType: HttpTransportType.LongPolling), + async (connection) => + { + var ex = await Assert.ThrowsAsync(async () => await connection.StartAsync(cts.Token).OrTimeout()); + }); + } + } + private static async Task AssertDisposedAsync(HttpConnection connection) { var exception = diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/TestHttpMessageHandler.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/TestHttpMessageHandler.cs index 75472a4970..06d05da7f5 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/TestHttpMessageHandler.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/TestHttpMessageHandler.cs @@ -46,6 +46,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests var firstPoll = true; OnRequest(async (request, next, cancellationToken) => { + cancellationToken.ThrowIfCancellationRequested(); if (ResponseUtils.IsLongPollRequest(request) && firstPoll) { firstPoll = false; @@ -156,6 +157,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { OnRequest((request, next, cancellationToken) => { + cancellationToken.ThrowIfCancellationRequested(); if (request.Method.Equals(method) && string.Equals(request.RequestUri.PathAndQuery, pathAndQuery)) { return handler(request, cancellationToken); diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/TestTransport.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/TestTransport.cs index ae1249dba7..35847771ae 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/TestTransport.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/TestTransport.cs @@ -44,6 +44,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests Application = pair.Application; await _startHandler(); + // To test canceling the token from the onTransportStart Func. + cancellationToken.ThrowIfCancellationRequested(); + // Start a loop to read from the pipe Receiving = ReceiveLoop(); async Task ReceiveLoop()