Skip to content

Filter Outgoing Invocations in SignalR #48401

@SuricateCan

Description

@SuricateCan

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 HubMethodInvocationMessages. 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 IHubFilters.

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-signalrIncludes: SignalR clients and serversdesign-proposalThis issue represents a design proposal for a different issue, linked in the description

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions