From 4ac8e786cfd86fa3b40321f932ea0db00f9d8ae1 Mon Sep 17 00:00:00 2001 From: Pawel Kadluczka Date: Fri, 1 Sep 2017 11:36:16 -0700 Subject: [PATCH] Adding HubConnectionBuilder --- samples/ClientSample/HubSample.cs | 8 +- .../ConnectionFactoryDelegate.cs | 9 +++ .../HubConnection.cs | 9 --- .../HubConnectionBuilder.cs | 75 +++++++++++++++++++ .../HubConnectionBuilderDefaults.cs | 11 +++ .../HubConnectionBuilderExtensions.cs | 46 ++++++++++++ .../IHubConnectionBuilder.cs | 13 ++++ .../HubConnectionBuilderExtensions.cs | 26 +++++++ .../HubConnectionBuilderHttpExtensions.cs | 71 ++++++++++++++++++ ...Microsoft.AspNetCore.SignalR.Client.csproj | 5 ++ .../IHubContext.cs | 5 -- .../HubConnectionTests.cs | 9 ++- .../HubConnectionBuilderTests.cs | 34 +++++++++ .../HubConnectionTests.cs | 15 ++-- .../EndToEndTests.cs | 6 +- 15 files changed, 315 insertions(+), 27 deletions(-) create mode 100644 src/Microsoft.AspNetCore.SignalR.Client.Core/ConnectionFactoryDelegate.cs create mode 100644 src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnectionBuilder.cs create mode 100644 src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnectionBuilderDefaults.cs create mode 100644 src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnectionBuilderExtensions.cs create mode 100644 src/Microsoft.AspNetCore.SignalR.Client.Core/IHubConnectionBuilder.cs create mode 100644 src/Microsoft.AspNetCore.SignalR.Client/HubConnectionBuilderExtensions.cs create mode 100644 src/Microsoft.AspNetCore.SignalR.Client/HubConnectionBuilderHttpExtensions.cs create mode 100644 test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionBuilderTests.cs diff --git a/samples/ClientSample/HubSample.cs b/samples/ClientSample/HubSample.cs index f63d3caa4a..acdb5954ac 100644 --- a/samples/ClientSample/HubSample.cs +++ b/samples/ClientSample/HubSample.cs @@ -30,11 +30,13 @@ namespace ClientSample { baseUrl = string.IsNullOrEmpty(baseUrl) ? "http://localhost:5000/default" : baseUrl; - var loggerFactory = new LoggerFactory(); - Console.WriteLine("Connecting to {0}", baseUrl); var httpConnection = new HttpConnection(new Uri(baseUrl)); - var connection = new HubConnection(httpConnection, loggerFactory); + var connection = new HubConnectionBuilder() + .WithUrl(baseUrl) + .WithConsoleLogger() + .Build(); + try { await connection.StartAsync(); diff --git a/src/Microsoft.AspNetCore.SignalR.Client.Core/ConnectionFactoryDelegate.cs b/src/Microsoft.AspNetCore.SignalR.Client.Core/ConnectionFactoryDelegate.cs new file mode 100644 index 0000000000..cdea9687de --- /dev/null +++ b/src/Microsoft.AspNetCore.SignalR.Client.Core/ConnectionFactoryDelegate.cs @@ -0,0 +1,9 @@ +// 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.Sockets.Client; + +namespace Microsoft.AspNetCore.SignalR.Client +{ + public delegate IConnection ConnectionFactoryDelegate(); +} diff --git a/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.cs b/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.cs index 15fe5dafbd..cb86c85c3a 100644 --- a/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.cs +++ b/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnection.cs @@ -51,15 +51,6 @@ namespace Microsoft.AspNetCore.SignalR.Client remove { _connection.Closed -= value; } } - public HubConnection(IConnection connection) - : this(connection, new JsonHubProtocol(new JsonSerializer()), null) - { } - - // These are only really needed for tests now... - public HubConnection(IConnection connection, ILoggerFactory loggerFactory) - : this(connection, new JsonHubProtocol(new JsonSerializer()), loggerFactory) - { } - public HubConnection(IConnection connection, IHubProtocol protocol, ILoggerFactory loggerFactory) { if (connection == null) diff --git a/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnectionBuilder.cs b/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnectionBuilder.cs new file mode 100644 index 0000000000..dbdb2c8e7b --- /dev/null +++ b/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnectionBuilder.cs @@ -0,0 +1,75 @@ +// 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.ComponentModel; +using Microsoft.AspNetCore.SignalR.Internal.Protocol; +using Newtonsoft.Json; + +namespace Microsoft.AspNetCore.SignalR.Client +{ + public class HubConnectionBuilder : IHubConnectionBuilder + { + private readonly Dictionary, object> _settings = new Dictionary, object>(); + private ConnectionFactoryDelegate _connectionFactoryDelegate; + + public void ConfigureConnectionFactory(ConnectionFactoryDelegate connectionFactoryDelegate) => + _connectionFactoryDelegate = connectionFactoryDelegate; + + public void AddSetting(string name, T value) + { + _settings[new KeyValuePair(name, typeof(T))] = value; + } + + public bool TryGetSetting(string name, out T value) + { + value = default(T); + if (!_settings.TryGetValue(new KeyValuePair(name, typeof(T)), out var setting)) + { + return false; + } + + value = (T)setting; + return true; + } + + public HubConnection Build() + { + if (_connectionFactoryDelegate == null) + { + throw new InvalidOperationException("Cannot create IConnection instance. The connection factory was not configured."); + } + + var connection = _connectionFactoryDelegate(); + var loggerFactory = ((IHubConnectionBuilder)this).GetLoggerFactory(); + var hubProtocol = ((IHubConnectionBuilder)this).GetHubProtocol(); + + return new HubConnection(connection, hubProtocol ?? new JsonHubProtocol(new JsonSerializer()), loggerFactory); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + { + return base.GetHashCode(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) + { + return base.Equals(obj); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public override string ToString() + { + return base.ToString(); + } + + [EditorBrowsable(EditorBrowsableState.Never)] + public new Type GetType() + { + return base.GetType(); + } + } +} diff --git a/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnectionBuilderDefaults.cs b/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnectionBuilderDefaults.cs new file mode 100644 index 0000000000..07b79b4ea9 --- /dev/null +++ b/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnectionBuilderDefaults.cs @@ -0,0 +1,11 @@ +// 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. + +namespace Microsoft.AspNetCore.SignalR.Client +{ + public static class HubConnectionBuilderDefaults + { + public static readonly string LoggerFactoryKey = "LoggerFactory"; + public static readonly string HubProtocolKey = "HubProtocol"; + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnectionBuilderExtensions.cs b/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnectionBuilderExtensions.cs new file mode 100644 index 0000000000..c65070a445 --- /dev/null +++ b/src/Microsoft.AspNetCore.SignalR.Client.Core/HubConnectionBuilderExtensions.cs @@ -0,0 +1,46 @@ +// 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.SignalR.Internal.Protocol; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; + +namespace Microsoft.AspNetCore.SignalR.Client +{ + public static class HubConnectionBuilderExtensions + { + public static IHubConnectionBuilder WithHubProtocol(this IHubConnectionBuilder hubConnectionBuilder, IHubProtocol hubProtocol) + { + hubConnectionBuilder.AddSetting(HubConnectionBuilderDefaults.HubProtocolKey, hubProtocol); + return hubConnectionBuilder; + } + + public static IHubConnectionBuilder WithJsonProtocol(this IHubConnectionBuilder hubConnectionBuilder) + { + return hubConnectionBuilder.WithHubProtocol(new JsonHubProtocol(new JsonSerializer())); + } + + public static IHubConnectionBuilder WithMessagePackProtocol(this IHubConnectionBuilder hubConnectionBuilder) + { + return hubConnectionBuilder.WithHubProtocol(new MessagePackHubProtocol()); + } + + public static IHubConnectionBuilder WithLogger(this IHubConnectionBuilder hubConnectionBuilder, ILoggerFactory loggerFactory) + { + hubConnectionBuilder.AddSetting(HubConnectionBuilderDefaults.LoggerFactoryKey, loggerFactory); + return hubConnectionBuilder; + } + + public static ILoggerFactory GetLoggerFactory(this IHubConnectionBuilder hubConnectionBuilder) + { + hubConnectionBuilder.TryGetSetting(HubConnectionBuilderDefaults.LoggerFactoryKey, out var loggerFactory); + return loggerFactory; + } + + public static IHubProtocol GetHubProtocol(this IHubConnectionBuilder hubConnectionBuilder) + { + hubConnectionBuilder.TryGetSetting(HubConnectionBuilderDefaults.LoggerFactoryKey, out var hubProtocol); + return hubProtocol; + } + } +} diff --git a/src/Microsoft.AspNetCore.SignalR.Client.Core/IHubConnectionBuilder.cs b/src/Microsoft.AspNetCore.SignalR.Client.Core/IHubConnectionBuilder.cs new file mode 100644 index 0000000000..6d852398ca --- /dev/null +++ b/src/Microsoft.AspNetCore.SignalR.Client.Core/IHubConnectionBuilder.cs @@ -0,0 +1,13 @@ +// 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. + +namespace Microsoft.AspNetCore.SignalR.Client +{ + public interface IHubConnectionBuilder + { + void AddSetting(string name, T value); + bool TryGetSetting(string name, out T value); + void ConfigureConnectionFactory(ConnectionFactoryDelegate connectionFactory); + HubConnection Build(); + } +} diff --git a/src/Microsoft.AspNetCore.SignalR.Client/HubConnectionBuilderExtensions.cs b/src/Microsoft.AspNetCore.SignalR.Client/HubConnectionBuilderExtensions.cs new file mode 100644 index 0000000000..b69d230c61 --- /dev/null +++ b/src/Microsoft.AspNetCore.SignalR.Client/HubConnectionBuilderExtensions.cs @@ -0,0 +1,26 @@ +// 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 Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.SignalR.Client +{ + public static class HubConnectionBuilderExtensions + { + public static IHubConnectionBuilder WithConsoleLogger(this IHubConnectionBuilder hubConnectionBuilder) + { + return hubConnectionBuilder.WithConsoleLogger(new LoggerFactory()); + } + + public static IHubConnectionBuilder WithConsoleLogger(this IHubConnectionBuilder hubConnectionBuilder, ILoggerFactory loggerFactory) + { + if (loggerFactory == null) + { + throw new ArgumentNullException(nameof(loggerFactory)); + } + + return hubConnectionBuilder.WithLogger(loggerFactory.AddConsole()); + } + } +} diff --git a/src/Microsoft.AspNetCore.SignalR.Client/HubConnectionBuilderHttpExtensions.cs b/src/Microsoft.AspNetCore.SignalR.Client/HubConnectionBuilderHttpExtensions.cs new file mode 100644 index 0000000000..4d5e0fb908 --- /dev/null +++ b/src/Microsoft.AspNetCore.SignalR.Client/HubConnectionBuilderHttpExtensions.cs @@ -0,0 +1,71 @@ +// 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 Microsoft.AspNetCore.Sockets; +using Microsoft.AspNetCore.Sockets.Client; + +namespace Microsoft.AspNetCore.SignalR.Client +{ + public static class HubConnectionBuilderHttpExtensions + { + public static readonly string TransportTypeKey = "TransportType"; + public static readonly string HttpMessageHandlerKey = "HttpMessageHandler"; + + public static IHubConnectionBuilder WithUrl(this IHubConnectionBuilder hubConnectionBuilder, string url) + { + if (url == null) + { + throw new ArgumentNullException(nameof(url)); + } + + return hubConnectionBuilder.WithUrl(new Uri(url)); + } + + public static IHubConnectionBuilder WithUrl(this IHubConnectionBuilder hubConnectionBuilder, Uri url) + { + if (url == null) + { + throw new ArgumentNullException(nameof(url)); + } + + hubConnectionBuilder.ConfigureConnectionFactory(() => + { + return new HttpConnection(url, + hubConnectionBuilder.GetTransportType(), + hubConnectionBuilder.GetLoggerFactory(), + hubConnectionBuilder.GetMessageHandler()); + }); + return hubConnectionBuilder; + } + + public static IHubConnectionBuilder WithTransportType(this IHubConnectionBuilder hubConnectionBuilder, TransportType transportType) + { + hubConnectionBuilder.AddSetting(TransportTypeKey, transportType); + return hubConnectionBuilder; + } + + public static IHubConnectionBuilder WithMessageHandler(this IHubConnectionBuilder hubConnectionBuilder, HttpMessageHandler httpMessageHandler) + { + hubConnectionBuilder.AddSetting(HttpMessageHandlerKey, httpMessageHandler); + return hubConnectionBuilder; + } + + public static TransportType GetTransportType(this IHubConnectionBuilder hubConnectionBuilder) + { + if (hubConnectionBuilder.TryGetSetting(TransportTypeKey, out var transportType)) + { + return transportType; + } + + return TransportType.All; + } + + public static HttpMessageHandler GetMessageHandler(this IHubConnectionBuilder hubConnectionBuilder) + { + hubConnectionBuilder.TryGetSetting(HttpMessageHandlerKey, out var messageHandler); + return messageHandler; + } + } +} diff --git a/src/Microsoft.AspNetCore.SignalR.Client/Microsoft.AspNetCore.SignalR.Client.csproj b/src/Microsoft.AspNetCore.SignalR.Client/Microsoft.AspNetCore.SignalR.Client.csproj index 24c801ac15..c8a1985611 100644 --- a/src/Microsoft.AspNetCore.SignalR.Client/Microsoft.AspNetCore.SignalR.Client.csproj +++ b/src/Microsoft.AspNetCore.SignalR.Client/Microsoft.AspNetCore.SignalR.Client.csproj @@ -10,4 +10,9 @@ + + + + + diff --git a/src/Microsoft.AspNetCore.SignalR.Core/IHubContext.cs b/src/Microsoft.AspNetCore.SignalR.Core/IHubContext.cs index 88c004aec2..2c92da730c 100644 --- a/src/Microsoft.AspNetCore.SignalR.Core/IHubContext.cs +++ b/src/Microsoft.AspNetCore.SignalR.Core/IHubContext.cs @@ -1,11 +1,6 @@ // 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.Linq; -using System.Threading.Tasks; - namespace Microsoft.AspNetCore.SignalR { public interface IHubContext where THub : Hub diff --git a/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/HubConnectionTests.cs b/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/HubConnectionTests.cs index 03e656a849..0b910f2405 100644 --- a/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/HubConnectionTests.cs +++ b/test/Microsoft.AspNetCore.SignalR.Client.FunctionalTests/HubConnectionTests.cs @@ -44,8 +44,13 @@ namespace Microsoft.AspNetCore.SignalR.Client.FunctionalTests { using (StartLog(out var loggerFactory)) { - var httpConnection = new HttpConnection(new Uri(_serverFixture.BaseUrl + path), transportType, loggerFactory); - var connection = new HubConnection(httpConnection, protocol, loggerFactory); + var connection = new HubConnectionBuilder() + .WithUrl(_serverFixture.BaseUrl + path) + .WithTransportType(transportType) + .WithLogger(loggerFactory) + .WithHubProtocol(protocol) + .Build(); + try { await connection.StartAsync().OrTimeout(); diff --git a/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionBuilderTests.cs b/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionBuilderTests.cs new file mode 100644 index 0000000000..d3ad0e3b61 --- /dev/null +++ b/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionBuilderTests.cs @@ -0,0 +1,34 @@ +// 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 Xunit; + +namespace Microsoft.AspNetCore.SignalR.Client.Tests +{ + public class HubConnectionBuilderTests + { + [Fact] + public void HubConnectionBuiderThrowsIfConnectionFactoryNotConfigured() + { + var ex = Assert.Throws(() => new HubConnectionBuilder().Build()); + Assert.Equal("Cannot create IConnection instance. The connection factory was not configured.", ex.Message); + } + + [Fact] + public void WithUrlThrowsForNullUrls() + { + Assert.Equal("url", + Assert.Throws(() => new HubConnectionBuilder().WithUrl((string)null)).ParamName); + Assert.Equal("url", + Assert.Throws(() => new HubConnectionBuilder().WithUrl((Uri)null)).ParamName); + } + + [Fact] + public void WithConsoleLoggerThrowsForNullLoggerFactory() + { + Assert.Equal("loggerFactory", + Assert.Throws(() => new HubConnectionBuilder().WithConsoleLogger(null)).ParamName); + } + } +} diff --git a/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionTests.cs b/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionTests.cs index d35eaa4f3c..f21ffe604d 100644 --- a/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionTests.cs +++ b/test/Microsoft.AspNetCore.SignalR.Client.Tests/HubConnectionTests.cs @@ -12,6 +12,7 @@ using Microsoft.AspNetCore.SignalR.Tests.Common; using Microsoft.AspNetCore.Sockets.Client; using Microsoft.Extensions.Logging; using Moq; +using Newtonsoft.Json; using Xunit; namespace Microsoft.AspNetCore.SignalR.Client.Tests @@ -24,7 +25,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests var connection = new Mock(); connection.SetupGet(p => p.Features).Returns(new FeatureCollection()); connection.Setup(m => m.StartAsync()).Returns(Task.CompletedTask).Verifiable(); - var hubConnection = new HubConnection(connection.Object); + var hubConnection = new HubConnection(connection.Object, Mock.Of(), null); await hubConnection.StartAsync(); connection.Verify(c => c.StartAsync(), Times.Once()); @@ -35,7 +36,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests { var connection = new Mock(); connection.Setup(m => m.StartAsync()).Verifiable(); - var hubConnection = new HubConnection(connection.Object); + var hubConnection = new HubConnection(connection.Object, Mock.Of(), null); await hubConnection.DisposeAsync(); connection.Verify(c => c.DisposeAsync(), Times.Once()); @@ -58,7 +59,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests public async Task HubConnectionConnectedEventRaisedWhenTheClientIsConnected() { var connection = new TestConnection(); - var hubConnection = new HubConnection(connection); + var hubConnection = new HubConnection(connection, Mock.Of(), null); try { var connectedEventRaisedTcs = new TaskCompletionSource(); @@ -81,7 +82,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests [Fact] public async Task ClosedEventRaisedWhenTheClientIsStopped() { - var hubConnection = new HubConnection(new TestConnection()); + var hubConnection = new HubConnection(new TestConnection(), Mock.Of(), null); var closedEventTcs = new TaskCompletionSource(); hubConnection.Closed += e => { @@ -99,7 +100,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests public async Task CannotCallInvokeOnClosedHubConnection() { var connection = new TestConnection(); - var hubConnection = new HubConnection(connection, new LoggerFactory()); + var hubConnection = new HubConnection(connection, new JsonHubProtocol(new JsonSerializer()), new LoggerFactory()); await hubConnection.StartAsync(); await hubConnection.DisposeAsync(); @@ -113,7 +114,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests public async Task PendingInvocationsAreCancelledWhenConnectionClosesCleanly() { var connection = new TestConnection(); - var hubConnection = new HubConnection(connection, new LoggerFactory()); + var hubConnection = new HubConnection(connection, new JsonHubProtocol(new JsonSerializer()), new LoggerFactory()); await hubConnection.StartAsync(); var invokeTask = hubConnection.InvokeAsync("testMethod"); @@ -133,7 +134,7 @@ namespace Microsoft.AspNetCore.SignalR.Client.Tests .Callback(() => mockConnection.Raise(c => c.Closed += null, exception)) .Returns(Task.FromResult(null)); - var hubConnection = new HubConnection(mockConnection.Object, new LoggerFactory()); + var hubConnection = new HubConnection(mockConnection.Object, Mock.Of(), new LoggerFactory()); await hubConnection.StartAsync(); var invokeTask = hubConnection.InvokeAsync("testMethod"); diff --git a/test/Microsoft.AspNetCore.SignalR.Tests/EndToEndTests.cs b/test/Microsoft.AspNetCore.SignalR.Tests/EndToEndTests.cs index 292d6c4d10..f2943c73ea 100644 --- a/test/Microsoft.AspNetCore.SignalR.Tests/EndToEndTests.cs +++ b/test/Microsoft.AspNetCore.SignalR.Tests/EndToEndTests.cs @@ -284,7 +284,11 @@ namespace Microsoft.AspNetCore.SignalR.Tests var url = _serverFixture.BaseUrl + "/uncreatable"; - var connection = new HubConnection(new HttpConnection(new Uri(url), transportType, loggerFactory), loggerFactory); + var connection = new HubConnectionBuilder() + .WithUrl(new Uri(url)) + .WithTransportType(transportType) + .WithLogger(loggerFactory) + .Build(); try { var closeTcs = new TaskCompletionSource();