// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Threading.Tasks.Channels; using Microsoft.Extensions.Logging; using Moq; using Moq.Protected; using Xunit; using Microsoft.AspNetCore.Sockets.Internal; using Microsoft.AspNetCore.SignalR.Tests.Common; namespace Microsoft.AspNetCore.Sockets.Client.Tests { public class LongPollingTransportTests { [Fact] public async Task LongPollingTransportStopsPollAndSendLoopsWhenTransportDisposed() { var mockHttpHandler = new Mock(); mockHttpHandler.Protected() .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) .Returns(async (request, cancellationToken) => { await Task.Yield(); return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(string.Empty) }; }); Task transportActiveTask; using (var httpClient = new HttpClient(mockHttpHandler.Object)) using (var longPollingTransport = new LongPollingTransport(httpClient, new LoggerFactory())) { var connectionToTransport = Channel.CreateUnbounded(); var transportToConnection = Channel.CreateUnbounded(); var channelConnection = new ChannelConnection(connectionToTransport, transportToConnection); await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection); transportActiveTask = longPollingTransport.Running; Assert.False(transportActiveTask.IsCompleted); } await transportActiveTask.OrTimeout(); } [Fact] public async Task LongPollingTransportStopsWhenPollReceives204() { var mockHttpHandler = new Mock(); mockHttpHandler.Protected() .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) .Returns(async (request, cancellationToken) => { await Task.Yield(); return new HttpResponseMessage(HttpStatusCode.NoContent) { Content = new StringContent(string.Empty) }; }); using (var httpClient = new HttpClient(mockHttpHandler.Object)) using (var longPollingTransport = new LongPollingTransport(httpClient, new LoggerFactory())) { var connectionToTransport = Channel.CreateUnbounded(); var transportToConnection = Channel.CreateUnbounded(); var channelConnection = new ChannelConnection(connectionToTransport, transportToConnection); await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection); await longPollingTransport.Running.OrTimeout(); Assert.True(transportToConnection.In.Completion.IsCompleted); } } [Fact] public async Task LongPollingTransportStopsWhenPollRequestFails() { var mockHttpHandler = new Mock(); mockHttpHandler.Protected() .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) .Returns(async (request, cancellationToken) => { await Task.Yield(); return new HttpResponseMessage(HttpStatusCode.InternalServerError) { Content = new StringContent(string.Empty) }; }); using (var httpClient = new HttpClient(mockHttpHandler.Object)) using (var longPollingTransport = new LongPollingTransport(httpClient, new LoggerFactory())) { var connectionToTransport = Channel.CreateUnbounded(); var transportToConnection = Channel.CreateUnbounded(); var channelConnection = new ChannelConnection(connectionToTransport, transportToConnection); await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection); var exception = await Assert.ThrowsAsync(async () => await transportToConnection.In.Completion.OrTimeout()); Assert.Contains(" 500 ", exception.Message); } } [Fact] public async Task LongPollingTransportStopsWhenSendRequestFails() { var mockHttpHandler = new Mock(); mockHttpHandler.Protected() .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) .Returns(async (request, cancellationToken) => { await Task.Yield(); var statusCode = request.RequestUri.AbsolutePath.EndsWith("send") ? HttpStatusCode.InternalServerError : HttpStatusCode.OK; return new HttpResponseMessage(statusCode) { Content = new StringContent(string.Empty) }; }); using (var httpClient = new HttpClient(mockHttpHandler.Object)) using (var longPollingTransport = new LongPollingTransport(httpClient, new LoggerFactory())) { var connectionToTransport = Channel.CreateUnbounded(); var transportToConnection = Channel.CreateUnbounded(); var channelConnection = new ChannelConnection(connectionToTransport, transportToConnection); await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection); await connectionToTransport.Out.WriteAsync(new Message()); await Assert.ThrowsAsync(async () => await longPollingTransport.Running.OrTimeout()); // The channel needs to be drained for the Completion task to be completed while (transportToConnection.In.TryRead(out Message message)) { message.Dispose(); } var exception = await Assert.ThrowsAsync(async () => await transportToConnection.In.Completion); Assert.Contains(" 500 ", exception.Message); } } [Fact] public async Task LongPollingTransportShutsDownWhenChannelIsClosed() { var mockHttpHandler = new Mock(); mockHttpHandler.Protected() .Setup>("SendAsync", ItExpr.IsAny(), ItExpr.IsAny()) .Returns(async (request, cancellationToken) => { await Task.Yield(); return new HttpResponseMessage(HttpStatusCode.OK) { Content = new StringContent(string.Empty) }; }); using (var httpClient = new HttpClient(mockHttpHandler.Object)) using (var longPollingTransport = new LongPollingTransport(httpClient, new LoggerFactory())) { var connectionToTransport = Channel.CreateUnbounded(); var transportToConnection = Channel.CreateUnbounded(); var channelConnection = new ChannelConnection(connectionToTransport, transportToConnection); await longPollingTransport.StartAsync(new Uri("http://fakeuri.org"), channelConnection); connectionToTransport.Out.Complete(); await longPollingTransport.Running.OrTimeout(); await longPollingTransport.Running.OrTimeout(); await connectionToTransport.In.Completion.OrTimeout(); } } } }