Luis
Luis

Reputation: 51

How to limit the number of stomp clients in Spring, subscribing to a specific topic, based on a condition?

I have been researching for a way to limit the number of clients who can subscribe to a specific stomp topic but have not yet understood, which could be the right approach according to my needs.

My use case is a game, which I am developing in Angular (ng2-stompjs stomp client) and Spring Boot Websockets (for the moment, the Spring in-memory message broker is in use).

The idea is that a user can be connected and subscribed to a "/lobby" stomp topic, and there he sees the opened game rooms, that could be in different statuses. for example, in-play or not started yet due to the low number of players joined. I'd like to intercept and programmatically restrict a possible subscription of a client, to a specific "/room/{roomId}" topic, IF the MAX number of players has been reached, for example, 4. There could also be some simple client-side validation to restrict that, but I believe only client-side is not sufficient

So my main questions are: How can a specific stomp topic subscription be intercepted in Spring? Is it possible to return to the client-requestor some kind of error message that subscription could not be done?

I'd really appreciate your help, thank you in advance!

Upvotes: 1

Views: 1918

Answers (1)

srinivas kumar
srinivas kumar

Reputation: 423

You could implement a StompEventListener which listens for subscriptions, in this we can have map mapping a destination(room number) versus the count of number of players in that particular room. if the count is already at max reject the subscription.

@Service
class StompEventListener() {
   private Map<String, int> roomIdVsPlayerCount = new HashMap<>();
   
   @EventListener
   public void handleSubscription(SessionSubscribe event) {
     StompHeaderAccessor accessor = StompHeaderAccessor.wrap(event.getMessage());
     String destination = accessor.getDestination();

     String roomId = destination.substring(...); //Parsed RoomID
     if(roomIdVsPlayerCount.get(roomId) == MAX_ALLOWED_PLAYERS) {
       //Throw exception which will terminate that client connection 
         or, send an error message like so:
       simpMessagingTemplate.convertAndSend(<some_error_message>);
       return;
     }
     //So it is not at maximum do further logic to actually subscribe 
       user and
     roomIdVsPlayerCount.get(roomId) += 1; 
   }

   @EventListener
   public void handleUnsubscription(SessionUnsubscribe event) {
    ...
   }
}

Useful References:

  1. SessionSubscribeEvent (For handling the subscriptions)
  2. ConvertAndSend. (For sending the error messages to client.)

EDIT

Please try sending the exception from a channel Interceptor since the above did not send the exception , so that it gets propagated to the client. The map we defined earlier can be defined as a bean in a separate class accessible(with @Autowired) to both event handler(for incrementing and decrementing) and TopicSubscriptionInterceptor(for validation).

@Component
class TopicSubscriptionInterceptor implements ChannelInterceptor {
    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel){
      StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
      String destination = accessor.getDestination();

      String roomId = destination.substring(...); //Parsed RoomID
      if(roomIdVsPlayerCount.get(roomId) == MAX_ALLOWED_PLAYERS) {
        //Throw exception which will terminate that client connection 
      }
     
     //Since it is not at limit continue 
    
    }
}

Useful reference for implementing a TopicSubscriptionInterceptor: TopicSubscriptionInterceptor

Upvotes: 3

Related Questions