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:
Rossen Stoyanchev
2014-05-08 19:18:53 -04:00
parent 3c7bb9c279
commit 97fb308b6b
13 changed files with 229 additions and 70 deletions

View File

@@ -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

View File

@@ -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