Filter Outgoing Invocations in SignalR
- 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
Assessment
This issue has not been assessed yet.