Description
Affects: v5.3.3
We used WebSockets within Spring Boot 2.3.5 (Spring Framework 5.2.10). After updating to Spring Boot 2.4.2 (Spring Framework 5.3.3), we noticed that our WebSocket mechanism was no longer working.
We looked at #26118, but the discussion there was about duplicate subscriptions and wrongly registered /user destinations. In our case we have only a single subscription per sessionId/subscriptionId.
WebSocket is basically configured like this:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(final MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.setUserDestinationPrefix("/user");
}
@Override
public void registerStompEndpoints(final StompEndpointRegistry registry) {
registry.addEndpoint("/websocket").withSockJS();
}
}
In our application we use user-specific WebSocket messages as a push mechanism. The client subscribes to /user/events, the server sends messages to /user/{username}/events using SimpMessagingTemplate#convertAndSendToUser. This worked as expected up to Version 5.3.3.
_simpMessagingTemplate.convertAndSendToUser("username", "/events", pojo);
After a little bit of debugging, we've noticed that there might now be a problem with user-specific subscriptions due to a change with #24395. DestinationCache#calculateSubscriptions now operates on an exact destination equals-comparison when using non-pattern-based subscriptions.
@NonNull
private LinkedMultiValueMap<String, String> calculateSubscriptions(String destination) {
LinkedMultiValueMap<String, String> sessionsToSubscriptions = new LinkedMultiValueMap<>();
DefaultSubscriptionRegistry.this.subscriptionRegistry.forEachSubscription((sessionId, subscriptionDetail) -> {
if (subscriptionDetail.isAntPattern()) {
...
}
else if (destination.equals(subscriptionDetail.getDestination())) {
...
While debugging the issue you can see, that the active subscription to subscriptionDetail.getDestination()="/user/events"
is compared to a given destination="/user/{username}/events"
, which has been extracted from the new user-specific Message in AbstractSubscriptionRegistry#findSubscriptions called by SimpleBrokerMessageHandler#sendMessageToSubscribers.
To me it looks like the {username} part of the message destination is not properly handled here.