-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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:
public interface IClientFilter {
ValueTask OnSendAsync(ClientInvocationContext context, Func<ClientInvocationContext, ValueTask> next);
}
And be added to HubOptions
the same way a IHubFilter
is:
services.AddSingalR(options => {
options.AddClientFilter<MyFilter>();
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.