Description
We used WebSockets within Spring Boot 2.3.5 using a simple message broker. After the update to Spring Boot 2.4 / Spring Framework 5.3, we noticed that our web socket mechanism is not working anymore. We use the following web socket config:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic", "/user");
config.setApplicationDestinationPrefixes("/app");
config.setUserDestinationPrefix("/user");
}
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/websocket");
registry.addEndpoint("/websocket").setAllowedOrigins("http://localhost:8080").withSockJS();
}
@Bean(name = "websocketLocaleResolver")
public SessionLocaleResolver websocketLocaleResolver() {
SessionLocaleResolver websocketLocaleResolver = new SessionLocaleResolver();
websocketLocaleResolver.setDefaultLocale(Locale.GERMANY);
return websocketLocaleResolver;
}
}
A look at the DefaultSubscriptionRegistry
shows that there changed a lot comparing to Spring Boot 2.3.5. We could nail down the issue because we saw that even though our web socket subscription was successful within the DefaultSubscriptionRegistry
, the method this.subscriptionRegistry.findSubscriptions(message)
within the SimpleBrokerMessageHandler
always returns an empty map (in contrast to Spring Boot 2.3).
protected void sendMessageToSubscribers(@Nullable String destination, Message<?> message) {
MultiValueMap<String,String> subscriptions = this.subscriptionRegistry.findSubscriptions(message);
...
When rolling back to Spring 2.3.5, the same web socket business logic works fine with the old implementations of DefaultSubscriptionRegistry
and SimpleBrokerMessageHandler
.
We also noticed that with Spring Boot 2.3.5 the DestinationCache
returns a map including a SessionId
and SubscriptionId
(expected behavior). Using Spring Boot 2.4.0, the returned LinkedMultiValueMap sessionIdToSubscriptionIds is always empty.
public LinkedMultiValueMap<String, String> getSubscriptions(String destination) {
LinkedMultiValueMap<String, String> sessionIdToSubscriptionIds = this.destinationCache.get(destination);
if (sessionIdToSubscriptionIds == null) {
sessionIdToSubscriptionIds = this.destinationCache.computeIfAbsent(destination, _destination -> {
LinkedMultiValueMap<String, String> matches = computeMatchingSubscriptions(destination);
// Update queue first, so that cacheSize <= queue.size(
this.cacheEvictionPolicy.add(destination);
this.cacheSize.incrementAndGet();
return matches;
});
ensureCacheLimit();
}
return sessionIdToSubscriptionIds;
}