dotnet / dotnet/aspnetcore

Filter Outgoing Invocations in SignalR

Open
#48,401 16 comments 14 reactions 0 assignees View on GitHub
area-signalr design-proposal
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 5h
Merged PRs (30d)
276

Description

## Summary

This is a proposal to add a possible `IClientFilter` to SignalR.
The goal here is to allow for engineers to filter messages being sent to clients connected to a SignalR hub, regardless of a prior hub invocation.

## Motivation and goals

My main motivation to design this is to be able to collect more granular metrics regarding the messages being sent to clients like:
* What transport is the client using?
* What is the volume of each particular event being sent?

The implementation of given filter would allow for other scenarios like:
* Tracking messages based on client/user;
* Blocking a message from being sent;
* Other verifications/validations done regarding a particular connection.

## In scope

* Middleware like filter to allow for code execution before/after a message is sent to a particular client;
* Allow to block a message from being sent to a particular client;
* Allow custom logic to run before/after each message is sent out;

## Out of scope

* The filter would apply only to `HubMethodInvocationMessage`s. Other message types, including messages used to control connection/flow would not run the filter;
* Modification of a message being sent is also out of scope.

## Risks / unknowns

The main risk here is the increased latency between a message being dispatched and it being actually sent throught a transport.
Adding the possibility to run custom code before/after sending a message could increase CPU/Memory usage and slow down communication with clients.
The `IHubFilter` is instantiated (or reused) for every hub invocation, but a single hub invocation could trigger "countless" `IClientFilter` executions when using `All` or `Except`.
Regardless, the engineer should have the possibility to weight pros and cons of adding such a code to his application. Perhaps the performance cost is worth the results.

## Examples

`IClientFilter` could have a single method:
```csharp
public interface IClientFilter {
ValueTask OnSendAsync(ClientInvocationContext context, Func next);
}
```
And be added to `HubOptions` the same way a `IHubFilter` is:
```csharp
services.AddSingalR(options => {
options.AddClientFilter();
options.AddClientFilter(typeof(MyFilter));
options.AddClientFilter(new MyFilter());
});
```

NOTE: We could not simply use `AddFilter` because it would collide with the current `AddFilter(Type)` used for `IHubFilter`s.

From here, each time a message is to be sent to a client, the method `OnSendAsync` would be invoked as a middleware.
If an exception occur (much like happens with `IHubFilter.InvokeMethodAsync`, the message would not be sent.
Messages sent from outside the hub instance, using `IHubContext` should behave the same way.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.