Server Send Events - SSL + Trigger

Hi @mythz ,

2 Questions on SSE.

  1. When running in development everything runs on https. So in general api calls I will use something like code below when connecting from a simulator to connect with my localhost as simulator normally reference specific IP to map to localhost. To prevent SSL error I will use code below.

ServerCertificateCustomValidationCallback = (req, cert, chain, errors) =>
return true;

On Server Events is there something that I can use on the “client side” to ignore the SSL errors
similar to code above.

I have tried on server side the code below but I still see the SSL Error

LimitToAuthenticatedUsers = false

SSL Error:

Error : System.Net.WebException: Error: TrustFailure (Authentication failed, see inner exception.) —> System.Security.Authentication.AuthenticationException: Authentication failed, see inner exception. —> Mono.Btls.MonoBtlsException: Ssl error:1000007d:SSL routines:OPENSSL_internal:CERTIFICATE_VERIFY_FAILED
at /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/boringssl/ssl/handshake_client.c:1132
at Mono.Btls.MonoBtlsContext.ProcessHandshake () [0x00042] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/mcs/class/System/Mono.Btls/MonoBtlsContext.cs:220

  1. I use the trigger functionality in SSE. On a mobile device I will subscribe to triggers with

sse_client?.AddListener(listenerName, handler);

So I have different tabs in my mobile app that subscribes to between 50-100 triggers at a time when the user selects different tabs to display data in real-time.

I assume each listener call above will trigger a http request to server. So 50-100 trigger per tab will result in many calls.

Is there perhaps a more efficient way to send the triggers to server in bulk so that I only end up with one htttp call to server ?

Thanks again.

The ServerEventsClient.cs only uses .NET’s HttpWebRequest & JsonServiceClient which also uses HttpWebRequest so the global “ignore all SSL errors” should work on all HTTP connections ServerEventsClient makes:

ServicePointManager.ServerCertificateValidationCallback += 
    (sender, cert, chain, sslPolicyErrors) => true;

Otherwise the AllRequestFilters allows you to provide a global Request Filter for all client requests:

var sse = new ServerEventsClient(baseUrl) {
    AllRequestFilters = req => 
        ((HttpWebRequest)req).ServerCertificateValidationCallback = 
            (sender, cert, chain, sslPolicyErrors) => true

I don’t understand what you mean by this, adding a listener is just a client concern for mapping which messages are handled by which handler, adding a listener doesn’t result in any additional server calls nor does it result in any additional messages being sent to the client which only receive messages sent to channels it subscribes to (or messages sent to all channels).

Thanks @mythz.

So you are saying triggers are only client side , therefor it means that if the server send information to say like:

this.ServerEvents.NotifyChannelAsync(“Main”, “trigger.stockLevelsID_100” , message)

The call above will be send to all users / clients on channel “Main” but only the clients that are subscribed to trigger is where the callback will happen ?

Is there a way to send the message to only subscribed clients ? I know you can use channels but in my case that will result in 100’s of channels,

Or let me rephrase what is the most efficient way to notify only those clients that are subscribed to a call without sending to clients that are not listening assuming that there can be thousands of “subscribed topics” collectively on the server side. ?

If client channels was as easy as code below then it would be great but to redefine new ServerEventsClient(connectionstring, channelname) with all events for every “subscribed topic” seems very complicated.

SSE.AddChannel (channelname, handler);

or is the recommended way to simply overwrite the existing ServerEventsClient with all the subscribed topics as “channels” each time the client topics change in which case I first have to stop the existing ServerEventsClient and then restart with updated channels. My concern here is that the stop / restarting process may result in some lost events that got fired from the server as there is then no continues connection on the client side.

Right all clients subscribed to the “Main” channel receive messages sent to the “Main” channel and invoke listeners that messages are targeted for.

No that’s the entire concept of channels, for clients to “subscribe to” to and receive events sent on that channel. If you don’t want to send messages to a channel, you can send them directly to a User or a Users unique subscription.

The different APIs you can use to send a notification is identified by the Notify* APIs in IServerEvents. E.g. in addition to a channel you can send messages to a single user by User Id, UserName or SessionId or a single subscription (i.e. Users can have multiple subscriptions).

You can only “subscribe” to channels which is an artificial group of your own choosing, either at the start of the subscription or later via the Un/SubscribeToChannels APIs which you can have as many of to make it has arbitrarily fine-grained or as coarse as you want it. E.g. If you want every handler to have its own its own channel, that’s up to you.

Use the Un/SubscribeToChannels APIs to change which channels a user is subscribed during an active subscription, you wont need to stop/restart your subscription.

Thanks @mythz. for the clarity on triggers. Channels will work for me. As always , awesome support :+1:

1 Like

Hi @mythz ,

Another question on this topic. When my client has say a 100 channel subscriptions on SSE. Is there a more effiecient way to unsubscribe from all channels without keeping track of all the channel names ?

I’m looking for something like:

Task ClearAllChannelsExceptAsync(string subscriptionId, string[] except_channels, CancellationToken ct);

Not supplying any except channels (null ) will clear all channels , adding “except channels” with clear all channels except mentioned channels.

You don’t need to maintain the collection yourself as you can query the subscription to find the active channels, so you can remove all channels except the ones you want with something like:

var sub = ServerEvents.GetSubscriptionInfo(subId);
var removeChannels = sub.Channels.Where(x => !exceptChannels.Contains(x));
await ServerEvents.UnsubscribeFromChannelsAsync(subId, removeChannels);

thanks @mythz , as always. :+1: :+1: You reference a subId in your code. Is this the same as subscriptionid

I see in docs you say

Whilst these all provide different ways to send a message to a single authenticated user, any user can be connected to multiple subscriptions at any one time (e.g. by having multiple tabs open). Each one of these subscriptions is uniquely identified by a subscriptionId which you can send a message with using.

I have one central datastore where I retrieve my data from so I assume the above info (multiple subscriptionId’s ) should be of no concern even if used in something like Blazor going forward.

Sorry for the ignorance here , how does subId relate to userId and ServerEvents.GetId , where do I get the subId from ?

1 Like

Yes subId is short for subscriptionId, every SSE Connection has a unique id that identifies the subscription (a single user can have many), this info is available to clients in the sub info sent in the connect event.

Awesome , so for completeness.

I add

sub.ConnectArgs[“SubId”] = sub.SubscriptionId;

on the server side and then I can pick up the subId on client and make a call to exclude channels for the specific subId.

or the onconnect event on the client include an “id” field that is the same as the subscriptionId.

Thanks alot.

Note: It’s already populated as “id” in ConnectArgs, that’s available to the client when handling the onConnect event.

1 Like