// 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.Http; using System.Threading.Tasks; using Microsoft.AspNetCore.Sockets; using Microsoft.AspNetCore.Sockets.Client; using Microsoft.AspNetCore.Sockets.Client.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; namespace Microsoft.AspNetCore.SignalR.Client.Tests { public partial class HttpConnectionTests { private static HttpConnection CreateConnection(HttpMessageHandler httpHandler = null, ILoggerFactory loggerFactory = null, string url = null, ITransport transport = null) { loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; var httpOptions = new HttpOptions() { HttpMessageHandler = httpHandler ?? TestHttpMessageHandler.CreateDefault(), }; var uri = new Uri(url ?? "http://fakeuri.org/"); var connection = (transport != null) ? new HttpConnection(uri, new TestTransportFactory(transport), loggerFactory, httpOptions) : new HttpConnection(uri, TransportType.LongPolling, loggerFactory, httpOptions); return connection; } private static async Task WithConnectionAsync(HttpConnection connection, Func body) { try { var closedTcs = new TaskCompletionSource(); connection.Closed += ex => { if (ex != null) { closedTcs.SetException(ex); } else { closedTcs.SetResult(null); } }; // Using OrTimeout here will hide any timeout issues in the test :(. await body(connection, closedTcs.Task); } finally { await connection.DisposeAsync().OrTimeout(); } } // Possibly useful as a general-purpose async testing helper? private class SyncPoint { private TaskCompletionSource _atSyncPoint = new TaskCompletionSource(); private TaskCompletionSource _continueFromSyncPoint = new TaskCompletionSource(); /// /// Waits for the code-under-test to reach . /// /// public Task WaitForSyncPoint() => _atSyncPoint.Task; /// /// Releases the code-under-test to continue past where it waited for . /// public void Continue() => _continueFromSyncPoint.TrySetResult(null); /// /// Used by the code-under-test to wait for the test code to sync up. /// /// /// This code will unblock and then block waiting for to be called. /// /// public Task WaitToContinue() { _atSyncPoint.TrySetResult(null); return _continueFromSyncPoint.Task; } public static Func Create(out SyncPoint syncPoint) { var handler = Create(1, out var syncPoints); syncPoint = syncPoints[0]; return handler; } /// /// Creates a re-entrant function that waits for sync points in sequence. /// /// The number of sync points to expect /// The objects that can be used to coordinate the sync point /// public static Func Create(int count, out SyncPoint[] syncPoints) { // Need to use a local so the closure can capture it. You can't use out vars in a closure. var localSyncPoints = new SyncPoint[count]; for (var i = 0; i < count; i += 1) { localSyncPoints[i] = new SyncPoint(); } syncPoints = localSyncPoints; var counter = 0; return () => { if (counter >= localSyncPoints.Length) { return Task.CompletedTask; } else { var syncPoint = localSyncPoints[counter]; counter += 1; return syncPoint.WaitToContinue(); } }; } } } }