Skip to content

Hub filters! #21278

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 29 commits into from
May 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public void GlobalSetup()
serviceScopeFactory,
new HubContext<TestHub>(new DefaultHubLifetimeManager<TestHub>(NullLogger<DefaultHubLifetimeManager<TestHub>>.Instance)),
enableDetailedErrors: false,
new Logger<DefaultHubDispatcher<TestHub>>(NullLoggerFactory.Instance));
new Logger<DefaultHubDispatcher<TestHub>>(NullLoggerFactory.Instance),
hubFilters: null);

var pair = DuplexPipe.CreateConnectionPair(PipeOptions.Default, PipeOptions.Default);
var connection = new DefaultConnectionContext(Guid.NewGuid().ToString(), pair.Application, pair.Transport);
Expand Down
11 changes: 1 addition & 10 deletions src/SignalR/samples/SignalRSamples/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
// 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.IO;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Text.Json;
using System.Text.Json.Serialization;
using SignalRSamples.ConnectionHandlers;
using SignalRSamples.Hubs;

namespace SignalRSamples
{
public class Startup
{

private readonly JsonWriterOptions _jsonWriterOptions = new JsonWriterOptions { Indented = true };

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

services.AddSignalR(options =>
{
// Faster pings for testing
options.KeepAliveInterval = TimeSpan.FromSeconds(5);
})
services.AddSignalR()
.AddMessagePackProtocol();
//.AddStackExchangeRedis();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,12 +181,23 @@ public void Reset() { }
}
public partial class HubInvocationContext
{
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) { }
[System.ObsoleteAttribute("This constructor is obsolete and will be removed in a future version. The recommended alternative is to use the other constructor.")]
public HubInvocationContext(Microsoft.AspNetCore.SignalR.HubCallerContext context, string hubMethodName, object[] hubMethodArguments) { }
public HubInvocationContext(Microsoft.AspNetCore.SignalR.HubCallerContext context, System.Type hubType, string hubMethodName, object[] hubMethodArguments) { }
public Microsoft.AspNetCore.SignalR.HubCallerContext Context { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public Microsoft.AspNetCore.SignalR.Hub Hub { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.Reflection.MethodInfo HubMethod { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.Collections.Generic.IReadOnlyList<object> HubMethodArguments { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
[System.ObsoleteAttribute("This property is obsolete and will be removed in a future version. Use HubMethod.Name instead.")]
public string HubMethodName { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought we decided in API review that we were not going to obsolete HubMethodName.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, maybe. No one commented that so it was forgotten. Fortunately we can easily fix this :)

public System.Type HubType { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.IServiceProvider ServiceProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
}
public sealed partial class HubLifetimeContext
{
public HubLifetimeContext(Microsoft.AspNetCore.SignalR.HubCallerContext context, System.IServiceProvider serviceProvider, Microsoft.AspNetCore.SignalR.Hub hub) { }
public Microsoft.AspNetCore.SignalR.HubCallerContext Context { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public Microsoft.AspNetCore.SignalR.Hub Hub { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
public System.IServiceProvider ServiceProvider { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
}
public abstract partial class HubLifetimeManager<THub> where THub : Microsoft.AspNetCore.SignalR.Hub
{
Expand Down Expand Up @@ -227,6 +238,12 @@ public HubOptions() { }
public int? StreamBufferCapacity { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
public System.Collections.Generic.IList<string> SupportedProtocols { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] set { } }
}
public static partial class HubOptionsExtensions
{
public static void AddFilter(this Microsoft.AspNetCore.SignalR.HubOptions options, Microsoft.AspNetCore.SignalR.IHubFilter hubFilter) { }
public static void AddFilter(this Microsoft.AspNetCore.SignalR.HubOptions options, System.Type filterType) { }
public static void AddFilter<TFilter>(this Microsoft.AspNetCore.SignalR.HubOptions options) where TFilter : Microsoft.AspNetCore.SignalR.IHubFilter { }
}
public partial class HubOptionsSetup : Microsoft.Extensions.Options.IConfigureOptions<Microsoft.AspNetCore.SignalR.HubOptions>
{
public HubOptionsSetup(System.Collections.Generic.IEnumerable<Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol> protocols) { }
Expand Down Expand Up @@ -294,6 +311,12 @@ public partial interface IHubContext<THub, T> where THub : Microsoft.AspNetCore.
Microsoft.AspNetCore.SignalR.IHubClients<T> Clients { get; }
Microsoft.AspNetCore.SignalR.IGroupManager Groups { get; }
}
public partial interface IHubFilter
{
System.Threading.Tasks.ValueTask<object> InvokeMethodAsync(Microsoft.AspNetCore.SignalR.HubInvocationContext invocationContext, System.Func<Microsoft.AspNetCore.SignalR.HubInvocationContext, System.Threading.Tasks.ValueTask<object>> next);
System.Threading.Tasks.Task OnConnectedAsync(Microsoft.AspNetCore.SignalR.HubLifetimeContext context, System.Func<Microsoft.AspNetCore.SignalR.HubLifetimeContext, System.Threading.Tasks.Task> next) { throw null; }
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; }
}
public partial interface IHubProtocolResolver
{
System.Collections.Generic.IReadOnlyList<Microsoft.AspNetCore.SignalR.Protocol.IHubProtocol> AllProtocols { get; }
Expand Down
17 changes: 16 additions & 1 deletion src/SignalR/server/Core/src/HubConnectionHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,37 @@ IServiceScopeFactory serviceScopeFactory
_userIdProvider = userIdProvider;

_enableDetailedErrors = false;

List<IHubFilter> hubFilters = null;
if (_hubOptions.UserHasSetValues)
{
_maximumMessageSize = _hubOptions.MaximumReceiveMessageSize;
_enableDetailedErrors = _hubOptions.EnableDetailedErrors ?? _enableDetailedErrors;

if (_hubOptions.HubFilters != null)
{
hubFilters = new List<IHubFilter>();
hubFilters.AddRange(_hubOptions.HubFilters);
}
}
else
{
_maximumMessageSize = _globalHubOptions.MaximumReceiveMessageSize;
_enableDetailedErrors = _globalHubOptions.EnableDetailedErrors ?? _enableDetailedErrors;

if (_globalHubOptions.HubFilters != null)
{
hubFilters = new List<IHubFilter>();
hubFilters.AddRange(_globalHubOptions.HubFilters);
}
}

_dispatcher = new DefaultHubDispatcher<THub>(
serviceScopeFactory,
new HubContext<THub>(lifetimeManager),
_enableDetailedErrors,
new Logger<DefaultHubDispatcher<THub>>(loggerFactory));
new Logger<DefaultHubDispatcher<THub>>(loggerFactory),
hubFilters);
}

/// <inheritdoc />
Expand Down
49 changes: 39 additions & 10 deletions src/SignalR/server/Core/src/HubInvocationContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.Authorization;
using System.Linq;
using System.Reflection;
using Microsoft.Extensions.Internal;

namespace Microsoft.AspNetCore.SignalR
{
Expand All @@ -12,16 +14,27 @@ namespace Microsoft.AspNetCore.SignalR
/// </summary>
public class HubInvocationContext
{
internal ObjectMethodExecutor ObjectMethodExecutor { get; }

/// <summary>
/// Instantiates a new instance of the <see cref="HubInvocationContext"/> class.
/// </summary>
/// <param name="context">Context for the active Hub connection and caller.</param>
/// <param name="hubType">The type of the Hub.</param>
/// <param name="hubMethodName">The name of the Hub method being invoked.</param>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> specific to the scope of this Hub method invocation.</param>
/// <param name="hub">The instance of the Hub.</param>
/// <param name="hubMethod">The <see cref="MethodInfo"/> for the Hub method being invoked.</param>
/// <param name="hubMethodArguments">The arguments provided by the client.</param>
public HubInvocationContext(HubCallerContext context, Type hubType, string hubMethodName, object[] hubMethodArguments): this(context, hubMethodName, hubMethodArguments)
public HubInvocationContext(HubCallerContext context, IServiceProvider serviceProvider, Hub hub, MethodInfo hubMethod, IReadOnlyList<object> hubMethodArguments)
{
HubType = hubType;
Hub = hub;
ServiceProvider = serviceProvider;
HubMethod = hubMethod;
HubMethodArguments = hubMethodArguments;
Context = context;

#pragma warning disable CS0618 // Type or member is obsolete
HubMethodName = HubMethod.Name;
#pragma warning restore CS0618 // Type or member is obsolete
}

/// <summary>
Expand All @@ -30,11 +43,16 @@ public HubInvocationContext(HubCallerContext context, Type hubType, string hubMe
/// <param name="context">Context for the active Hub connection and caller.</param>
/// <param name="hubMethodName">The name of the Hub method being invoked.</param>
/// <param name="hubMethodArguments">The arguments provided by the client.</param>
[Obsolete("This constructor is obsolete and will be removed in a future version. The recommended alternative is to use the other constructor.")]
public HubInvocationContext(HubCallerContext context, string hubMethodName, object[] hubMethodArguments)
{
HubMethodName = hubMethodName;
HubMethodArguments = hubMethodArguments;
Context = context;
throw new NotSupportedException("This constructor no longer works. Use the other constructor.");
}

internal HubInvocationContext(ObjectMethodExecutor objectMethodExecutor, HubCallerContext context, IServiceProvider serviceProvider, Hub hub, object[] hubMethodArguments)
: this(context, serviceProvider, hub, objectMethodExecutor.MethodInfo, hubMethodArguments)
{
ObjectMethodExecutor = objectMethodExecutor;
}

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

/// <summary>
/// Gets the Hub type.
/// Gets the Hub instance.
/// </summary>
public Type HubType { get; }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we retain this property to avoid a breaking change?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not breaking because this was added in 5.0

public Hub Hub { get; }

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

/// <summary>
/// Gets the arguments provided by the client.
/// </summary>
public IReadOnlyList<object> HubMethodArguments { get; }

/// <summary>
/// The <see cref="IServiceProvider"/> specific to the scope of this Hub method invocation.
/// </summary>
public IServiceProvider ServiceProvider { get; }

/// <summary>
/// The <see cref="MethodInfo"/> for the Hub method being invoked.
/// </summary>
public MethodInfo HubMethod { get; }
}
}
43 changes: 43 additions & 0 deletions src/SignalR/server/Core/src/HubLifetimeContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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.

#nullable enable

using System;

namespace Microsoft.AspNetCore.SignalR
{
/// <summary>
/// Context for the hub lifetime events <see cref="Hub.OnConnectedAsync"/> and <see cref="Hub.OnDisconnectedAsync(Exception)"/>.
/// </summary>
public sealed class HubLifetimeContext
{
/// <summary>
/// Instantiates a new instance of the <see cref="HubLifetimeContext"/> class.
/// </summary>
/// <param name="context">Context for the active Hub connection and caller.</param>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> specific to the scope of this Hub method invocation.</param>
/// <param name="hub">The instance of the Hub.</param>
public HubLifetimeContext(HubCallerContext context, IServiceProvider serviceProvider, Hub hub)
{
Hub = hub;
ServiceProvider = serviceProvider;
Context = context;
}

/// <summary>
/// Gets the context for the active Hub connection and caller.
/// </summary>
public HubCallerContext Context { get; }

/// <summary>
/// Gets the Hub instance.
/// </summary>
public Hub Hub { get; }

/// <summary>
/// The <see cref="IServiceProvider"/> specific to the scope of this Hub method invocation.
/// </summary>
public IServiceProvider ServiceProvider { get; }
}
}
2 changes: 2 additions & 0 deletions src/SignalR/server/Core/src/HubOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,7 @@ public class HubOptions
/// Gets or sets the max buffer size for client upload streams. The default size is 10.
/// </summary>
public int? StreamBufferCapacity { get; set; } = null;

internal List<IHubFilter> HubFilters { get; set; } = null;
}
}
60 changes: 60 additions & 0 deletions src/SignalR/server/Core/src/HubOptionsExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 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.

#nullable enable

using System;
using System.Collections.Generic;
using Microsoft.AspNetCore.SignalR.Internal;

namespace Microsoft.AspNetCore.SignalR
{
/// <summary>
/// Methods to add <see cref="IHubFilter"/>'s to Hubs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Methods to add <see cref="IHubFilter"/>'s to Hubs.
/// Methods to add <see cref="IHubFilter"/> instances to Hubs.

/// </summary>
public static class HubOptionsExtensions
{
/// <summary>
/// Adds an instance of an <see cref="IHubFilter"/> to the <see cref="HubOptions"/>.
/// </summary>
/// <param name="options">The options to add a filter to.</param>
/// <param name="hubFilter">The filter instance to add to the options.</param>
public static void AddFilter(this HubOptions options, IHubFilter hubFilter)
{
_ = options ?? throw new ArgumentNullException(nameof(options));
_ = hubFilter ?? throw new ArgumentNullException(nameof(hubFilter));

if (options.HubFilters == null)
{
options.HubFilters = new List<IHubFilter>();
}

options.HubFilters.Add(hubFilter);
}

/// <summary>
/// Adds an <see cref="IHubFilter"/> type to the <see cref="HubOptions"/> that will be resolved via DI or type activated.
/// </summary>
/// <typeparam name="TFilter">The <see cref="IHubFilter"/> type that will be added to the options.</typeparam>
/// <param name="options">The options to add a filter to.</param>
public static void AddFilter<TFilter>(this HubOptions options) where TFilter : IHubFilter
{
_ = options ?? throw new ArgumentNullException(nameof(options));

options.AddFilter(typeof(TFilter));
}

/// <summary>
/// Adds an <see cref="IHubFilter"/> type to the <see cref="HubOptions"/> that will be resolved via DI or type activated.
/// </summary>
/// <param name="options">The options to add a filter to.</param>
/// <param name="filterType">The <see cref="IHubFilter"/> type that will be added to the options.</param>
public static void AddFilter(this HubOptions options, Type filterType)
{
_ = options ?? throw new ArgumentNullException(nameof(options));
_ = filterType ?? throw new ArgumentNullException(nameof(filterType));

options.AddFilter(new HubFilterFactory(filterType));
}
}
}
11 changes: 6 additions & 5 deletions src/SignalR/server/Core/src/HubOptionsSetup`T.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,7 @@ public HubOptionsSetup(IOptions<HubOptions> options)
public void Configure(HubOptions<THub> options)
{
// Do a deep copy, otherwise users modifying the HubOptions<THub> list would be changing the global options list
options.SupportedProtocols = new List<string>(_hubOptions.SupportedProtocols.Count);
foreach (var protocol in _hubOptions.SupportedProtocols)
{
options.SupportedProtocols.Add(protocol);
}
options.SupportedProtocols = new List<string>(_hubOptions.SupportedProtocols);
options.KeepAliveInterval = _hubOptions.KeepAliveInterval;
options.HandshakeTimeout = _hubOptions.HandshakeTimeout;
options.ClientTimeoutInterval = _hubOptions.ClientTimeoutInterval;
Expand All @@ -30,6 +26,11 @@ public void Configure(HubOptions<THub> options)
options.StreamBufferCapacity = _hubOptions.StreamBufferCapacity;

options.UserHasSetValues = true;

if (_hubOptions.HubFilters != null)
{
options.HubFilters = new List<IHubFilter>(_hubOptions.HubFilters);
}
}
}
}
Loading