From 30fe3a228871b4f75282be4ca7507dd3db92b1f5 Mon Sep 17 00:00:00 2001 From: Stephen Halter Date: Thu, 27 Jun 2019 15:26:16 -0700 Subject: [PATCH] Adopt Bedrock client abstractions in SignalR client (#11484) --- ...Connections.Abstractions.netstandard2.0.cs | 9 ++ ...Connections.Abstractions.netstandard2.1.cs | 9 ++ .../src/IConnectionFactory.cs | 25 +++++ .../src/IConnectionListenerFactory.cs | 3 - .../src/UriEndPoint.cs | 28 ++++++ ...Core.SignalR.Client.Core.netstandard2.0.cs | 10 +- ...Core.SignalR.Client.Core.netstandard2.1.cs | 10 +- .../csharp/Client.Core/src/HubConnection.cs | 35 ++++--- .../Client.Core/src/HubConnectionBuilder.cs | 12 ++- .../Client.Core/src/IConnectionFactory.cs | 35 ------- ...soft.AspNetCore.SignalR.Client.Core.csproj | 2 +- ...spNetCore.SignalR.Client.netstandard2.0.cs | 7 -- .../Client/src/HttpConnectionFactory.cs | 67 ++++++++++--- .../src/HubConnectionBuilderHttpExtensions.cs | 40 ++++++++ ...Microsoft.AspNetCore.SignalR.Client.csproj | 4 + .../FunctionalTests/HubConnectionTests.cs | 27 ++--- .../HubProtocolVersionTests.cs | 25 +++-- .../UnitTests/HttpConnectionFactoryTests.cs | 98 ++++++++++++++++++- ...HttpConnectionTests.ConnectionLifecycle.cs | 30 +++--- .../UnitTests/HttpConnectionTests.Helpers.cs | 23 +++-- .../HttpConnectionTests.Negotiate.cs | 22 ++--- .../HttpConnectionTests.Transport.cs | 31 ++++-- .../test/UnitTests/HttpConnectionTests.cs | 4 +- .../UnitTests/HubConnectionBuilderTests.cs | 12 +++ .../HubConnectionTests.ConnectionLifecycle.cs | 30 +++--- .../UnitTests/HubConnectionTests.Helpers.cs | 6 +- .../UnitTests/HubConnectionTests.Reconnect.cs | 50 +++++++--- .../test/UnitTests/HubConnectionTests.cs | 5 +- .../Client/test/UnitTests/TestConnection.cs | 2 +- ....Http.Connections.Client.netstandard2.0.cs | 1 + ....Http.Connections.Client.netstandard2.1.cs | 1 + .../src/HttpConnection.cs | 7 +- .../src/HttpConnectionOptions.cs | 8 ++ .../Tests.Utils/DelegateConnectionFactory.cs | 17 +--- .../HubConnectionReceiveBenchmark.cs | 23 ++--- .../HubConnectionSendBenchmark.cs | 10 +- .../HubConnectionStartBenchmark.cs | 10 +- src/SignalR/samples/ClientSample/RawSample.cs | 13 ++- .../samples/ClientSample/Tcp/TcpConnection.cs | 8 +- .../Tcp/TcpHubConnectionBuilderExtensions.cs | 22 ++--- .../server/SignalR/test/EndToEndTests.cs | 43 ++++---- 41 files changed, 527 insertions(+), 297 deletions(-) create mode 100644 src/Servers/Connections.Abstractions/src/IConnectionFactory.cs create mode 100644 src/Servers/Connections.Abstractions/src/UriEndPoint.cs delete mode 100644 src/SignalR/clients/csharp/Client.Core/src/IConnectionFactory.cs diff --git a/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.0.cs b/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.0.cs index 1dcd3e2e3b..d6344a6f2e 100644 --- a/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.0.cs +++ b/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.0.cs @@ -109,6 +109,10 @@ namespace Microsoft.AspNetCore.Connections Microsoft.AspNetCore.Connections.ConnectionDelegate Build(); Microsoft.AspNetCore.Connections.IConnectionBuilder Use(System.Func middleware); } + public partial interface IConnectionFactory + { + System.Threading.Tasks.ValueTask ConnectAsync(System.Net.EndPoint endPoint, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + } public partial interface IConnectionListener : System.IAsyncDisposable { System.Net.EndPoint EndPoint { get; } @@ -125,6 +129,11 @@ namespace Microsoft.AspNetCore.Connections Binary = 1, Text = 2, } + public partial class UriEndPoint : System.Net.EndPoint + { + public UriEndPoint(System.Uri uri) { } + public System.Uri Uri { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } } namespace Microsoft.AspNetCore.Connections.Features { diff --git a/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.1.cs b/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.1.cs index 1dcd3e2e3b..d6344a6f2e 100644 --- a/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.1.cs +++ b/src/Servers/Connections.Abstractions/ref/Microsoft.AspNetCore.Connections.Abstractions.netstandard2.1.cs @@ -109,6 +109,10 @@ namespace Microsoft.AspNetCore.Connections Microsoft.AspNetCore.Connections.ConnectionDelegate Build(); Microsoft.AspNetCore.Connections.IConnectionBuilder Use(System.Func middleware); } + public partial interface IConnectionFactory + { + System.Threading.Tasks.ValueTask ConnectAsync(System.Net.EndPoint endPoint, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + } public partial interface IConnectionListener : System.IAsyncDisposable { System.Net.EndPoint EndPoint { get; } @@ -125,6 +129,11 @@ namespace Microsoft.AspNetCore.Connections Binary = 1, Text = 2, } + public partial class UriEndPoint : System.Net.EndPoint + { + public UriEndPoint(System.Uri uri) { } + public System.Uri Uri { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } } + } } namespace Microsoft.AspNetCore.Connections.Features { diff --git a/src/Servers/Connections.Abstractions/src/IConnectionFactory.cs b/src/Servers/Connections.Abstractions/src/IConnectionFactory.cs new file mode 100644 index 0000000000..9dd5171a17 --- /dev/null +++ b/src/Servers/Connections.Abstractions/src/IConnectionFactory.cs @@ -0,0 +1,25 @@ +// 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.Net; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Connections +{ + /// + /// A factory abstraction for creating connections to an endpoint. + /// + public interface IConnectionFactory + { + /// + /// Creates a new connection to an endpoint. + /// + /// The to connect to. + /// The token to monitor for cancellation requests. The default value is . + /// + /// A that represents the asynchronous connect, yielding the for the new connection when completed. + /// + ValueTask ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken = default); + } +} diff --git a/src/Servers/Connections.Abstractions/src/IConnectionListenerFactory.cs b/src/Servers/Connections.Abstractions/src/IConnectionListenerFactory.cs index ad68e06c02..2bcfa5ebca 100644 --- a/src/Servers/Connections.Abstractions/src/IConnectionListenerFactory.cs +++ b/src/Servers/Connections.Abstractions/src/IConnectionListenerFactory.cs @@ -1,10 +1,7 @@ // 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.Collections.Generic; using System.Net; -using System.Text; using System.Threading; using System.Threading.Tasks; diff --git a/src/Servers/Connections.Abstractions/src/UriEndPoint.cs b/src/Servers/Connections.Abstractions/src/UriEndPoint.cs new file mode 100644 index 0000000000..0321d5af2d --- /dev/null +++ b/src/Servers/Connections.Abstractions/src/UriEndPoint.cs @@ -0,0 +1,28 @@ +// 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; + +namespace Microsoft.AspNetCore.Connections +{ + /// + /// An defined by a . + /// + public class UriEndPoint : EndPoint + { + /// + /// Initializes a new instance of the class. + /// + /// The defining the . + public UriEndPoint(Uri uri) + { + Uri = uri ?? throw new ArgumentNullException(nameof(uri)); + } + + /// + /// The defining the . + /// + public Uri Uri { get; } + } +} diff --git a/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs b/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs index 419936b5de..a361a4ed03 100644 --- a/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs +++ b/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.0.cs @@ -8,9 +8,8 @@ namespace Microsoft.AspNetCore.SignalR.Client public static readonly System.TimeSpan DefaultHandshakeTimeout; public static readonly System.TimeSpan DefaultKeepAliveInterval; public static readonly System.TimeSpan DefaultServerTimeout; - public HubConnection(Microsoft.AspNetCore.SignalR.Client.IConnectionFactory connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol protocol, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } - public HubConnection(Microsoft.AspNetCore.SignalR.Client.IConnectionFactory connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol protocol, System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } - public HubConnection(Microsoft.AspNetCore.SignalR.Client.IConnectionFactory connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol protocol, System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, Microsoft.AspNetCore.SignalR.Client.IRetryPolicy reconnectPolicy) { } + public HubConnection(Microsoft.AspNetCore.Connections.IConnectionFactory connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol protocol, System.Net.EndPoint endPoint, System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } + public HubConnection(Microsoft.AspNetCore.Connections.IConnectionFactory connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol protocol, System.Net.EndPoint endPoint, System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, Microsoft.AspNetCore.SignalR.Client.IRetryPolicy reconnectPolicy) { } public string ConnectionId { get { throw null; } } public System.TimeSpan HandshakeTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public System.TimeSpan KeepAliveInterval { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } @@ -145,11 +144,6 @@ namespace Microsoft.AspNetCore.SignalR.Client Connecting = 2, Reconnecting = 3, } - public partial interface IConnectionFactory - { - System.Threading.Tasks.Task ConnectAsync(Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); - System.Threading.Tasks.Task DisposeAsync(Microsoft.AspNetCore.Connections.ConnectionContext connection); - } public partial interface IHubConnectionBuilder : Microsoft.AspNetCore.SignalR.ISignalRBuilder { Microsoft.AspNetCore.SignalR.Client.HubConnection Build(); diff --git a/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.1.cs b/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.1.cs index 419936b5de..a361a4ed03 100644 --- a/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.1.cs +++ b/src/SignalR/clients/csharp/Client.Core/ref/Microsoft.AspNetCore.SignalR.Client.Core.netstandard2.1.cs @@ -8,9 +8,8 @@ namespace Microsoft.AspNetCore.SignalR.Client public static readonly System.TimeSpan DefaultHandshakeTimeout; public static readonly System.TimeSpan DefaultKeepAliveInterval; public static readonly System.TimeSpan DefaultServerTimeout; - public HubConnection(Microsoft.AspNetCore.SignalR.Client.IConnectionFactory connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol protocol, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } - public HubConnection(Microsoft.AspNetCore.SignalR.Client.IConnectionFactory connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol protocol, System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } - public HubConnection(Microsoft.AspNetCore.SignalR.Client.IConnectionFactory connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol protocol, System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, Microsoft.AspNetCore.SignalR.Client.IRetryPolicy reconnectPolicy) { } + public HubConnection(Microsoft.AspNetCore.Connections.IConnectionFactory connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol protocol, System.Net.EndPoint endPoint, System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } + public HubConnection(Microsoft.AspNetCore.Connections.IConnectionFactory connectionFactory, Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol protocol, System.Net.EndPoint endPoint, System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory, Microsoft.AspNetCore.SignalR.Client.IRetryPolicy reconnectPolicy) { } public string ConnectionId { get { throw null; } } public System.TimeSpan HandshakeTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public System.TimeSpan KeepAliveInterval { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } @@ -145,11 +144,6 @@ namespace Microsoft.AspNetCore.SignalR.Client Connecting = 2, Reconnecting = 3, } - public partial interface IConnectionFactory - { - System.Threading.Tasks.Task ConnectAsync(Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); - System.Threading.Tasks.Task DisposeAsync(Microsoft.AspNetCore.Connections.ConnectionContext connection); - } public partial interface IHubConnectionBuilder : Microsoft.AspNetCore.SignalR.ISignalRBuilder { Microsoft.AspNetCore.SignalR.Client.HubConnection Build(); diff --git a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs index 19eaba4d0a..5e04bd309a 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs +++ b/src/SignalR/clients/csharp/Client.Core/src/HubConnection.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; +using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Threading; @@ -21,6 +22,7 @@ using Microsoft.AspNetCore.SignalR.Internal; using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; +using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.SignalR.Client { @@ -59,6 +61,7 @@ namespace Microsoft.AspNetCore.SignalR.Client private readonly IServiceProvider _serviceProvider; private readonly IConnectionFactory _connectionFactory; private readonly IRetryPolicy _reconnectPolicy; + private readonly EndPoint _endPoint; private readonly ConcurrentDictionary _handlers = new ConcurrentDictionary(StringComparer.Ordinal); // Holds all mutable state other than user-defined handlers and settable properties. @@ -172,6 +175,7 @@ namespace Microsoft.AspNetCore.SignalR.Client /// /// The used to create a connection each time is called. /// The used by the connection. + /// The to connect to. /// An containing the services provided to this instance. /// The logger factory. /// @@ -181,8 +185,8 @@ namespace Microsoft.AspNetCore.SignalR.Client /// /// The used to initialize the connection will be disposed when the connection is disposed. /// - public HubConnection(IConnectionFactory connectionFactory, IHubProtocol protocol, IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IRetryPolicy reconnectPolicy) - : this(connectionFactory, protocol, serviceProvider, loggerFactory) + public HubConnection(IConnectionFactory connectionFactory, IHubProtocol protocol, EndPoint endPoint, IServiceProvider serviceProvider, ILoggerFactory loggerFactory, IRetryPolicy reconnectPolicy) + : this(connectionFactory, protocol, endPoint, serviceProvider, loggerFactory) { _reconnectPolicy = reconnectPolicy; } @@ -192,27 +196,22 @@ namespace Microsoft.AspNetCore.SignalR.Client /// /// The used to create a connection each time is called. /// The used by the connection. + /// The to connect to. /// An containing the services provided to this instance. /// The logger factory. /// /// The used to initialize the connection will be disposed when the connection is disposed. /// - public HubConnection(IConnectionFactory connectionFactory, IHubProtocol protocol, IServiceProvider serviceProvider, ILoggerFactory loggerFactory) - : this(connectionFactory, protocol, loggerFactory) - { - _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); - } - - /// - /// Initializes a new instance of the class. - /// - /// The used to create a connection each time is called. - /// The used by the connection. - /// The logger factory. - public HubConnection(IConnectionFactory connectionFactory, IHubProtocol protocol, ILoggerFactory loggerFactory) + public HubConnection(IConnectionFactory connectionFactory, + IHubProtocol protocol, + EndPoint endPoint, + IServiceProvider serviceProvider, + ILoggerFactory loggerFactory) { _connectionFactory = connectionFactory ?? throw new ArgumentNullException(nameof(connectionFactory)); _protocol = protocol ?? throw new ArgumentNullException(nameof(protocol)); + _endPoint = endPoint ?? throw new ArgumentException(nameof(endPoint)); + _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); _loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; _logger = _loggerFactory.CreateLogger(); @@ -424,7 +423,7 @@ namespace Microsoft.AspNetCore.SignalR.Client Log.Starting(_logger); // Start the connection - var connection = await _connectionFactory.ConnectAsync(_protocol.TransferFormat, cancellationToken); + var connection = await _connectionFactory.ConnectAsync(_endPoint, cancellationToken); var startingConnectionState = new ConnectionState(connection, this); // From here on, if an error occurs we need to shut down the connection because @@ -450,9 +449,9 @@ namespace Microsoft.AspNetCore.SignalR.Client Log.Started(_logger); } - private Task CloseAsync(ConnectionContext connection) + private ValueTask CloseAsync(ConnectionContext connection) { - return _connectionFactory.DisposeAsync(connection); + return connection.DisposeAsync(); } // This method does both Dispose and Start, the 'disposing' flag indicates which. diff --git a/src/SignalR/clients/csharp/Client.Core/src/HubConnectionBuilder.cs b/src/SignalR/clients/csharp/Client.Core/src/HubConnectionBuilder.cs index bb7f9071f6..a63f6c9a80 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/HubConnectionBuilder.cs +++ b/src/SignalR/clients/csharp/Client.Core/src/HubConnectionBuilder.cs @@ -3,7 +3,11 @@ using System; using System.ComponentModel; +using System.Linq; +using System.Net; +using Microsoft.AspNetCore.Connections; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.SignalR.Client { @@ -42,11 +46,11 @@ namespace Microsoft.AspNetCore.SignalR.Client // The service provider is disposed by the HubConnection var serviceProvider = Services.BuildServiceProvider(); - var connectionFactory = serviceProvider.GetService(); - if (connectionFactory == null) - { + var connectionFactory = serviceProvider.GetService() ?? throw new InvalidOperationException($"Cannot create {nameof(HubConnection)} instance. An {nameof(IConnectionFactory)} was not configured."); - } + + var endPoint = serviceProvider.GetService() ?? + throw new InvalidOperationException($"Cannot create {nameof(HubConnection)} instance. An {nameof(EndPoint)} was not configured."); return serviceProvider.GetService(); } diff --git a/src/SignalR/clients/csharp/Client.Core/src/IConnectionFactory.cs b/src/SignalR/clients/csharp/Client.Core/src/IConnectionFactory.cs deleted file mode 100644 index 6749b6140e..0000000000 --- a/src/SignalR/clients/csharp/Client.Core/src/IConnectionFactory.cs +++ /dev/null @@ -1,35 +0,0 @@ -// 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.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Connections; - -namespace Microsoft.AspNetCore.SignalR.Client -{ - /// - /// A factory abstraction for creating connections to a SignalR server. - /// - public interface IConnectionFactory - { - /// - /// Creates a new connection to a SignalR server using the specified . - /// - /// The transfer format the connection should use. - /// The token to monitor for cancellation requests. The default value is . - /// - /// A that represents the asynchronous connect. - /// The property returns a for the new connection. - /// - Task ConnectAsync(TransferFormat transferFormat, CancellationToken cancellationToken = default); - - // Current plan for IAsyncDisposable is that DisposeAsync will NOT take a CancellationToken - // https://github.com/dotnet/csharplang/blob/195efa07806284d7b57550e7447dc8bd39c156bf/proposals/async-streams.md#iasyncdisposable - /// - /// Disposes the specified . - /// - /// The connection to dispose. - /// A that represents the asynchronous dispose. - Task DisposeAsync(ConnectionContext connection); - } -} \ No newline at end of file diff --git a/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj b/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj index 0bc1fe9403..736295fb4c 100644 --- a/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj +++ b/src/SignalR/clients/csharp/Client.Core/src/Microsoft.AspNetCore.SignalR.Client.Core.csproj @@ -1,4 +1,4 @@ - + Client for ASP.NET Core SignalR diff --git a/src/SignalR/clients/csharp/Client/ref/Microsoft.AspNetCore.SignalR.Client.netstandard2.0.cs b/src/SignalR/clients/csharp/Client/ref/Microsoft.AspNetCore.SignalR.Client.netstandard2.0.cs index e9e01c384f..1fb111784b 100644 --- a/src/SignalR/clients/csharp/Client/ref/Microsoft.AspNetCore.SignalR.Client.netstandard2.0.cs +++ b/src/SignalR/clients/csharp/Client/ref/Microsoft.AspNetCore.SignalR.Client.netstandard2.0.cs @@ -3,13 +3,6 @@ namespace Microsoft.AspNetCore.SignalR.Client { - public partial class HttpConnectionFactory : Microsoft.AspNetCore.SignalR.Client.IConnectionFactory - { - public HttpConnectionFactory(Microsoft.Extensions.Options.IOptions options, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { } - [System.Diagnostics.DebuggerStepThroughAttribute] - public System.Threading.Tasks.Task ConnectAsync(Microsoft.AspNetCore.Connections.TransferFormat transferFormat, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } - public System.Threading.Tasks.Task DisposeAsync(Microsoft.AspNetCore.Connections.ConnectionContext connection) { throw null; } - } public static partial class HubConnectionBuilderHttpExtensions { public static Microsoft.AspNetCore.SignalR.Client.IHubConnectionBuilder WithUrl(this Microsoft.AspNetCore.SignalR.Client.IHubConnectionBuilder hubConnectionBuilder, string url) { throw null; } diff --git a/src/SignalR/clients/csharp/Client/src/HttpConnectionFactory.cs b/src/SignalR/clients/csharp/Client/src/HttpConnectionFactory.cs index 3ecf4dacd0..60b5168c6c 100644 --- a/src/SignalR/clients/csharp/Client/src/HttpConnectionFactory.cs +++ b/src/SignalR/clients/csharp/Client/src/HttpConnectionFactory.cs @@ -2,6 +2,7 @@ // 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.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; @@ -14,7 +15,7 @@ namespace Microsoft.AspNetCore.SignalR.Client /// /// A factory for creating instances. /// - public class HttpConnectionFactory : IConnectionFactory + internal class HttpConnectionFactory : IConnectionFactory { private readonly HttpConnectionOptions _httpConnectionOptions; private readonly ILoggerFactory _loggerFactory; @@ -31,22 +32,44 @@ namespace Microsoft.AspNetCore.SignalR.Client throw new ArgumentNullException(nameof(options)); } - if (loggerFactory == null) - { - throw new ArgumentNullException(nameof(loggerFactory)); - } - _httpConnectionOptions = options.Value; - _loggerFactory = loggerFactory; + _loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); } - /// - public async Task ConnectAsync(TransferFormat transferFormat, CancellationToken cancellationToken = default) + /// + /// Creates a new connection to an . + /// + /// The to connect to. + /// The token to monitor for cancellation requests. The default value is . + /// + /// A that represents the asynchronous connect, yielding the for the new connection when completed. + /// + public async ValueTask ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken = default) { - var connection = new HttpConnection(_httpConnectionOptions, _loggerFactory); + if (endPoint == null) + { + throw new ArgumentNullException(nameof(endPoint)); + } + + if (!(endPoint is UriEndPoint uriEndPoint)) + { + throw new NotSupportedException($"The provided {nameof(EndPoint)} must be of type {nameof(UriEndPoint)}."); + } + + if (_httpConnectionOptions.Url != null && _httpConnectionOptions.Url != uriEndPoint.Uri) + { + throw new InvalidOperationException($"If {nameof(HttpConnectionOptions)}.{nameof(HttpConnectionOptions.Url)} was set, it must match the {nameof(UriEndPoint)}.{nameof(UriEndPoint.Uri)} passed to {nameof(ConnectAsync)}."); + } + + // Shallow copy before setting the Url property so we don't mutate the user-defined options object. + var shallowCopiedOptions = ShallowCopyHttpConnectionOptions(_httpConnectionOptions); + shallowCopiedOptions.Url = uriEndPoint.Uri; + + var connection = new HttpConnection(shallowCopiedOptions, _loggerFactory); + try { - await connection.StartAsync(transferFormat, cancellationToken); + await connection.StartAsync(cancellationToken); return connection; } catch @@ -57,10 +80,26 @@ namespace Microsoft.AspNetCore.SignalR.Client } } - /// - public Task DisposeAsync(ConnectionContext connection) + // Internal for testing + internal static HttpConnectionOptions ShallowCopyHttpConnectionOptions(HttpConnectionOptions options) { - return connection.DisposeAsync().AsTask(); + return new HttpConnectionOptions + { + HttpMessageHandlerFactory = options.HttpMessageHandlerFactory, + Headers = options.Headers, + ClientCertificates = options.ClientCertificates, + Cookies = options.Cookies, + Url = options.Url, + Transports = options.Transports, + SkipNegotiation = options.SkipNegotiation, + AccessTokenProvider = options.AccessTokenProvider, + CloseTimeout = options.CloseTimeout, + Credentials = options.Credentials, + Proxy = options.Proxy, + UseDefaultCredentials = options.UseDefaultCredentials, + DefaultTransferFormat = options.DefaultTransferFormat, + WebSocketConfiguration = options.WebSocketConfiguration, + }; } } } diff --git a/src/SignalR/clients/csharp/Client/src/HubConnectionBuilderHttpExtensions.cs b/src/SignalR/clients/csharp/Client/src/HubConnectionBuilderHttpExtensions.cs index c79542cb49..6ec4bcda79 100644 --- a/src/SignalR/clients/csharp/Client/src/HubConnectionBuilderHttpExtensions.cs +++ b/src/SignalR/clients/csharp/Client/src/HubConnectionBuilderHttpExtensions.cs @@ -2,9 +2,13 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Net; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Http.Connections; using Microsoft.AspNetCore.Http.Connections.Client; +using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.SignalR.Client { @@ -138,8 +142,44 @@ namespace Microsoft.AspNetCore.SignalR.Client hubConnectionBuilder.Services.Configure(configureHttpConnection); } + // Add HttpConnectionOptionsDerivedHttpEndPoint so HubConnection can read the Url from HttpConnectionOptions + // without the Signal.Client.Core project taking a new dependency on Http.Connections.Client. + hubConnectionBuilder.Services.AddSingleton(); + + // Configure the HttpConnection so that it uses the correct transfer format for the configured IHubProtocol. + hubConnectionBuilder.Services.AddSingleton, HubProtocolDerivedHttpOptionsConfigurer>(); + + // If and when HttpConnectionFactory is made public, it can be moved out of this assembly and into Http.Connections.Client. hubConnectionBuilder.Services.AddSingleton(); return hubConnectionBuilder; } + + private class HttpConnectionOptionsDerivedHttpEndPoint : UriEndPoint + { + public HttpConnectionOptionsDerivedHttpEndPoint(IOptions httpConnectionOptions) + : base(httpConnectionOptions.Value.Url) + { + } + } + + private class HubProtocolDerivedHttpOptionsConfigurer : IConfigureNamedOptions + { + private readonly TransferFormat _defaultTransferFormat; + + public HubProtocolDerivedHttpOptionsConfigurer(IHubProtocol hubProtocol) + { + _defaultTransferFormat = hubProtocol.TransferFormat; + } + + public void Configure(string name, HttpConnectionOptions options) + { + Configure(options); + } + + public void Configure(HttpConnectionOptions options) + { + options.DefaultTransferFormat = _defaultTransferFormat; + } + } } } diff --git a/src/SignalR/clients/csharp/Client/src/Microsoft.AspNetCore.SignalR.Client.csproj b/src/SignalR/clients/csharp/Client/src/Microsoft.AspNetCore.SignalR.Client.csproj index 5f9ce585c0..7bfb5b895c 100644 --- a/src/SignalR/clients/csharp/Client/src/Microsoft.AspNetCore.SignalR.Client.csproj +++ b/src/SignalR/clients/csharp/Client/src/Microsoft.AspNetCore.SignalR.Client.csproj @@ -11,4 +11,8 @@ + + + + diff --git a/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs b/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs index 369adb8895..0ecf1f8290 100644 --- a/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs +++ b/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubConnectionTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Channels; @@ -13,7 +14,6 @@ using Microsoft.AspNetCore.Http.Connections; using Microsoft.AspNetCore.Http.Connections.Client; using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Tests; -using Microsoft.AspNetCore.Testing; using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -42,10 +42,10 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests { var hubConnectionBuilder = new HubConnectionBuilder(); - if (protocol != null) - { - hubConnectionBuilder.Services.AddSingleton(protocol); - } + hubConnectionBuilder.WithUrl(url + path); + + protocol ??= new JsonHubProtocol(); + hubConnectionBuilder.Services.AddSingleton(protocol); if (loggerFactory != null) { @@ -57,20 +57,25 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests hubConnectionBuilder.WithAutomaticReconnect(); } + transportType ??= HttpTransportType.LongPolling | HttpTransportType.WebSockets | HttpTransportType.ServerSentEvents; + var delegateConnectionFactory = new DelegateConnectionFactory( - GetHttpConnectionFactory(url, loggerFactory, path, transportType ?? HttpTransportType.LongPolling | HttpTransportType.WebSockets | HttpTransportType.ServerSentEvents), - connection => ((HttpConnection)connection).DisposeAsync().AsTask()); + GetHttpConnectionFactory(url, loggerFactory, path, transportType.Value, protocol.TransferFormat)); hubConnectionBuilder.Services.AddSingleton(delegateConnectionFactory); return hubConnectionBuilder.Build(); } - private Func> GetHttpConnectionFactory(string url, ILoggerFactory loggerFactory, string path, HttpTransportType transportType) + private Func> GetHttpConnectionFactory(string url, ILoggerFactory loggerFactory, string path, HttpTransportType transportType, TransferFormat transferFormat) { - return async format => + return async endPoint => { - var connection = new HttpConnection(new Uri(url + path), transportType, loggerFactory); - await connection.StartAsync(format); + var httpEndpoint = (UriEndPoint)endPoint; + var options = new HttpConnectionOptions { Url = httpEndpoint.Uri, Transports = transportType, DefaultTransferFormat = transferFormat }; + var connection = new HttpConnection(options, loggerFactory); + + await connection.StartAsync(); + return connection; }; } diff --git a/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubProtocolVersionTests.cs b/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubProtocolVersionTests.cs index f0b5658ca2..c9e7b65e66 100644 --- a/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubProtocolVersionTests.cs +++ b/src/SignalR/clients/csharp/Client/test/FunctionalTests/HubProtocolVersionTests.cs @@ -4,6 +4,7 @@ using System; using System.Buffers; using System.Collections.Generic; +using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -101,16 +102,19 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests { using (StartServer(out var server)) { - var httpConnectionFactory = new HttpConnectionFactory(Options.Create(new HttpConnectionOptions - { - Url = new Uri(server.Url + "/version"), - Transports = transportType - }), LoggerFactory); + var httpConnectionFactory = new HttpConnectionFactory( + Options.Create(new HttpConnectionOptions + { + Transports = transportType, + DefaultTransferFormat = TransferFormat.Text + }), + LoggerFactory); var tcs = new TaskCompletionSource(); var proxyConnectionFactory = new ProxyConnectionFactory(httpConnectionFactory); var connectionBuilder = new HubConnectionBuilder() + .WithUrl(new Uri(server.Url + "/version")) .WithLoggerFactory(LoggerFactory); connectionBuilder.Services.AddSingleton(new VersionedJsonHubProtocol(1000)); connectionBuilder.Services.AddSingleton(proxyConnectionFactory); @@ -192,23 +196,18 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests private class ProxyConnectionFactory : IConnectionFactory { private readonly IConnectionFactory _innerFactory; - public Task ConnectTask { get; private set; } + public ValueTask ConnectTask { get; private set; } public ProxyConnectionFactory(IConnectionFactory innerFactory) { _innerFactory = innerFactory; } - public Task ConnectAsync(TransferFormat transferFormat, CancellationToken cancellationToken = default) + public ValueTask ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken = default) { - ConnectTask = _innerFactory.ConnectAsync(transferFormat, cancellationToken); + ConnectTask = _innerFactory.ConnectAsync(endPoint, cancellationToken); return ConnectTask; } - - public Task DisposeAsync(ConnectionContext connection) - { - return _innerFactory.DisposeAsync(connection); - } } public static IEnumerable TransportTypes() diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionFactoryTests.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionFactoryTests.cs index dc9dcbb371..f411f7cba1 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionFactoryTests.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionFactoryTests.cs @@ -1,10 +1,20 @@ +// 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.Collections.Generic; +using System.Net; using System.Net.Http; +using System.Net.WebSockets; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Http.Connections; using Microsoft.AspNetCore.Http.Connections.Client; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; +using Moq; using Xunit; namespace Microsoft.AspNetCore.SignalR.Client.Tests @@ -17,16 +27,94 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests var testHandler = new TestHttpMessageHandler(autoNegotiate: false, handleFirstPoll: false); testHandler.OnRequest((req, next, ct) => Task.FromException(new Exception("BOOM"))); - var factory = new HttpConnectionFactory(Options.Create(new HttpConnectionOptions() { - Url = new Uri("http://example.com"), - HttpMessageHandlerFactory = _ => testHandler - }), NullLoggerFactory.Instance); + var factory = new HttpConnectionFactory( + Options.Create(new HttpConnectionOptions + { + DefaultTransferFormat = TransferFormat.Text, + HttpMessageHandlerFactory = _ => testHandler, + }), + NullLoggerFactory.Instance); // We don't care about the specific exception - await Assert.ThrowsAnyAsync(() => factory.ConnectAsync(TransferFormat.Text)); + await Assert.ThrowsAnyAsync(async () => await factory.ConnectAsync(new UriEndPoint(new Uri("http://example.com")))); // We care that the handler (and by extension the client) was disposed Assert.True(testHandler.Disposed); } + + [Fact] + public async Task DoesNotSupportNonUriEndPoints() + { + var factory = new HttpConnectionFactory( + Options.Create(new HttpConnectionOptions { DefaultTransferFormat = TransferFormat.Text }), + NullLoggerFactory.Instance); + + var ex = await Assert.ThrowsAsync(async () => await factory.ConnectAsync(new IPEndPoint(IPAddress.Loopback, 0))); + + Assert.Equal("The provided EndPoint must be of type UriEndPoint.", ex.Message); + } + + [Fact] + public async Task OptionsUrlMustMatchEndPointIfSet() + { + var url1 = new Uri("http://example.com/1"); + var url2 = new Uri("http://example.com/2"); + + var factory = new HttpConnectionFactory( + Options.Create(new HttpConnectionOptions + { + Url = url1, + DefaultTransferFormat = TransferFormat.Text + }), + NullLoggerFactory.Instance); + + var ex = await Assert.ThrowsAsync(async () => await factory.ConnectAsync(new UriEndPoint(url2))); + Assert.Equal("If HttpConnectionOptions.Url was set, it must match the UriEndPoint.Uri passed to ConnectAsync.", ex.Message); + } + + [Fact] + public void ShallowCopyHttpConnectionOptionsCopiesAllPublicProperties() + { + Func handlerFactory = handler => handler; + Func> tokenProvider = () => Task.FromResult(""); + Action webSocketConfig = options => { }; + + var testValues = new Dictionary + { + { $"{nameof(HttpConnectionOptions.HttpMessageHandlerFactory)}", handlerFactory }, + { $"{nameof(HttpConnectionOptions.Headers)}", new Dictionary() }, + { $"{nameof(HttpConnectionOptions.ClientCertificates)}", new X509CertificateCollection() }, + { $"{nameof(HttpConnectionOptions.Cookies)}", new CookieContainer() }, + { $"{nameof(HttpConnectionOptions.Url)}", new Uri("https://example.com") }, + { $"{nameof(HttpConnectionOptions.Transports)}", HttpTransportType.ServerSentEvents }, + { $"{nameof(HttpConnectionOptions.SkipNegotiation)}", true }, + { $"{nameof(HttpConnectionOptions.AccessTokenProvider)}", tokenProvider }, + { $"{nameof(HttpConnectionOptions.CloseTimeout)}", TimeSpan.FromDays(1) }, + { $"{nameof(HttpConnectionOptions.Credentials)}", Mock.Of() }, + { $"{nameof(HttpConnectionOptions.Proxy)}", Mock.Of() }, + { $"{nameof(HttpConnectionOptions.UseDefaultCredentials)}", true }, + { $"{nameof(HttpConnectionOptions.DefaultTransferFormat)}", TransferFormat.Text }, + { $"{nameof(HttpConnectionOptions.WebSocketConfiguration)}", webSocketConfig }, + }; + + var options = new HttpConnectionOptions(); + var properties = typeof(HttpConnectionOptions) + .GetProperties(BindingFlags.Public | BindingFlags.Instance); + + foreach (var property in properties) + { + property.SetValue(options, testValues[property.Name]); + } + + var shallowCopiedOptions = HttpConnectionFactory.ShallowCopyHttpConnectionOptions(options); + + foreach (var property in properties) + { + Assert.Equal(testValues[property.Name], property.GetValue(shallowCopiedOptions)); + testValues.Remove(property.Name); + } + + Assert.Empty(testValues); + } } } 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 e551d774ac..142063ab05 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.ConnectionLifecycle.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.ConnectionLifecycle.cs @@ -30,8 +30,8 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { await WithConnectionAsync(CreateConnection(loggerFactory: LoggerFactory), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); + await connection.StartAsync().OrTimeout(); }); } } @@ -45,9 +45,9 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(loggerFactory: LoggerFactory, transport: new TestTransport(onTransportStart: SyncPoint.Create(out var syncPoint))), async (connection) => { - var firstStart = connection.StartAsync(TransferFormat.Text); + var firstStart = connection.StartAsync(); await syncPoint.WaitForSyncPoint().OrTimeout(); - var secondStart = connection.StartAsync(TransferFormat.Text); + var secondStart = connection.StartAsync(); syncPoint.Continue(); await firstStart.OrTimeout(); @@ -65,11 +65,11 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(loggerFactory: LoggerFactory), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); await connection.DisposeAsync().OrTimeout(); var exception = await Assert.ThrowsAsync( - async () => await connection.StartAsync(TransferFormat.Text)).OrTimeout(); + async () => await connection.StartAsync()).OrTimeout(); Assert.Equal(nameof(HttpConnection), exception.ObjectName); }); @@ -123,7 +123,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests async (connection) => { Assert.Equal(0, startCounter); - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); Assert.Equal(passThreshold, startCounter); }); } @@ -156,7 +156,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests transport: new TestTransport(onTransportStart: OnTransportStart)), async (connection) => { - var ex = await Assert.ThrowsAsync(() => connection.StartAsync(TransferFormat.Text)).OrTimeout(); + var ex = await Assert.ThrowsAsync(() => connection.StartAsync()).OrTimeout(); Assert.Equal("Unable to connect to the server with any of the available transports. " + "(WebSockets failed: Transport failed to start) (ServerSentEvents failed: Transport failed to start) (LongPolling failed: Transport failed to start)", ex.Message); @@ -200,7 +200,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests async (connection) => { // Start the connection and wait for the transport to start up. - var startTask = connection.StartAsync(TransferFormat.Text); + var startTask = connection.StartAsync(); await transportStart.WaitForSyncPoint().OrTimeout(); // While the transport is starting, dispose the connection @@ -232,7 +232,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests async (connection) => { // Start the connection - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); // Dispose the connection var stopTask = connection.DisposeAsync(); @@ -268,7 +268,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests async (connection) => { // Start the transport - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); Assert.NotNull(testTransport.Receiving); Assert.False(testTransport.Receiving.IsCompleted); @@ -313,7 +313,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(httpHandler, LoggerFactory), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); await connection.Transport.Output.WriteAsync(new byte[] { 0x42 }).OrTimeout(); // We should get the exception in the transport input completion. @@ -347,7 +347,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests async (connection) => { await Assert.ThrowsAsync( - () => connection.StartAsync(TransferFormat.Text).OrTimeout()); + () => connection.StartAsync().OrTimeout()); }); } } @@ -372,7 +372,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(httpHandler, loggerFactory: LoggerFactory, transport: sse), async (connection) => { - var startTask = connection.StartAsync(TransferFormat.Text); + var startTask = connection.StartAsync(); Assert.False(connectResponseTcs.Task.IsCompleted); Assert.False(startTask.IsCompleted); connectResponseTcs.TrySetResult(null); @@ -430,7 +430,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests private static async Task AssertDisposedAsync(HttpConnection connection) { var exception = - await Assert.ThrowsAsync(() => connection.StartAsync(TransferFormat.Text)); + await Assert.ThrowsAsync(() => connection.StartAsync()); Assert.Equal(nameof(HttpConnection), exception.ObjectName); } } diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.Helpers.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.Helpers.cs index 45b7cdbccb..5a788be1cf 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.Helpers.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.Helpers.cs @@ -22,6 +22,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests ITransport transport = null, ITransportFactory transportFactory = null, HttpTransportType? transportType = null, + TransferFormat transferFormat = TransferFormat.Text, Func> accessTokenProvider = null) { var httpOptions = new HttpConnectionOptions @@ -35,24 +36,32 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests httpOptions.Url = new Uri(url); } - return CreateConnection(httpOptions, loggerFactory, transport, transportFactory); + return CreateConnection(httpOptions, loggerFactory, transport, transportFactory, transferFormat); } - private static HttpConnection CreateConnection(HttpConnectionOptions httpConnectionOptions, ILoggerFactory loggerFactory = null, ITransport transport = null, ITransportFactory transportFactory = null) + private static HttpConnection CreateConnection( + HttpConnectionOptions httpConnectionOptions, + ILoggerFactory loggerFactory = null, + ITransport transport = null, + ITransportFactory transportFactory = null, + TransferFormat transferFormat = TransferFormat.Text) { loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; - httpConnectionOptions.Url = httpConnectionOptions.Url ?? new Uri("http://fakeuri.org/"); + httpConnectionOptions.Url ??= new Uri("http://fakeuri.org/"); + httpConnectionOptions.DefaultTransferFormat = transferFormat; + + if (transportFactory == null && transport != null) + { + transportFactory = new TestTransportFactory(transport); + } if (transportFactory != null) { return new HttpConnection(httpConnectionOptions, loggerFactory, transportFactory); } - else if (transport != null) - { - return new HttpConnection(httpConnectionOptions, loggerFactory, new TestTransportFactory(transport)); - } else { + // Use the public constructor to get the default transport factory. return new HttpConnection(httpConnectionOptions, loggerFactory); } } diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.Negotiate.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.Negotiate.cs index db18ec12e5..348e33cebf 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.Negotiate.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.Negotiate.cs @@ -76,7 +76,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(testHttpHandler, url: requestedUrl, loggerFactory: noErrorScope.LoggerFactory), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); }); } @@ -111,7 +111,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(testHttpHandler, loggerFactory: noErrorScope.LoggerFactory), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); connectionId = connection.ConnectionId; }); } @@ -168,7 +168,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(testHttpHandler, loggerFactory: noErrorScope.LoggerFactory), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); }); } @@ -198,7 +198,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(testHttpHandler, loggerFactory: noErrorScope.LoggerFactory), async (connection) => { - var exception = await Assert.ThrowsAsync(() => connection.StartAsync(TransferFormat.Text).OrTimeout()); + var exception = await Assert.ThrowsAsync(() => connection.StartAsync().OrTimeout()); Assert.Equal("Negotiate redirection limit exceeded.", exception.Message); }); } @@ -274,7 +274,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(testHttpHandler, loggerFactory: noErrorScope.LoggerFactory, accessTokenProvider: AccessTokenProvider), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); }); } @@ -327,10 +327,10 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests using (var noErrorScope = new VerifyNoErrorsScope()) { await WithConnectionAsync( - CreateConnection(testHttpHandler, transportFactory: transportFactory.Object, loggerFactory: noErrorScope.LoggerFactory), + CreateConnection(testHttpHandler, transportFactory: transportFactory.Object, loggerFactory: noErrorScope.LoggerFactory, transferFormat: TransferFormat.Binary), async (connection) => { - await connection.StartAsync(TransferFormat.Binary).OrTimeout(); + await connection.StartAsync().OrTimeout(); }); } } @@ -374,10 +374,10 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests .Returns(new TestTransport(transferFormat: TransferFormat.Text | TransferFormat.Binary)); await WithConnectionAsync( - CreateConnection(testHttpHandler, transportFactory: transportFactory.Object), + CreateConnection(testHttpHandler, transportFactory: transportFactory.Object, transferFormat: TransferFormat.Binary), async (connection) => { - await connection.StartAsync(TransferFormat.Binary).OrTimeout(); + await connection.StartAsync().OrTimeout(); }); } @@ -406,7 +406,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(testHttpHandler, loggerFactory: noErrorScope.LoggerFactory), async (connection) => { - var exception = await Assert.ThrowsAsync(() => connection.StartAsync(TransferFormat.Text).OrTimeout()); + var exception = await Assert.ThrowsAsync(() => connection.StartAsync().OrTimeout()); Assert.Equal("Test error.", exception.Message); }); } @@ -423,7 +423,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests async (connection) => { var exception = await Assert.ThrowsAsync( - () => connection.StartAsync(TransferFormat.Text).OrTimeout()); + () => connection.StartAsync().OrTimeout()); Assert.Equal(expectedExceptionMessage, exception.Message); }); diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.Transport.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.Transport.cs index 54ecfa98b5..142e40546c 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.Transport.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.Transport.cs @@ -63,7 +63,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(testHttpHandler, transportType: transportType, accessTokenProvider: AccessTokenProvider), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello world 1")); await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello world 2")); }); @@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(testHttpHandler, transportType: transportType, loggerFactory: LoggerFactory), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); var feature = connection.Features.Get(); Assert.NotNull(feature); @@ -138,7 +138,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(testHttpHandler, transportType: transportType), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello World")); }); // Fail safe in case the code is modified and some requests don't execute as a result @@ -178,7 +178,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(testHttpHandler, transportType: transportType), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); await connection.Transport.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello World")); }); // Fail safe in case the code is modified and some requests don't execute as a result @@ -210,7 +210,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(testHttpHandler), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); Assert.Contains("This is a test", Encoding.UTF8.GetString(await connection.Transport.Input.ReadAllAsync())); }); } @@ -237,7 +237,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(testHttpHandler), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); await connection.Transport.Output.WriteAsync(data).OrTimeout(); @@ -267,7 +267,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); await connection.DisposeAsync().OrTimeout(); var exception = await Assert.ThrowsAsync( @@ -284,7 +284,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(transport: transport), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); await connection.DisposeAsync().OrTimeout(); // This will throw OperationCanceledException if it's forcibly terminated @@ -292,6 +292,21 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests await transport.Receiving.OrTimeout(); }); } + + [Fact] + public Task StartAsyncTransferFormatOverridesOptions() + { + var transport = new TestTransport(); + + return WithConnectionAsync( + CreateConnection(transport: transport, transferFormat: TransferFormat.Binary), + async (connection) => + { + await connection.StartAsync(TransferFormat.Text).OrTimeout(); + + Assert.Equal(TransferFormat.Text, transport.Format); + }); + } } } } diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.cs index 4de8ca1f4b..a2fd1a61fd 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HttpConnectionTests.cs @@ -77,7 +77,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(httpOptions), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); }); Assert.NotNull(httpClientHandler); @@ -115,7 +115,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests CreateConnection(httpOptions, loggerFactory: mockLoggerFactory.Object), async (connection) => { - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); }); } catch diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionBuilderTests.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionBuilderTests.cs index 601634a252..7d9452331a 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionBuilderTests.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionBuilderTests.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Http.Connections.Client; using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.Extensions.DependencyInjection; @@ -22,6 +23,16 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests Assert.Equal("Cannot create HubConnection instance. An IConnectionFactory was not configured.", ex.Message); } + [Fact] + public void CannotCreateConnectionWithNoEndPoint() + { + var builder = new HubConnectionBuilder(); + builder.Services.AddSingleton(new HttpConnectionFactory(Options.Create(new HttpConnectionOptions()), NullLoggerFactory.Instance)); + + var ex = Assert.Throws(() => builder.Build()); + Assert.Equal("Cannot create HubConnection instance. An EndPoint was not configured.", ex.Message); + } + [Fact] public void AddJsonProtocolSetsHubProtocolToJsonWithDefaultOptions() { @@ -51,6 +62,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { var builder = new HubConnectionBuilder(); builder.Services.AddSingleton(new HttpConnectionFactory(Options.Create(new HttpConnectionOptions()), NullLoggerFactory.Instance)); + builder.WithUrl("http://example.com"); Assert.NotNull(builder.Build()); diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs index 7e29231574..06ad695e15 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.ConnectionLifecycle.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; @@ -78,19 +79,13 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { // Set up StartAsync to wait on the syncPoint when starting var createCount = 0; - Task ConnectionFactory(TransferFormat format) + ValueTask ConnectionFactory(EndPoint endPoint) { createCount += 1; - return new TestConnection().StartAsync(format); + return new TestConnection().StartAsync(); } - - Task DisposeAsync(ConnectionContext connection) - { - return connection.DisposeAsync().AsTask(); - } - - var builder = new HubConnectionBuilder(); - var delegateConnectionFactory = new DelegateConnectionFactory(ConnectionFactory, DisposeAsync); + var builder = new HubConnectionBuilder().WithUrl("http://example.com"); + var delegateConnectionFactory = new DelegateConnectionFactory(ConnectionFactory); builder.Services.AddSingleton(delegateConnectionFactory); await AsyncUsing(builder.Build(), async connection => @@ -116,16 +111,14 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests // Set up StartAsync to wait on the syncPoint when starting var createCount = 0; var onDisposeForFirstConnection = SyncPoint.Create(out var syncPoint); - Task ConnectionFactory(TransferFormat format) + ValueTask ConnectionFactory(EndPoint endPoint) { createCount += 1; - return new TestConnection(onDispose: createCount == 1 ? onDisposeForFirstConnection : null).StartAsync(format); + return new TestConnection(onDispose: createCount == 1 ? onDisposeForFirstConnection : null).StartAsync(); } - Task DisposeAsync(ConnectionContext connection) => connection.DisposeAsync().AsTask(); - - var builder = new HubConnectionBuilder(); - var delegateConnectionFactory = new DelegateConnectionFactory(ConnectionFactory, DisposeAsync); + var builder = new HubConnectionBuilder().WithUrl("http://example.com"); + var delegateConnectionFactory = new DelegateConnectionFactory(ConnectionFactory); builder.Services.AddSingleton(delegateConnectionFactory); await AsyncUsing(builder.Build(), async connection => @@ -596,12 +589,11 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests [Fact] public async Task HubConnectionClosesWithErrorIfTerminatedWithPartialMessage() { - var builder = new HubConnectionBuilder(); + var builder = new HubConnectionBuilder().WithUrl("http://example.com"); var innerConnection = new TestConnection(); var delegateConnectionFactory = new DelegateConnectionFactory( - format => innerConnection.StartAsync(format), - connection => connection.DisposeAsync().AsTask()); + endPoint => innerConnection.StartAsync()); builder.Services.AddSingleton(delegateConnectionFactory); var hubConnection = builder.Build(); diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Helpers.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Helpers.cs index 96c18db8d0..779cd71392 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Helpers.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Helpers.cs @@ -1,6 +1,7 @@ // 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 Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Tests; using Microsoft.Extensions.DependencyInjection; @@ -12,11 +13,10 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { private static HubConnection CreateHubConnection(TestConnection connection, IHubProtocol protocol = null, ILoggerFactory loggerFactory = null) { - var builder = new HubConnectionBuilder(); + var builder = new HubConnectionBuilder().WithUrl("http://example.com"); var delegateConnectionFactory = new DelegateConnectionFactory( - connection.StartAsync, - c => c.DisposeAsync().AsTask()); + endPoint => connection.StartAsync()); builder.Services.AddSingleton(delegateConnectionFactory); diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Reconnect.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Reconnect.cs index a039bbd0ef..8b1743fdef 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Reconnect.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.Reconnect.cs @@ -3,9 +3,12 @@ using System; using System.Collections.Generic; +using System.IO.Pipelines; +using System.Net; using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Internal; using Microsoft.AspNetCore.SignalR.Protocol; using Microsoft.AspNetCore.SignalR.Tests; @@ -75,7 +78,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests using (StartVerifiableLog(ExpectedErrors)) { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory).WithUrl("http://example.com"); var testConnectionFactory = default(ReconnectingConnectionFactory); var startCallCount = 0; var originalConnectionId = "originalConnectionId"; @@ -189,7 +192,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests using (StartVerifiableLog(ExpectedErrors)) { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory).WithUrl("http://example.com"); var startCallCount = 0; Task OnTestConnectionStart() @@ -281,7 +284,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests using (StartVerifiableLog(ExpectedErrors)) { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory).WithUrl("http://example.com"); var testConnectionFactory = new ReconnectingConnectionFactory(); builder.Services.AddSingleton(testConnectionFactory); @@ -376,7 +379,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests using (StartVerifiableLog(ExpectedErrors)) { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory).WithUrl("http://example.com"); var testConnectionFactory = new ReconnectingConnectionFactory(); builder.Services.AddSingleton(testConnectionFactory); @@ -431,7 +434,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests using (StartVerifiableLog(ExpectedErrors)) { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory).WithUrl("http://example.com"); var testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(autoHandshake: false)); builder.Services.AddSingleton(testConnectionFactory); @@ -489,7 +492,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests using (StartVerifiableLog(ExpectedErrors)) { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory).WithUrl("http://example.com"); var testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(autoHandshake: false)); builder.Services.AddSingleton(testConnectionFactory); @@ -596,7 +599,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests using (StartVerifiableLog(ExpectedErrors)) { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory).WithUrl("http://example.com"); var testConnectionFactory = new ReconnectingConnectionFactory(() => new TestConnection(autoHandshake: false)); builder.Services.AddSingleton(testConnectionFactory); @@ -715,7 +718,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests using (StartVerifiableLog(ExpectedErrors)) { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory).WithUrl("http://example.com"); var connectionStartTcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); async Task OnTestConnectionStart() @@ -806,7 +809,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests using (StartVerifiableLog(ExpectedErrors)) { - var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory); + var builder = new HubConnectionBuilder().WithLoggerFactory(LoggerFactory).WithUrl("http://example.com"); var testConnectionFactory = new ReconnectingConnectionFactory(); builder.Services.AddSingleton(testConnectionFactory); @@ -886,7 +889,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests return _testConnectionTcs.Task; } - public async Task ConnectAsync(TransferFormat transferFormat, CancellationToken cancellationToken = default) + public async ValueTask ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken = default) { var testConnection = _testConnectionFactory(); @@ -894,7 +897,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests try { - return await testConnection.StartAsync(transferFormat); + return new DisposeInterceptingConnectionContextDecorator(await testConnection.StartAsync(), this); } catch { @@ -912,6 +915,31 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests await disposingTestConnection.DisposeAsync(); } } + + private class DisposeInterceptingConnectionContextDecorator : ConnectionContext + { + private readonly ConnectionContext _inner; + private readonly ReconnectingConnectionFactory _reconnectingConnectionFactory; + + public DisposeInterceptingConnectionContextDecorator(ConnectionContext inner, ReconnectingConnectionFactory reconnectingConnectionFactory) + { + _inner = inner; + _reconnectingConnectionFactory = reconnectingConnectionFactory; + } + + public override string ConnectionId { get => _inner.ConnectionId; set => _inner.ConnectionId = value; } + public override IFeatureCollection Features => _inner.Features; + public override IDictionary Items { get => _inner.Items; set => _inner.Items = value; } + public override IDuplexPipe Transport { get => _inner.Transport; set => _inner.Transport = value; } + public override CancellationToken ConnectionClosed { get => _inner.ConnectionClosed; set => _inner.ConnectionClosed = value; } + public override EndPoint LocalEndPoint { get => _inner.LocalEndPoint; set => _inner.LocalEndPoint = value; } + public override EndPoint RemoteEndPoint { get => _inner.RemoteEndPoint; set => _inner.RemoteEndPoint = value; } + + public override void Abort(ConnectionAbortedException abortReason) => _inner.Abort(abortReason); + public override void Abort() => _inner.Abort(); + + public override ValueTask DisposeAsync() => new ValueTask(_reconnectingConnectionFactory.DisposeAsync(_inner)); + } } } } diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs index 74a2da0c7f..2c9df93cb8 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/HubConnectionTests.cs @@ -59,11 +59,10 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests [Fact] public async Task ClosedEventRaisedWhenTheClientIsStopped() { - var builder = new HubConnectionBuilder(); + var builder = new HubConnectionBuilder().WithUrl("http://example.com"); var delegateConnectionFactory = new DelegateConnectionFactory( - format => new TestConnection().StartAsync(format), - connection => ((TestConnection)connection).DisposeAsync().AsTask()); + endPoint => new TestConnection().StartAsync()); builder.Services.AddSingleton(delegateConnectionFactory); var hubConnection = builder.Build(); diff --git a/src/SignalR/clients/csharp/Client/test/UnitTests/TestConnection.cs b/src/SignalR/clients/csharp/Client/test/UnitTests/TestConnection.cs index cdb61db868..a832ca97fe 100644 --- a/src/SignalR/clients/csharp/Client/test/UnitTests/TestConnection.cs +++ b/src/SignalR/clients/csharp/Client/test/UnitTests/TestConnection.cs @@ -62,7 +62,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests public override ValueTask DisposeAsync() => DisposeCoreAsync(); - public async Task StartAsync(TransferFormat transferFormat = TransferFormat.Binary) + public async ValueTask StartAsync() { _started.TrySetResult(null); diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netstandard2.0.cs b/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netstandard2.0.cs index 35b6c7cc35..2d430b664a 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netstandard2.0.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netstandard2.0.cs @@ -28,6 +28,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Client public System.TimeSpan CloseTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public System.Net.CookieContainer Cookies { get { throw null; } set { } } public System.Net.ICredentials Credentials { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public Microsoft.AspNetCore.Connections.TransferFormat DefaultTransferFormat { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public System.Collections.Generic.IDictionary Headers { get { throw null; } set { } } public System.Func HttpMessageHandlerFactory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public System.Net.IWebProxy Proxy { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netstandard2.1.cs b/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netstandard2.1.cs index 35b6c7cc35..2d430b664a 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netstandard2.1.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/ref/Microsoft.AspNetCore.Http.Connections.Client.netstandard2.1.cs @@ -28,6 +28,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Client public System.TimeSpan CloseTimeout { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public System.Net.CookieContainer Cookies { get { throw null; } set { } } public System.Net.ICredentials Credentials { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } + public Microsoft.AspNetCore.Connections.TransferFormat DefaultTransferFormat { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public System.Collections.Generic.IDictionary Headers { get { throw null; } set { } } public System.Func HttpMessageHandlerFactory { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } public System.Net.IWebProxy Proxy { [System.Runtime.CompilerServices.CompilerGeneratedAttribute]get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute]set { } } diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs index c40ced0f23..852681d963 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnection.cs @@ -129,6 +129,11 @@ namespace Microsoft.AspNetCore.Http.Connections.Client /// The logger factory. public HttpConnection(HttpConnectionOptions httpConnectionOptions, ILoggerFactory loggerFactory) { + if (httpConnectionOptions == null) + { + throw new ArgumentNullException(nameof(httpConnectionOptions)); + } + if (httpConnectionOptions.Url == null) { throw new ArgumentException("Options does not have a URL specified.", nameof(httpConnectionOptions)); @@ -168,7 +173,7 @@ namespace Microsoft.AspNetCore.Http.Connections.Client /// public Task StartAsync(CancellationToken cancellationToken = default) { - return StartAsync(TransferFormat.Binary, cancellationToken); + return StartAsync(_httpConnectionOptions.DefaultTransferFormat, cancellationToken); } /// diff --git a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnectionOptions.cs b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnectionOptions.cs index e5507a3a04..ef8b177eaf 100644 --- a/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnectionOptions.cs +++ b/src/SignalR/clients/csharp/Http.Connections.Client/src/HttpConnectionOptions.cs @@ -7,7 +7,9 @@ using System.Net; using System.Net.Http; using System.Net.WebSockets; using System.Security.Cryptography.X509Certificates; +using System.Threading; using System.Threading.Tasks; +using Microsoft.AspNetCore.Connections; namespace Microsoft.AspNetCore.Http.Connections.Client { @@ -108,6 +110,12 @@ namespace Microsoft.AspNetCore.Http.Connections.Client /// public bool? UseDefaultCredentials { get; set; } + /// + /// Gets or sets the default to use if + /// is called instead of . + /// + public TransferFormat DefaultTransferFormat { get; set; } = TransferFormat.Binary; + /// /// Gets or sets a delegate that will be invoked with the object used /// to configure the WebSocket when using the WebSockets transport. diff --git a/src/SignalR/common/testassets/Tests.Utils/DelegateConnectionFactory.cs b/src/SignalR/common/testassets/Tests.Utils/DelegateConnectionFactory.cs index 900d2a5894..c66dbda5da 100644 --- a/src/SignalR/common/testassets/Tests.Utils/DelegateConnectionFactory.cs +++ b/src/SignalR/common/testassets/Tests.Utils/DelegateConnectionFactory.cs @@ -2,33 +2,26 @@ // 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.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; -using Microsoft.AspNetCore.SignalR.Client; namespace Microsoft.AspNetCore.SignalR.Tests { public class DelegateConnectionFactory : IConnectionFactory { - private readonly Func> _connectDelegate; - private readonly Func _disposeDelegate; + private readonly Func> _connectDelegate; // We have no tests that use the CancellationToken. When we do, we can add it to the delegate. This is test code. - public DelegateConnectionFactory(Func> connectDelegate, Func disposeDelegate) + public DelegateConnectionFactory(Func> connectDelegate) { _connectDelegate = connectDelegate; - _disposeDelegate = disposeDelegate; } - public Task ConnectAsync(TransferFormat transferFormat, CancellationToken cancellationToken) + public ValueTask ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken) { - return _connectDelegate(transferFormat); - } - - public Task DisposeAsync(ConnectionContext connection) - { - return _disposeDelegate(connection); + return _connectDelegate(endPoint); } } } diff --git a/src/SignalR/perf/Microbenchmarks/HubConnectionReceiveBenchmark.cs b/src/SignalR/perf/Microbenchmarks/HubConnectionReceiveBenchmark.cs index 4821e95eeb..41944f26ef 100644 --- a/src/SignalR/perf/Microbenchmarks/HubConnectionReceiveBenchmark.cs +++ b/src/SignalR/perf/Microbenchmarks/HubConnectionReceiveBenchmark.cs @@ -77,21 +77,14 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks _invocationMessageBytes = hubProtocol.GetMessageBytes(new InvocationMessage(MethodName, arguments)); - var delegateConnectionFactory = new DelegateConnectionFactory( - format => - { - var connection = new DefaultConnectionContext(); - // prevents keep alive time being activated - connection.Features.Set(new TestConnectionInherentKeepAliveFeature()); - connection.Transport = _pipe; - return Task.FromResult(connection); - }, - connection => - { - connection.Transport.Output.Complete(); - connection.Transport.Input.Complete(); - return Task.CompletedTask; - }); + var delegateConnectionFactory = new DelegateConnectionFactory(endPoint => + { + var connection = new DefaultConnectionContext(); + // prevents keep alive time being activated + connection.Features.Set(new TestConnectionInherentKeepAliveFeature()); + connection.Transport = _pipe; + return new ValueTask(connection); + }); hubConnectionBuilder.Services.AddSingleton(delegateConnectionFactory); _hubConnection = hubConnectionBuilder.Build(); diff --git a/src/SignalR/perf/Microbenchmarks/HubConnectionSendBenchmark.cs b/src/SignalR/perf/Microbenchmarks/HubConnectionSendBenchmark.cs index 2341a49191..5f2d391612 100644 --- a/src/SignalR/perf/Microbenchmarks/HubConnectionSendBenchmark.cs +++ b/src/SignalR/perf/Microbenchmarks/HubConnectionSendBenchmark.cs @@ -55,19 +55,13 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks hubConnectionBuilder.AddMessagePackProtocol(); } - var delegateConnectionFactory = new DelegateConnectionFactory(format => + var delegateConnectionFactory = new DelegateConnectionFactory(endPoint => { var connection = new DefaultConnectionContext(); // prevents keep alive time being activated connection.Features.Set(new TestConnectionInherentKeepAliveFeature()); connection.Transport = _pipe; - return Task.FromResult(connection); - }, - connection => - { - connection.Transport.Output.Complete(); - connection.Transport.Input.Complete(); - return Task.CompletedTask; + return new ValueTask(connection); }); hubConnectionBuilder.Services.AddSingleton(delegateConnectionFactory); diff --git a/src/SignalR/perf/Microbenchmarks/HubConnectionStartBenchmark.cs b/src/SignalR/perf/Microbenchmarks/HubConnectionStartBenchmark.cs index fbf8da9afb..e4c2b6747f 100644 --- a/src/SignalR/perf/Microbenchmarks/HubConnectionStartBenchmark.cs +++ b/src/SignalR/perf/Microbenchmarks/HubConnectionStartBenchmark.cs @@ -40,19 +40,13 @@ namespace Microsoft.AspNetCore.SignalR.Microbenchmarks _pipe = new TestDuplexPipe(); var hubConnectionBuilder = new HubConnectionBuilder(); - var delegateConnectionFactory = new DelegateConnectionFactory(format => + var delegateConnectionFactory = new DelegateConnectionFactory(endPoint => { var connection = new DefaultConnectionContext(); // prevents keep alive time being activated connection.Features.Set(new TestConnectionInherentKeepAliveFeature()); connection.Transport = _pipe; - return Task.FromResult(connection); - }, - connection => - { - connection.Transport.Output.Complete(); - connection.Transport.Input.Complete(); - return Task.CompletedTask; + return new ValueTask(connection); }); hubConnectionBuilder.Services.AddSingleton(delegateConnectionFactory); diff --git a/src/SignalR/samples/ClientSample/RawSample.cs b/src/SignalR/samples/ClientSample/RawSample.cs index 7674740272..abc09d1e26 100644 --- a/src/SignalR/samples/ClientSample/RawSample.cs +++ b/src/SignalR/samples/ClientSample/RawSample.cs @@ -11,7 +11,6 @@ using System.Threading.Tasks; using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Http.Connections.Client; using Microsoft.Extensions.CommandLineUtils; -using Microsoft.Extensions.Logging; namespace ClientSample { @@ -34,10 +33,18 @@ namespace ClientSample baseUrl = string.IsNullOrEmpty(baseUrl) ? "http://localhost:5000/chat" : baseUrl; Console.WriteLine($"Connecting to {baseUrl}..."); - var connection = new HttpConnection(new Uri(baseUrl)); + + var connectionOptions = new HttpConnectionOptions + { + Url = new Uri(baseUrl), + DefaultTransferFormat = TransferFormat.Text, + }; + + var connection = new HttpConnection(connectionOptions, loggerFactory: null); + try { - await connection.StartAsync(TransferFormat.Text); + await connection.StartAsync(); Console.WriteLine($"Connected to {baseUrl}"); var shutdown = new TaskCompletionSource(); diff --git a/src/SignalR/samples/ClientSample/Tcp/TcpConnection.cs b/src/SignalR/samples/ClientSample/Tcp/TcpConnection.cs index d2802b2e0c..d64b5b8fb9 100644 --- a/src/SignalR/samples/ClientSample/Tcp/TcpConnection.cs +++ b/src/SignalR/samples/ClientSample/Tcp/TcpConnection.cs @@ -53,7 +53,7 @@ namespace ClientSample return default; } - public async Task StartAsync() + public async ValueTask StartAsync() { await _socket.ConnectAsync(_endPoint); @@ -245,11 +245,5 @@ namespace ClientSample } } } - - public Task StartAsync(TransferFormat transferFormat) - { - // Transfer format is irrelevant - return StartAsync(); - } } } diff --git a/src/SignalR/samples/ClientSample/Tcp/TcpHubConnectionBuilderExtensions.cs b/src/SignalR/samples/ClientSample/Tcp/TcpHubConnectionBuilderExtensions.cs index a52b18600e..cca53befa4 100644 --- a/src/SignalR/samples/ClientSample/Tcp/TcpHubConnectionBuilderExtensions.cs +++ b/src/SignalR/samples/ClientSample/Tcp/TcpHubConnectionBuilderExtensions.cs @@ -1,11 +1,12 @@ using System; +using System.Diagnostics; using System.Net; using System.Threading; using System.Threading.Tasks; using ClientSample; using Microsoft.AspNetCore.Connections; +using Microsoft.AspNetCore.Http.Connections.Client; using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; namespace Microsoft.AspNetCore.SignalR.Client { @@ -33,28 +34,17 @@ namespace Microsoft.AspNetCore.SignalR.Client public static IHubConnectionBuilder WithEndPoint(this IHubConnectionBuilder builder, EndPoint endPoint) { - builder.Services.AddSingleton(new TcpConnectionFactory(endPoint)); + builder.Services.AddSingleton(); + builder.Services.AddSingleton(endPoint); return builder; } private class TcpConnectionFactory : IConnectionFactory { - private readonly EndPoint _endPoint; - - public TcpConnectionFactory(EndPoint endPoint) + public ValueTask ConnectAsync(EndPoint endPoint, CancellationToken cancellationToken = default) { - _endPoint = endPoint; - } - - public Task ConnectAsync(TransferFormat transferFormat, CancellationToken cancellationToken = default) - { - return new TcpConnection(_endPoint).StartAsync(); - } - - public Task DisposeAsync(ConnectionContext connection) - { - return connection.DisposeAsync().AsTask(); + return new TcpConnection(endPoint).StartAsync(); } } } diff --git a/src/SignalR/server/SignalR/test/EndToEndTests.cs b/src/SignalR/server/SignalR/test/EndToEndTests.cs index b3aabeee1c..f4d1815b26 100644 --- a/src/SignalR/server/SignalR/test/EndToEndTests.cs +++ b/src/SignalR/server/SignalR/test/EndToEndTests.cs @@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests // The test should connect to the server using WebSockets transport on Windows 8 and newer. // On Windows 7/2008R2 it should use ServerSentEvents transport to connect to the server. var connection = new HttpConnection(new Uri(url), HttpTransports.All, LoggerFactory); - await connection.StartAsync(TransferFormat.Binary).OrTimeout(); + await connection.StartAsync().OrTimeout(); await connection.DisposeAsync().OrTimeout(); } } @@ -64,8 +64,8 @@ namespace Microsoft.AspNetCore.SignalR.Tests // On Windows 7/2008R2 it should use ServerSentEvents transport to connect to the server. // The test logic lives in the TestTransportFactory and FakeTransport. - var connection = new HttpConnection(new HttpConnectionOptions { Url = new Uri(url) }, LoggerFactory, new TestTransportFactory()); - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + var connection = new HttpConnection(new HttpConnectionOptions { Url = new Uri(url), DefaultTransferFormat = TransferFormat.Text }, LoggerFactory, new TestTransportFactory()); + await connection.StartAsync().OrTimeout(); await connection.DisposeAsync().OrTimeout(); } } @@ -78,8 +78,8 @@ namespace Microsoft.AspNetCore.SignalR.Tests using (StartServer(out var server)) { var url = server.Url + "/echo"; - var connection = new HttpConnection(new Uri(url), transportType, LoggerFactory); - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + var connection = new HttpConnection(new HttpConnectionOptions { Url = new Uri(url), Transports = transportType, DefaultTransferFormat = TransferFormat.Text }, LoggerFactory); + await connection.StartAsync().OrTimeout(); await connection.DisposeAsync().OrTimeout(); } } @@ -189,7 +189,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests try { var message = new byte[] { 42 }; - await connection.StartAsync(TransferFormat.Binary).OrTimeout(); + await connection.StartAsync().OrTimeout(); await connection.Transport.Output.WriteAsync(message).OrTimeout(); @@ -238,7 +238,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests try { - var exception = await Assert.ThrowsAsync(() => connection.StartAsync(TransferFormat.Binary).OrTimeout()); + var exception = await Assert.ThrowsAsync(() => connection.StartAsync().OrTimeout()); Assert.Equal("Negotiation can only be skipped when using the WebSocket transport directly.", exception.Message); } catch (Exception ex) @@ -265,11 +265,11 @@ namespace Microsoft.AspNetCore.SignalR.Tests const string message = "Major Key"; var url = server.Url + "/echo"; - var connection = new HttpConnection(new Uri(url), transportType, LoggerFactory); + var connection = new HttpConnection(new HttpConnectionOptions { Url = new Uri(url), Transports = transportType, DefaultTransferFormat = requestedTransferFormat }, LoggerFactory); try { logger.LogInformation("Starting connection to {url}", url); - await connection.StartAsync(requestedTransferFormat).OrTimeout(); + await connection.StartAsync().OrTimeout(); logger.LogInformation("Started connection to {url}", url); var bytes = Encoding.UTF8.GetBytes(message); @@ -327,7 +327,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests try { logger.LogInformation("Starting connection to {url}", url); - await connection.StartAsync(TransferFormat.Binary).OrTimeout(); + await connection.StartAsync().OrTimeout(); logger.LogInformation("Started connection to {url}", url); var bytes = Encoding.UTF8.GetBytes(message); @@ -373,7 +373,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests var url = server.Url + "/auth"; var connection = new HttpConnection(new Uri(url), HttpTransportType.WebSockets, LoggerFactory); - var exception = await Assert.ThrowsAsync(() => connection.StartAsync(TransferFormat.Binary).OrTimeout()); + var exception = await Assert.ThrowsAsync(() => connection.StartAsync().OrTimeout()); Assert.Contains("401", exception.Message); } @@ -404,7 +404,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests var connection = new HttpConnection(options, LoggerFactory); - await Assert.ThrowsAsync(() => connection.StartAsync(TransferFormat.Binary).OrTimeout()); + await Assert.ThrowsAsync(() => connection.StartAsync().OrTimeout()); } } @@ -430,7 +430,7 @@ namespace Microsoft.AspNetCore.SignalR.Tests try { logger.LogInformation("Starting connection to {url}", url); - await connection.StartAsync(TransferFormat.Binary).OrTimeout(); + await connection.StartAsync().OrTimeout(); Assert.True(false); } catch (Exception ex) @@ -470,17 +470,20 @@ namespace Microsoft.AspNetCore.SignalR.Tests } var url = server.Url + "/auth"; - var connection = new HttpConnection(new HttpConnectionOptions() - { - AccessTokenProvider = () => Task.FromResult(token), - Url = new Uri(url), - Transports = HttpTransportType.ServerSentEvents - }, LoggerFactory); + var connection = new HttpConnection( + new HttpConnectionOptions() + { + Url = new Uri(url), + AccessTokenProvider = () => Task.FromResult(token), + Transports = HttpTransportType.ServerSentEvents, + DefaultTransferFormat = TransferFormat.Text, + }, + LoggerFactory); try { logger.LogInformation("Starting connection to {url}", url); - await connection.StartAsync(TransferFormat.Text).OrTimeout(); + await connection.StartAsync().OrTimeout(); logger.LogInformation("Connected to {url}", url); } finally