Allow use of @SendToUser even w/o authenticated user
Before this change, subscribing to a user destination and use of
@SendToUser annotation required an authenticated user.
This change makes it possible to subscribe to a user destination from
WebSocket sessions without an authenticated user. In such cases the
destination is associated with one session only rather than with a
user (and all their sessions).
It is then also possible to send a message to a user destination
via "/user/{sessionId}/.." rather than "/user/{user}/...".
That means @SendToUser works relying on the session id of the input
message, effectively sending a reply to destination private to the
session.
A key use case for this is handling an exception with an
@MessageExceptionHandler method and sending a reply with @SendToUser.
Issue: SPR-11309
This commit is contained in:
@@ -246,6 +246,7 @@ public class StompSubProtocolHandlerTests {
|
||||
assertEquals(1, this.session.getSentMessages().size());
|
||||
WebSocketMessage<?> textMessage = this.session.getSentMessages().get(0);
|
||||
assertTrue(((String) textMessage.getPayload()).contains("destination:/user/queue/foo\n"));
|
||||
assertFalse(((String) textMessage.getPayload()).contains(SimpMessageHeaderAccessor.ORIGINAL_DESTINATION));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.messaging.handler.annotation.MessageExceptionHandler;
|
||||
import org.springframework.messaging.handler.annotation.MessageMapping;
|
||||
import org.springframework.messaging.simp.annotation.SendToUser;
|
||||
import org.springframework.messaging.simp.annotation.SubscribeMapping;
|
||||
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
|
||||
import org.springframework.messaging.simp.stomp.StompCommand;
|
||||
@@ -118,10 +119,10 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
||||
@Test
|
||||
public void sendMessageToBrokerAndReceiveReplyViaTopic() throws Exception {
|
||||
|
||||
TextMessage message1 = create(StompCommand.SUBSCRIBE).headers("id:subs1", "destination:/topic/foo").build();
|
||||
TextMessage message2 = create(StompCommand.SEND).headers("destination:/topic/foo").body("5").build();
|
||||
TextMessage m1 = create(StompCommand.SUBSCRIBE).headers("id:subs1", "destination:/topic/foo").build();
|
||||
TextMessage m2 = create(StompCommand.SEND).headers("destination:/topic/foo").body("5").build();
|
||||
|
||||
TestClientWebSocketHandler clientHandler = new TestClientWebSocketHandler(1, message1, message2);
|
||||
TestClientWebSocketHandler clientHandler = new TestClientWebSocketHandler(1, m1, m2);
|
||||
WebSocketSession session = doHandshake(clientHandler, "/ws").get();
|
||||
|
||||
try {
|
||||
@@ -140,8 +141,8 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
||||
@Test
|
||||
public void sendSubscribeToControllerAndReceiveReply() throws Exception {
|
||||
|
||||
TextMessage message = create(StompCommand.SUBSCRIBE).headers(
|
||||
"id:subs1", "destination:/app/number").build();
|
||||
String destHeader = "destination:/app/number";
|
||||
TextMessage message = create(StompCommand.SUBSCRIBE).headers("id:subs1", destHeader).build();
|
||||
|
||||
TestClientWebSocketHandler clientHandler = new TestClientWebSocketHandler(1, message);
|
||||
WebSocketSession session = doHandshake(clientHandler, "/ws").get();
|
||||
@@ -149,7 +150,7 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
||||
try {
|
||||
assertTrue(clientHandler.latch.await(2, TimeUnit.SECONDS));
|
||||
String payload = clientHandler.actual.get(0).getPayload();
|
||||
assertTrue("Expected STOMP destination=/app/number, got " + payload, payload.contains("destination:/app/number"));
|
||||
assertTrue("Expected STOMP destination=/app/number, got " + payload, payload.contains(destHeader));
|
||||
assertTrue("Expected STOMP Payload=42, got " + payload, payload.contains("42"));
|
||||
}
|
||||
finally {
|
||||
@@ -157,6 +158,29 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void handleExceptionAndSendToUser() throws Exception {
|
||||
|
||||
String destHeader = "destination:/user/queue/error";
|
||||
TextMessage m1 = create(StompCommand.SUBSCRIBE).headers("id:subs1", destHeader).build();
|
||||
TextMessage m2 = create(StompCommand.SEND).headers("destination:/app/exception").build();
|
||||
|
||||
TestClientWebSocketHandler clientHandler = new TestClientWebSocketHandler(1, m1, m2);
|
||||
WebSocketSession session = doHandshake(clientHandler, "/ws").get();
|
||||
|
||||
try {
|
||||
assertTrue(clientHandler.latch.await(2, TimeUnit.SECONDS));
|
||||
|
||||
String payload = clientHandler.actual.get(0).getPayload();
|
||||
assertTrue(payload.startsWith("MESSAGE\n"));
|
||||
assertTrue(payload.contains("destination:/user/queue/error\n"));
|
||||
assertTrue(payload.endsWith("\"Got error: Bad input\"\0"));
|
||||
}
|
||||
finally {
|
||||
session.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@IntegrationTestController
|
||||
static class SimpleController {
|
||||
@@ -174,10 +198,10 @@ public class StompWebSocketIntegrationTests extends AbstractWebSocketIntegration
|
||||
}
|
||||
|
||||
@MessageExceptionHandler
|
||||
public void handleException(IllegalArgumentException ex) {
|
||||
|
||||
@SendToUser("/queue/error")
|
||||
public String handleException(IllegalArgumentException ex) {
|
||||
return "Got error: " + ex.getMessage();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@IntegrationTestController
|
||||
|
||||
Reference in New Issue
Block a user