Skip to content

Commit 2ad8121

Browse files
Hub filters! (#21278)
1 parent bad6e32 commit 2ad8121

16 files changed

+1544
-49
lines changed

src/SignalR/perf/Microbenchmarks/DefaultHubDispatcherBenchmark.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ public void GlobalSetup()
3838
serviceScopeFactory,
3939
new HubContext<TestHub>(new DefaultHubLifetimeManager<TestHub>(NullLogger<DefaultHubLifetimeManager<TestHub>>.Instance)),
4040
enableDetailedErrors: false,
41-
new Logger<DefaultHubDispatcher<TestHub>>(NullLoggerFactory.Instance));
41+
new Logger<DefaultHubDispatcher<TestHub>>(NullLoggerFactory.Instance),
42+
hubFilters: null);
4243

4344
var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
4445
var connection = new DefaultConnectionContext(Guid.NewGuid().ToString(), pair.Application, pair.Transport);

src/SignalR/samples/SignalRSamples/Startup.cs

+1-10
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4-
using System;
5-
using System.IO;
64
using System.Reflection;
7-
using System.Threading.Tasks;
85
using Microsoft.AspNetCore.Builder;
96
using Microsoft.AspNetCore.Hosting;
107
using Microsoft.Extensions.DependencyInjection;
118
using Microsoft.Extensions.Hosting;
129
using System.Text.Json;
13-
using System.Text.Json.Serialization;
1410
using SignalRSamples.ConnectionHandlers;
1511
using SignalRSamples.Hubs;
1612

1713
namespace SignalRSamples
1814
{
1915
public class Startup
2016
{
21-
2217
private readonly JsonWriterOptions _jsonWriterOptions = new JsonWriterOptions { Indented = true };
2318

2419
// This method gets called by the runtime. Use this method to add services to the container.
@@ -27,11 +22,7 @@ public void ConfigureServices(IServiceCollection services)
2722
{
2823
services.AddConnections();
2924

30-
services.AddSignalR(options =>
31-
{
32-
// Faster pings for testing
33-
options.KeepAliveInterval = TimeSpan.FromSeconds(5);
34-
})
25+
services.AddSignalR()
3526
.AddMessagePackProtocol();
3627
//.AddStackExchangeRedis();
3728
}

src/SignalR/server/Core/ref/Microsoft.AspNetCore.SignalR.Core.netcoreapp.cs

+25-2
Original file line numberDiff line numberDiff line change
@@ -181,12 +181,23 @@ public void Reset() { }
181181
}
182182
public partial class HubInvocationContext
183183
{
184+
public HubInvocationContext(Microsoft.AspNetCore.SignalR.HubCallerContext context, System.IServiceProvider serviceProvider, Microsoft.AspNetCore.SignalR.Hub hub, System.Reflection.MethodInfo hubMethod, System.Collections.Generic.IReadOnlyList<object> hubMethodArguments) { }
185+
[System.ObsoleteAttribute("This constructor is obsolete and will be removed in a future version. The recommended alternative is to use the other constructor.")]
184186
public HubInvocationContext(Microsoft.AspNetCore.SignalR.HubCallerContext context, string hubMethodName, object[] hubMethodArguments) { }
185-
public HubInvocationContext(Microsoft.AspNetCore.SignalR.HubCallerContext context, System.Type hubType, string hubMethodName, object[] hubMethodArguments) { }
186187
public Microsoft.AspNetCore.SignalR.HubCallerContext Context { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
188+
public Microsoft.AspNetCore.SignalR.Hub Hub { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
189+
public System.Reflection.MethodInfo HubMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
187190
public System.Collections.Generic.IReadOnlyList<object> HubMethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
191+
[System.ObsoleteAttribute("This property is obsolete and will be removed in a future version. Use HubMethod.Name instead.")]
188192
public string HubMethodName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
189-
public System.Type HubType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
193+
public System.IServiceProvider ServiceProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
194+
}
195+
public sealed partial class HubLifetimeContext
196+
{
197+
public HubLifetimeContext(Microsoft.AspNetCore.SignalR.HubCallerContext context, System.IServiceProvider serviceProvider, Microsoft.AspNetCore.SignalR.Hub hub) { }
198+
public Microsoft.AspNetCore.SignalR.HubCallerContext Context { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
199+
public Microsoft.AspNetCore.SignalR.Hub Hub { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
200+
public System.IServiceProvider ServiceProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
190201
}
191202
public abstract partial class HubLifetimeManager<THub> where THub : Microsoft.AspNetCore.SignalR.Hub
192203
{
@@ -227,6 +238,12 @@ public HubOptions() { }
227238
public int? StreamBufferCapacity { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
228239
public System.Collections.Generic.IList<string> SupportedProtocols { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
229240
}
241+
public static partial class HubOptionsExtensions
242+
{
243+
public static void AddFilter(this Microsoft.AspNetCore.SignalR.HubOptions options, Microsoft.AspNetCore.SignalR.IHubFilter hubFilter) { }
244+
public static void AddFilter(this Microsoft.AspNetCore.SignalR.HubOptions options, System.Type filterType) { }
245+
public static void AddFilter<TFilter>(this Microsoft.AspNetCore.SignalR.HubOptions options) where TFilter : Microsoft.AspNetCore.SignalR.IHubFilter { }
246+
}
230247
public partial class HubOptionsSetup : Microsoft.Extensions.Options.IConfigureOptions<Microsoft.AspNetCore.SignalR.HubOptions>
231248
{
232249
public HubOptionsSetup(System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol> protocols) { }
@@ -294,6 +311,12 @@ public partial interface IHubContext<THub, T> where THub : Microsoft.AspNetCore.
294311
Microsoft.AspNetCore.SignalR.IHubClients<T> Clients { get; }
295312
Microsoft.AspNetCore.SignalR.IGroupManager Groups { get; }
296313
}
314+
public partial interface IHubFilter
315+
{
316+
System.Threading.Tasks.ValueTask<object> InvokeMethodAsync(Microsoft.AspNetCore.SignalR.HubInvocationContext invocationContext, System.Func<Microsoft.AspNetCore.SignalR.HubInvocationContext, System.Threading.Tasks.ValueTask<object>> next);
317+
System.Threading.Tasks.Task OnConnectedAsync(Microsoft.AspNetCore.SignalR.HubLifetimeContext context, System.Func<Microsoft.AspNetCore.SignalR.HubLifetimeContext, System.Threading.Tasks.Task> next) { throw null; }
318+
System.Threading.Tasks.Task OnDisconnectedAsync(Microsoft.AspNetCore.SignalR.HubLifetimeContext context, System.Exception exception, System.Func<Microsoft.AspNetCore.SignalR.HubLifetimeContext, System.Exception, System.Threading.Tasks.Task> next) { throw null; }
319+
}
297320
public partial interface IHubProtocolResolver
298321
{
299322
System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol> AllProtocols { get; }

src/SignalR/server/Core/src/HubConnectionHandler.cs

+16-1
Original file line numberDiff line numberDiff line change
@@ -64,22 +64,37 @@ IServiceScopeFactory serviceScopeFactory
6464
_userIdProvider = userIdProvider;
6565

6666
_enableDetailedErrors = false;
67+
68+
List<IHubFilter> hubFilters = null;
6769
if (_hubOptions.UserHasSetValues)
6870
{
6971
_maximumMessageSize = _hubOptions.MaximumReceiveMessageSize;
7072
_enableDetailedErrors = _hubOptions.EnableDetailedErrors ?? _enableDetailedErrors;
73+
74+
if (_hubOptions.HubFilters != null)
75+
{
76+
hubFilters = new List<IHubFilter>();
77+
hubFilters.AddRange(_hubOptions.HubFilters);
78+
}
7179
}
7280
else
7381
{
7482
_maximumMessageSize = _globalHubOptions.MaximumReceiveMessageSize;
7583
_enableDetailedErrors = _globalHubOptions.EnableDetailedErrors ?? _enableDetailedErrors;
84+
85+
if (_globalHubOptions.HubFilters != null)
86+
{
87+
hubFilters = new List<IHubFilter>();
88+
hubFilters.AddRange(_globalHubOptions.HubFilters);
89+
}
7690
}
7791

7892
_dispatcher = new DefaultHubDispatcher<THub>(
7993
serviceScopeFactory,
8094
new HubContext<THub>(lifetimeManager),
8195
_enableDetailedErrors,
82-
new Logger<DefaultHubDispatcher<THub>>(loggerFactory));
96+
new Logger<DefaultHubDispatcher<THub>>(loggerFactory),
97+
hubFilters);
8398
}
8499

85100
/// <inheritdoc />

src/SignalR/server/Core/src/HubInvocationContext.cs

+39-10
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33

44
using System;
55
using System.Collections.Generic;
6-
using Microsoft.AspNetCore.Authorization;
6+
using System.Linq;
7+
using System.Reflection;
8+
using Microsoft.Extensions.Internal;
79

810
namespace Microsoft.AspNetCore.SignalR
911
{
@@ -12,16 +14,27 @@ namespace Microsoft.AspNetCore.SignalR
1214
/// </summary>
1315
public class HubInvocationContext
1416
{
17+
internal ObjectMethodExecutor ObjectMethodExecutor { get; }
18+
1519
/// <summary>
1620
/// Instantiates a new instance of the <see cref="HubInvocationContext"/> class.
1721
/// </summary>
1822
/// <param name="context">Context for the active Hub connection and caller.</param>
19-
/// <param name="hubType">The type of the Hub.</param>
20-
/// <param name="hubMethodName">The name of the Hub method being invoked.</param>
23+
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> specific to the scope of this Hub method invocation.</param>
24+
/// <param name="hub">The instance of the Hub.</param>
25+
/// <param name="hubMethod">The <see cref="MethodInfo"/> for the Hub method being invoked.</param>
2126
/// <param name="hubMethodArguments">The arguments provided by the client.</param>
22-
public HubInvocationContext(HubCallerContext context, Type hubType, string hubMethodName, object[] hubMethodArguments): this(context, hubMethodName, hubMethodArguments)
27+
public HubInvocationContext(HubCallerContext context, IServiceProvider serviceProvider, Hub hub, MethodInfo hubMethod, IReadOnlyList<object> hubMethodArguments)
2328
{
24-
HubType = hubType;
29+
Hub = hub;
30+
ServiceProvider = serviceProvider;
31+
HubMethod = hubMethod;
32+
HubMethodArguments = hubMethodArguments;
33+
Context = context;
34+
35+
#pragma warning disable CS0618 // Type or member is obsolete
36+
HubMethodName = HubMethod.Name;
37+
#pragma warning restore CS0618 // Type or member is obsolete
2538
}
2639

2740
/// <summary>
@@ -30,11 +43,16 @@ public HubInvocationContext(HubCallerContext context, Type hubType, string hubMe
3043
/// <param name="context">Context for the active Hub connection and caller.</param>
3144
/// <param name="hubMethodName">The name of the Hub method being invoked.</param>
3245
/// <param name="hubMethodArguments">The arguments provided by the client.</param>
46+
[Obsolete("This constructor is obsolete and will be removed in a future version. The recommended alternative is to use the other constructor.")]
3347
public HubInvocationContext(HubCallerContext context, string hubMethodName, object[] hubMethodArguments)
3448
{
35-
HubMethodName = hubMethodName;
36-
HubMethodArguments = hubMethodArguments;
37-
Context = context;
49+
throw new NotSupportedException("This constructor no longer works. Use the other constructor.");
50+
}
51+
52+
internal HubInvocationContext(ObjectMethodExecutor objectMethodExecutor, HubCallerContext context, IServiceProvider serviceProvider, Hub hub, object[] hubMethodArguments)
53+
: this(context, serviceProvider, hub, objectMethodExecutor.MethodInfo, hubMethodArguments)
54+
{
55+
ObjectMethodExecutor = objectMethodExecutor;
3856
}
3957

4058
/// <summary>
@@ -43,18 +61,29 @@ public HubInvocationContext(HubCallerContext context, string hubMethodName, obje
4361
public HubCallerContext Context { get; }
4462

4563
/// <summary>
46-
/// Gets the Hub type.
64+
/// Gets the Hub instance.
4765
/// </summary>
48-
public Type HubType { get; }
66+
public Hub Hub { get; }
4967

5068
/// <summary>
5169
/// Gets the name of the Hub method being invoked.
5270
/// </summary>
71+
[Obsolete("This property is obsolete and will be removed in a future version. Use HubMethod.Name instead.")]
5372
public string HubMethodName { get; }
5473

5574
/// <summary>
5675
/// Gets the arguments provided by the client.
5776
/// </summary>
5877
public IReadOnlyList<object> HubMethodArguments { get; }
78+
79+
/// <summary>
80+
/// The <see cref="IServiceProvider"/> specific to the scope of this Hub method invocation.
81+
/// </summary>
82+
public IServiceProvider ServiceProvider { get; }
83+
84+
/// <summary>
85+
/// The <see cref="MethodInfo"/> for the Hub method being invoked.
86+
/// </summary>
87+
public MethodInfo HubMethod { get; }
5988
}
6089
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
#nullable enable
5+
6+
using System;
7+
8+
namespace Microsoft.AspNetCore.SignalR
9+
{
10+
/// <summary>
11+
/// Context for the hub lifetime events <see cref="Hub.OnConnectedAsync"/> and <see cref="Hub.OnDisconnectedAsync(Exception)"/>.
12+
/// </summary>
13+
public sealed class HubLifetimeContext
14+
{
15+
/// <summary>
16+
/// Instantiates a new instance of the <see cref="HubLifetimeContext"/> class.
17+
/// </summary>
18+
/// <param name="context">Context for the active Hub connection and caller.</param>
19+
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> specific to the scope of this Hub method invocation.</param>
20+
/// <param name="hub">The instance of the Hub.</param>
21+
public HubLifetimeContext(HubCallerContext context, IServiceProvider serviceProvider, Hub hub)
22+
{
23+
Hub = hub;
24+
ServiceProvider = serviceProvider;
25+
Context = context;
26+
}
27+
28+
/// <summary>
29+
/// Gets the context for the active Hub connection and caller.
30+
/// </summary>
31+
public HubCallerContext Context { get; }
32+
33+
/// <summary>
34+
/// Gets the Hub instance.
35+
/// </summary>
36+
public Hub Hub { get; }
37+
38+
/// <summary>
39+
/// The <see cref="IServiceProvider"/> specific to the scope of this Hub method invocation.
40+
/// </summary>
41+
public IServiceProvider ServiceProvider { get; }
42+
}
43+
}

src/SignalR/server/Core/src/HubOptions.cs

+2
Original file line numberDiff line numberDiff line change
@@ -51,5 +51,7 @@ public class HubOptions
5151
/// Gets or sets the max buffer size for client upload streams. The default size is 10.
5252
/// </summary>
5353
public int? StreamBufferCapacity { get; set; } = null;
54+
55+
internal List<IHubFilter> HubFilters { get; set; } = null;
5456
}
5557
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3+
4+
#nullable enable
5+
6+
using System;
7+
using System.Collections.Generic;
8+
using Microsoft.AspNetCore.SignalR.Internal;
9+
10+
namespace Microsoft.AspNetCore.SignalR
11+
{
12+
/// <summary>
13+
/// Methods to add <see cref="IHubFilter"/>'s to Hubs.
14+
/// </summary>
15+
public static class HubOptionsExtensions
16+
{
17+
/// <summary>
18+
/// Adds an instance of an <see cref="IHubFilter"/> to the <see cref="HubOptions"/>.
19+
/// </summary>
20+
/// <param name="options">The options to add a filter to.</param>
21+
/// <param name="hubFilter">The filter instance to add to the options.</param>
22+
public static void AddFilter(this HubOptions options, IHubFilter hubFilter)
23+
{
24+
_ = options ?? throw new ArgumentNullException(nameof(options));
25+
_ = hubFilter ?? throw new ArgumentNullException(nameof(hubFilter));
26+
27+
if (options.HubFilters == null)
28+
{
29+
options.HubFilters = new List<IHubFilter>();
30+
}
31+
32+
options.HubFilters.Add(hubFilter);
33+
}
34+
35+
/// <summary>
36+
/// Adds an <see cref="IHubFilter"/> type to the <see cref="HubOptions"/> that will be resolved via DI or type activated.
37+
/// </summary>
38+
/// <typeparam name="TFilter">The <see cref="IHubFilter"/> type that will be added to the options.</typeparam>
39+
/// <param name="options">The options to add a filter to.</param>
40+
public static void AddFilter<TFilter>(this HubOptions options) where TFilter : IHubFilter
41+
{
42+
_ = options ?? throw new ArgumentNullException(nameof(options));
43+
44+
options.AddFilter(typeof(TFilter));
45+
}
46+
47+
/// <summary>
48+
/// Adds an <see cref="IHubFilter"/> type to the <see cref="HubOptions"/> that will be resolved via DI or type activated.
49+
/// </summary>
50+
/// <param name="options">The options to add a filter to.</param>
51+
/// <param name="filterType">The <see cref="IHubFilter"/> type that will be added to the options.</param>
52+
public static void AddFilter(this HubOptions options, Type filterType)
53+
{
54+
_ = options ?? throw new ArgumentNullException(nameof(options));
55+
_ = filterType ?? throw new ArgumentNullException(nameof(filterType));
56+
57+
options.AddFilter(new HubFilterFactory(filterType));
58+
}
59+
}
60+
}

src/SignalR/server/Core/src/HubOptionsSetup`T.cs

+6-5
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,7 @@ public HubOptionsSetup(IOptions<HubOptions> options)
1717
public void Configure(HubOptions<THub> options)
1818
{
1919
// Do a deep copy, otherwise users modifying the HubOptions<THub> list would be changing the global options list
20-
options.SupportedProtocols = new List<string>(_hubOptions.SupportedProtocols.Count);
21-
foreach (var protocol in _hubOptions.SupportedProtocols)
22-
{
23-
options.SupportedProtocols.Add(protocol);
24-
}
20+
options.SupportedProtocols = new List<string>(_hubOptions.SupportedProtocols);
2521
options.KeepAliveInterval = _hubOptions.KeepAliveInterval;
2622
options.HandshakeTimeout = _hubOptions.HandshakeTimeout;
2723
options.ClientTimeoutInterval = _hubOptions.ClientTimeoutInterval;
@@ -30,6 +26,11 @@ public void Configure(HubOptions<THub> options)
3026
options.StreamBufferCapacity = _hubOptions.StreamBufferCapacity;
3127

3228
options.UserHasSetValues = true;
29+
30+
if (_hubOptions.HubFilters != null)
31+
{
32+
options.HubFilters = new List<IHubFilter>(_hubOptions.HubFilters);
33+
}
3334
}
3435
}
3536
}

0 commit comments

Comments
 (0)