Add session attributes to SimpMessageHeaderAccessor

This change exposes the WebSocketSession attributes through a message header.
The StompSubProtocolHandler adds this to incoming messages.
For now messaging handling  methods can access the map via @Header, e.g.:

@Header(StompHeaderAccessor.SESSION_ATTRIBUTES) Map<String, Object> attrs) {

Issue: SPR-11566
This commit is contained in:
Rossen Stoyanchev
2014-03-19 13:45:40 -04:00
parent 48b62e80d5
commit 1bab8a3916
9 changed files with 34 additions and 12 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -46,6 +46,8 @@ public class SimpMessageHeaderAccessor extends NativeMessageHeaderAccessor {
public static final String SESSION_ID_HEADER = "simpSessionId";
public static final String SESSION_ATTRIBUTES = "simpSessionAttributes";
public static final String SUBSCRIPTION_ID_HEADER = "simpSubscriptionId";
public static final String USER_HEADER = "simpUser";
@@ -127,6 +129,15 @@ public class SimpMessageHeaderAccessor extends NativeMessageHeaderAccessor {
setHeader(SESSION_ID_HEADER, sessionId);
}
@SuppressWarnings("unchecked")
public Map<String, Object> getSessionAttributes() {
return (Map<String, Object>) getHeader(SESSION_ATTRIBUTES);
}
public void setSessionAttributes(Map<String, Object> attributes) {
setHeader(SESSION_ATTRIBUTES, attributes);
}
public Principal getUser() {
return (Principal) getHeader(USER_HEADER);
}

View File

@@ -18,6 +18,7 @@ package org.springframework.web.socket.adapter;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -43,16 +44,19 @@ public abstract class AbstractWebSocketSession<T> implements NativeWebSocketSess
private T nativeSession;
private final Map<String, Object> attributes;
private final Map<String, Object> attributes = new ConcurrentHashMap<String, Object>();
/**
* Create a new instance and associate the given attributes with it.
*
* @param attributes attributes from the HTTP handshake to associate with the WebSocket session
* @param attributes attributes from the HTTP handshake to associate with the WebSocket
* session; the provided attributes are copied, the original map is not used.
*/
public AbstractWebSocketSession(Map<String, Object> attributes) {
this.attributes = attributes;
if (attributes != null) {
this.attributes.putAll(attributes);
}
}

View File

@@ -65,7 +65,8 @@ public class JettyWebSocketSession extends AbstractWebSocketSession<Session> {
/**
* Create a new {@link JettyWebSocketSession} instance associated with the given user.
*
* @param attributes attributes from the HTTP handshake to associate with the WebSocket session
* @param attributes attributes from the HTTP handshake to associate with the WebSocket
* session; the provided attributes are copied, the original map is not used.
* @param user the user associated with the session; if {@code null} we'll fallback on the user
* available via {@link org.eclipse.jetty.websocket.api.Session#getUpgradeRequest()}
*/

View File

@@ -63,7 +63,8 @@ public class StandardWebSocketSession extends AbstractWebSocketSession<Session>
* Class constructor.
*
* @param headers the headers of the handshake request
* @param attributes attributes from the HTTP handshake to associate with the WebSocket session
* @param attributes attributes from the HTTP handshake to associate with the WebSocket
* session; the provided attributes are copied, the original map is not used.
* @param localAddress the address on which the request was received
* @param remoteAddress the address of the remote client
*/

View File

@@ -133,6 +133,7 @@ public class StompSubProtocolHandler implements SubProtocolHandler {
}
headers.setSessionId(session.getId());
headers.setSessionAttributes(session.getAttributes());
headers.setUser(session.getPrincipal());
message = MessageBuilder.withPayload(message.getPayload()).setHeaders(headers).build();

View File

@@ -37,17 +37,19 @@ public interface HandshakeHandler {
/**
* Initiate the handshake.
*
* @param request the current request
* @param response the current response
* @param wsHandler the handler to process WebSocket messages; see
* {@link PerConnectionWebSocketHandler} for providing a handler with
* per-connection lifecycle.
* @param attributes handshake request specific attributes to be set on the WebSocket
* session via {@link HandshakeInterceptor} and thus made available to the
* {@link WebSocketHandler};
* @param attributes attributes from the HTTP handshake to associate with the WebSocket
* session; the provided attributes are copied, the original map is not used.
*
* @return whether the handshake negotiation was successful or not. In either case the
* response status, headers, and body will have been updated to reflect the
* result of the negotiation
*
* @throws HandshakeFailureException thrown when handshake processing failed to
* complete due to an internal, unrecoverable error, i.e. a server error as
* opposed to a failure to successfully negotiate the handshake.

View File

@@ -40,8 +40,8 @@ public interface HandshakeInterceptor {
* @param request the current request
* @param response the current response
* @param wsHandler the target WebSocket handler
* @param attributes attributes from the HTTP handshake to associate with the
* WebSocket session, i.e. via {@link WebSocketSession#getAttributes()}
* @param attributes attributes from the HTTP handshake to associate with the WebSocket
* session; the provided attributes are copied, the original map is not used.
* @return whether to proceed with the handshake {@code true} or abort {@code false}
*/
boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,

View File

@@ -110,7 +110,8 @@ public abstract class AbstractSockJsSession implements SockJsSession {
* @param id the session ID
* @param config SockJS service configuration options
* @param handler the recipient of SockJS messages
* @param attributes attributes from the HTTP handshake to associate with the WebSocket session
* @param attributes attributes from the HTTP handshake to associate with the WebSocket
* session; the provided attributes are copied, the original map is not used.
*/
public AbstractSockJsSession(String id, SockJsServiceConfig config, WebSocketHandler handler,
Map<String, Object> attributes) {

View File

@@ -170,6 +170,7 @@ public class StompSubProtocolHandlerTests {
StompHeaderAccessor headers = StompHeaderAccessor.wrap(actual);
assertEquals(StompCommand.CONNECT, headers.getCommand());
assertEquals("s1", headers.getSessionId());
assertNotNull(headers.getSessionAttributes());
assertEquals("joe", headers.getUser().getName());
assertEquals("guest", headers.getLogin());
assertEquals("PROTECTED", headers.getPasscode());