ralscha / sse-eventbus

EventBus library for sending events from a Spring appliction to the web browser with SSE
Apache License 2.0
79 stars 26 forks source link

Functionality to exclude some clients from receive events #15

Closed adrimarti closed 3 years ago

adrimarti commented 4 years ago

I'm currently working on a webapp that uses this library to notificate clients when there is some update over specific data. The problem is that sometimes is a specific client who updates the data and it would be nice to have any method to avoid this client to receive the notification of its own update task.

I have seen that there is a Set called excludeClientIds but it has no function when clientIds is not empty. It would be nice if the funcionality works anyway.

ralscha commented 4 years ago

The problem is that the library has no knowledge about which client sent a message. You have to tell the library which clients you want to exclude. It's unlike WebSocket connections where you send and receive messages over the same channel. With Server-Sent Events you can only send message from the server to the client. Updates happen over a different channel usually a normal HTTP request.

When you look at the chat demo you see how this is done. https://github.com/ralscha/sse-eventbus-demo-chat/blob/master/src/main/java/ch/rasc/eventbus/demo/chat/ChatController.java#L142-L166

When the client sends an update to the server he also sends the client id with it and the server can then exclude this client id

    return fetch(`${environment.SERVER_URL}/join`, {
      method: 'POST',
      headers: this.jsonHeaders,
      body: JSON.stringify({
        clientId: this.clientId,
        roomId
      })
    });
this.eventBus.handleEvent(SseEvent.builder().event(request.getRoomId())
                .data(Collections.singletonList(message))
                .addExcludeClientId(request.getClientId()).build());
adrimarti commented 4 years ago

First of all, thank you for your fast reply.

I already how sse protocol works in difference to ws and also how to add a specific clientId to the exlusion list. The problem it's it does not work now, i think due to this lines at the SsEventBus.java class (excludeClientIds its ignored when the eventBus instance has not empty clientMap). ¿Is this correct?

Regards.

ralscha commented 4 years ago

I see. You are right. exclude only has an effect when include is empty.

You have three ways how to address the clients. Either all, with include or with exclude. These examples assumes that client1 to client5 are subscribed:

All

        SseEvent.builder()
            .event("msg").data("payload")
            .build();

Client 1 to 5 receive the message

Include Send message to only the include clients

        SseEvent.builder()
            .event("msg").data("payload")
            .addClientIds("client1", "client2", "client3")
            .build();

If you want to exclude a client with this approach you don't add it to the includes. For example you don't want to address client3

        SseEvent.builder()
            .event("msg").data("payload")
            .addClientIds("client1", "client2")
            .build();

Exclude Send message to all clients except excludes.

        SseEvent.builder()
            .event("msg").data("payload")
            .addExcludeClientIds("client1", "client2", "client3")
            .build();

client4 and client5 receive the message

When you specify include and exclude at the same time, exclude is ignored

        SseEvent.builder()
            .event("msg").data("payload")
            .addClientIds("client1", "client2", "client3")
                .addExcludeClientId("client1")  
            .build();

Message sent to 1,2 and 3

I didn't implement this approach because I thought it would be much easier to simply write it like this. Exclude "client1"

        SseEvent.builder()
            .event("msg").data("payload")
            .addClientIds("client2", "client3")
            .build();

We can implement the include/exclude approach but we need to define what exactly the semantic of exclude is. In the current implementation addExcludeClientId says: "send message to all clients minus excludes"

Does the following example send the message to client2 and client3, or does it send it to 2,3,4 and 5, or does it send it to 1,2,3,4,5? The question is do we combine include and exclude? Or does exclude change the meaning when include is specified and excludes only clients that are in the includes?

        SseEvent.builder()
            .event("msg").data("payload")
            .addClientIds("client1", "client2", "client3")
                .addExcludeClientId("client1") 
            .build();

And what happens when you exclude a client that is not in the includes?

        SseEvent.builder()
            .event("msg").data("payload")
            .addClientIds("client1", "client2", "client3")
                .addExcludeClientId("client5") 
            .build();