The core WebSocket API provides foundational interfaces and classes for handling WebSocket connections, messages, and sessions. This is the raw WebSocket programming model that all other Spring WebSocket features build upon.
Main interface for handling WebSocket lifecycle events. Implement this interface to create custom WebSocket handlers, or extend one of the convenience base classes.
/**
* Central interface for handling WebSocket messages and lifecycle events.
* Implementations handle connection establishment, message processing, errors,
* and connection closure.
*/
public interface WebSocketHandler {
/**
* Invoked after WebSocket negotiation has succeeded and the WebSocket
* connection is opened and ready for use.
*
* @param session the WebSocket session
* @throws Exception if an error occurs
*/
void afterConnectionEstablished(WebSocketSession session) throws Exception;
/**
* Invoked when a new WebSocket message arrives.
*
* @param session the WebSocket session
* @param message the message received (TextMessage, BinaryMessage,
* PingMessage, or PongMessage)
* @throws Exception if an error occurs
*/
void handleMessage(WebSocketSession session, WebSocketMessage<?> message)
throws Exception;
/**
* Handle a transport error.
*
* @param session the WebSocket session
* @param exception the exception that occurred
* @throws Exception if an error occurs
*/
void handleTransportError(WebSocketSession session, Throwable exception)
throws Exception;
/**
* Invoked after the WebSocket connection has been closed by either side,
* or after a transport error has occurred.
*
* @param session the WebSocket session
* @param closeStatus the reason for closure
* @throws Exception if an error occurs
*/
void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus)
throws Exception;
/**
* Whether the handler can handle partial messages. If false and an incoming
* message is fragmented, the message will be buffered until it's complete.
* If true, each message fragment will be delivered as it arrives.
*
* @return true if partial messages are supported, false otherwise
*/
boolean supportsPartialMessages();
}Represents a WebSocket session providing access to session properties, attributes, and message sending capabilities.
/**
* Represents a WebSocket session. Provides access to session metadata,
* attributes, and operations for sending messages and closing the connection.
*/
public interface WebSocketSession extends Closeable {
/**
* Return a unique session identifier.
*/
String getId();
/**
* Return the URI used to open the WebSocket connection, or null if not available.
*/
URI getUri();
/**
* Return the headers used in the handshake request.
*/
HttpHeaders getHandshakeHeaders();
/**
* Return the map with attributes associated with the WebSocket session.
* Can be used to store session-specific data.
*/
Map<String, Object> getAttributes();
/**
* Return the authenticated user for the session, or null if not authenticated.
*/
Principal getPrincipal();
/**
* Return the address on which the request was received, or null if not available.
*/
InetSocketAddress getLocalAddress();
/**
* Return the address of the remote client, or null if not available.
*/
InetSocketAddress getRemoteAddress();
/**
* Return the negotiated sub-protocol, or null if none was specified or
* negotiated successfully.
*/
String getAcceptedProtocol();
/**
* Configure the maximum size for an incoming text message.
*/
void setTextMessageSizeLimit(int messageSizeLimit);
/**
* Get the configured maximum size for an incoming text message.
*/
int getTextMessageSizeLimit();
/**
* Configure the maximum size for an incoming binary message.
*/
void setBinaryMessageSizeLimit(int messageSizeLimit);
/**
* Get the configured maximum size for an incoming binary message.
*/
int getBinaryMessageSizeLimit();
/**
* Return the negotiated extensions, or an empty list if none.
*/
List<WebSocketExtension> getExtensions();
/**
* Send a WebSocket message: either TextMessage, BinaryMessage,
* PingMessage, or PongMessage.
*
* @param message the message to send
* @throws IOException if sending fails
*/
void sendMessage(WebSocketMessage<?> message) throws IOException;
/**
* Return whether the connection is still open.
*/
boolean isOpen();
/**
* Close the WebSocket connection with status 1000 (normal closure).
*
* @throws IOException if closing fails
*/
@Override
void close() throws IOException;
/**
* Close the WebSocket connection with the given status.
*
* @param status the close status
* @throws IOException if closing fails
*/
void close(CloseStatus status) throws IOException;
}Marker interface for handlers that support WebSocket sub-protocols.
/**
* Implemented by WebSocketHandler implementations that support sub-protocols.
* A sub-protocol provides higher-level application semantics over the base
* WebSocket protocol (e.g., STOMP).
*/
public interface SubProtocolCapable {
/**
* Return the list of sub-protocols supported by this handler.
* The first protocol in the list that matches a client-requested protocol
* will be selected.
*/
List<String> getSubProtocols();
}Base interface for WebSocket messages.
/**
* Represents a WebSocket message containing a payload of a specific type.
*
* @param <T> the payload type (String for text, ByteBuffer for binary)
*/
public interface WebSocketMessage<T> {
/**
* Return the message payload (never null).
*/
T getPayload();
/**
* Return the number of bytes contained in the payload.
*/
int getPayloadLength();
/**
* When partial message support is available, this method indicates whether
* this is the last part of the message.
*/
boolean isLast();
}Abstract base class for WebSocket messages providing common functionality for all message types.
/**
* Abstract base class for WebSocket messages. Provides common implementation
* for payload storage and partial message support.
*
* @param <T> the payload type (String for text, ByteBuffer for binary)
*/
public abstract class AbstractWebSocketMessage<T> implements WebSocketMessage<T> {
/**
* Return the message payload (never null).
*/
@Override
public T getPayload();
/**
* Whether this is the last part of a message sent as a series of partial messages.
*/
@Override
public boolean isLast();
}WebSocket text message.
/**
* A text WebSocket message.
*/
public class TextMessage extends AbstractWebSocketMessage<String> {
/**
* Create a new text message with the given CharSequence payload.
*
* @param payload the message payload (never null)
*/
public TextMessage(CharSequence payload);
/**
* Create a new text message from a byte array with UTF-8 encoding.
*
* @param payload the message payload (never null)
*/
public TextMessage(byte[] payload);
/**
* Create a new text message with the given payload representing the full
* or partial message content. When the isLast flag is false, the message
* is sent as a partial message. Subsequent partial messages should use the
* same flag until the last part is sent.
*
* @param payload the message payload (never null)
* @param isLast whether this is the last part of a message
*/
public TextMessage(CharSequence payload, boolean isLast);
/**
* Return the message payload as a byte array using UTF-8 encoding.
*/
public byte[] asBytes();
@Override
public int getPayloadLength();
}WebSocket binary message.
/**
* A binary WebSocket message.
*/
public class BinaryMessage extends AbstractWebSocketMessage<ByteBuffer> {
/**
* Create a new binary message with the given ByteBuffer payload.
*
* @param payload the message payload (never null)
*/
public BinaryMessage(ByteBuffer payload);
/**
* Create a new binary message with the given payload representing the full
* or partial message content.
*
* @param payload the message payload (never null)
* @param isLast whether this is the last part of a message
*/
public BinaryMessage(ByteBuffer payload, boolean isLast);
/**
* Create a new binary message from a byte array.
*
* @param payload the message payload (never null)
*/
public BinaryMessage(byte[] payload);
/**
* Create a new binary message with the given payload representing the full
* or partial message content.
*
* @param payload the message payload (never null)
* @param isLast whether this is the last part of a message
*/
public BinaryMessage(byte[] payload, boolean isLast);
/**
* Create a new binary message from a portion of a byte array.
*
* @param payload the message payload (never null)
* @param offset the offset into the payload array
* @param length the number of bytes to use
* @param isLast whether this is the last part of a message
*/
public BinaryMessage(byte[] payload, int offset, int length, boolean isLast);
@Override
public int getPayloadLength();
}WebSocket ping message. Can be used to check if the connection is alive.
/**
* A WebSocket ping message. When received, the receiver should respond
* with a pong message.
*/
public class PingMessage extends AbstractWebSocketMessage<ByteBuffer> {
/**
* Create a new ping message with an empty payload.
*/
public PingMessage();
/**
* Create a new ping message with the given ByteBuffer payload.
*
* @param payload the message payload (never null)
*/
public PingMessage(ByteBuffer payload);
}WebSocket pong message. Sent in response to a ping message.
/**
* A WebSocket pong message. Typically sent in response to a ping message.
*/
public class PongMessage extends AbstractWebSocketMessage<ByteBuffer> {
/**
* Create a new pong message with an empty payload.
*/
public PongMessage();
/**
* Create a new pong message with the given ByteBuffer payload.
*
* @param payload the message payload (never null)
*/
public PongMessage(ByteBuffer payload);
}Represents a WebSocket close status code and reason as defined in RFC 6455.
/**
* Represents a WebSocket close status consisting of a status code and
* a reason phrase. Standard status codes are provided as constants.
*/
public final class CloseStatus {
// Standard close status codes (RFC 6455)
public static final CloseStatus NORMAL; // 1000
public static final CloseStatus GOING_AWAY; // 1001
public static final CloseStatus PROTOCOL_ERROR; // 1002
public static final CloseStatus NOT_ACCEPTABLE; // 1003
public static final CloseStatus NO_STATUS_CODE; // 1005
public static final CloseStatus NO_CLOSE_FRAME; // 1006
public static final CloseStatus BAD_DATA; // 1007
public static final CloseStatus POLICY_VIOLATION; // 1008
public static final CloseStatus TOO_BIG_TO_PROCESS; // 1009
public static final CloseStatus REQUIRED_EXTENSION; // 1010
public static final CloseStatus SERVER_ERROR; // 1011
public static final CloseStatus SERVICE_RESTARTED; // 1012
public static final CloseStatus SERVICE_OVERLOAD; // 1013
public static final CloseStatus TLS_HANDSHAKE_FAILURE; // 1015
public static final CloseStatus SESSION_NOT_RELIABLE; // 4500
/**
* Create a new CloseStatus with the given code.
*
* @param code the status code
*/
public CloseStatus(int code);
/**
* Create a new CloseStatus with the given code and reason.
*
* @param code the status code
* @param reason the reason phrase (may be null)
*/
public CloseStatus(int code, String reason);
/**
* Return the status code.
*/
public int getCode();
/**
* Return the reason phrase, or null if none.
*/
public String getReason();
/**
* Create a new CloseStatus from this one with the given reason.
*
* @param reason the reason phrase
* @return a new CloseStatus instance
*/
public CloseStatus withReason(String reason);
/**
* Return whether this status has the same code as the given status.
*
* @param other the other status
* @return true if the codes match
*/
public boolean equalsCode(CloseStatus other);
}Represents a WebSocket protocol extension.
/**
* Represents a WebSocket extension as defined in RFC 6455.
* WebSocket extensions provide additional functionality such as
* compression (permessage-deflate).
*/
public class WebSocketExtension {
/**
* Create a WebSocket extension with the given name.
*
* @param name the extension name
*/
public WebSocketExtension(String name);
/**
* Create a WebSocket extension with the given name and parameters.
*
* @param name the extension name
* @param parameters the extension parameters (may be empty)
*/
public WebSocketExtension(String name, Map<String, String> parameters);
/**
* Return the name of the extension (never null).
*/
public String getName();
/**
* Return the parameters of the extension (never null but may be empty).
*/
public Map<String, String> getParameters();
/**
* Parse the given comma-separated list of WebSocket extensions.
*
* @param extensions the extensions header value
* @return a list of WebSocketExtension instances
*/
public static List<WebSocketExtension> parseExtensions(String extensions);
}HTTP headers specifically for WebSocket handshake requests and responses.
/**
* HTTP headers for WebSocket handshake requests and responses.
* Provides convenient access to WebSocket-specific header fields.
*/
public class WebSocketHttpHeaders extends HttpHeaders {
// WebSocket header names
public static final String SEC_WEBSOCKET_ACCEPT = "Sec-WebSocket-Accept";
public static final String SEC_WEBSOCKET_EXTENSIONS = "Sec-WebSocket-Extensions";
public static final String SEC_WEBSOCKET_KEY = "Sec-WebSocket-Key";
public static final String SEC_WEBSOCKET_PROTOCOL = "Sec-WebSocket-Protocol";
public static final String SEC_WEBSOCKET_VERSION = "Sec-WebSocket-Version";
/**
* Construct a new, empty WebSocketHttpHeaders instance.
*/
public WebSocketHttpHeaders();
/**
* Construct a new WebSocketHttpHeaders instance backed by the supplied HttpHeaders.
*
* @param httpHeaders the headers to expose
*/
public WebSocketHttpHeaders(HttpHeaders httpHeaders);
/**
* Set the Sec-WebSocket-Accept header (server response).
*/
public void setSecWebSocketAccept(String secWebSocketAccept);
/**
* Return the Sec-WebSocket-Accept header value.
*/
public String getSecWebSocketAccept();
/**
* Return the Sec-WebSocket-Extensions header value as a list of extensions.
*/
public List<WebSocketExtension> getSecWebSocketExtensions();
/**
* Set the Sec-WebSocket-Extensions header.
*/
public void setSecWebSocketExtensions(List<WebSocketExtension> extensions);
/**
* Set the Sec-WebSocket-Key header (client request).
*/
public void setSecWebSocketKey(String secWebSocketKey);
/**
* Return the Sec-WebSocket-Key header value.
*/
public String getSecWebSocketKey();
/**
* Set the Sec-WebSocket-Protocol header with a single sub-protocol value.
*/
public void setSecWebSocketProtocol(String secWebSocketProtocol);
/**
* Set the Sec-WebSocket-Protocol header with sub-protocol values.
*/
public void setSecWebSocketProtocol(List<String> protocols);
/**
* Return the Sec-WebSocket-Protocol header value as a list of sub-protocols.
*/
public List<String> getSecWebSocketProtocol();
/**
* Set the Sec-WebSocket-Version header (client request).
*/
public void setSecWebSocketVersion(String secWebSocketVersion);
/**
* Return the Sec-WebSocket-Version header value.
*/
public String getSecWebSocketVersion();
}import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
public class EchoHandler extends TextWebSocketHandler {
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message)
throws Exception {
// Echo the received message back to the client
session.sendMessage(new TextMessage("Echo: " + message.getPayload()));
}
}import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.BinaryWebSocketHandler;
import java.nio.ByteBuffer;
public class BinaryDataHandler extends BinaryWebSocketHandler {
@Override
protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message)
throws Exception {
ByteBuffer payload = message.getPayload();
// Process binary data
byte[] data = new byte[payload.remaining()];
payload.get(data);
// Send response
session.sendMessage(new BinaryMessage(ByteBuffer.wrap(data)));
}
}import org.springframework.web.socket.*;
public class CustomHandler implements WebSocketHandler {
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
System.out.println("Connection established: " + session.getId());
session.getAttributes().put("startTime", System.currentTimeMillis());
}
@Override
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message)
throws Exception {
if (message instanceof TextMessage) {
handleTextMessage(session, (TextMessage) message);
} else if (message instanceof BinaryMessage) {
handleBinaryMessage(session, (BinaryMessage) message);
} else if (message instanceof PongMessage) {
System.out.println("Pong received from: " + session.getId());
}
}
private void handleTextMessage(WebSocketSession session, TextMessage message)
throws Exception {
String payload = message.getPayload();
if ("ping".equals(payload)) {
// Send ping message to check connection
session.sendMessage(new PingMessage());
} else {
// Handle other text messages
session.sendMessage(new TextMessage("Received: " + payload));
}
}
private void handleBinaryMessage(WebSocketSession session, BinaryMessage message)
throws Exception {
// Handle binary messages
session.sendMessage(new BinaryMessage(message.getPayload()));
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception)
throws Exception {
System.err.println("Transport error for session " + session.getId() + ": " +
exception.getMessage());
}
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus)
throws Exception {
Long startTime = (Long) session.getAttributes().get("startTime");
long duration = System.currentTimeMillis() - startTime;
System.out.println("Connection closed: " + session.getId() +
" | Status: " + closeStatus +
" | Duration: " + duration + "ms");
}
@Override
public boolean supportsPartialMessages() {
return false;
}
}import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.handler.TextWebSocketHandler;
public class SessionAwareHandler extends TextWebSocketHandler {
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// Store user-specific data in session attributes
String userId = extractUserId(session);
session.getAttributes().put("userId", userId);
session.getAttributes().put("messageCount", 0);
}
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message)
throws Exception {
// Access session attributes
String userId = (String) session.getAttributes().get("userId");
int messageCount = (int) session.getAttributes().get("messageCount");
messageCount++;
session.getAttributes().put("messageCount", messageCount);
session.sendMessage(new TextMessage(
"User " + userId + " message #" + messageCount + ": " +
message.getPayload()
));
}
private String extractUserId(WebSocketSession session) {
// Extract from principal, headers, or query parameters
if (session.getPrincipal() != null) {
return session.getPrincipal().getName();
}
return "anonymous";
}
}import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
public class ConditionalCloseHandler extends TextWebSocketHandler {
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message)
throws Exception {
String payload = message.getPayload();
if ("shutdown".equals(payload)) {
// Close with custom status and reason
session.close(CloseStatus.NORMAL.withReason("Client requested shutdown"));
} else if (payload.length() > 1000) {
// Close due to policy violation
session.close(CloseStatus.POLICY_VIOLATION.withReason(
"Message too large"));
} else {
session.sendMessage(new TextMessage("Received: " + payload));
}
}
@Override
public void handleTransportError(WebSocketSession session, Throwable exception)
throws Exception {
// Close with server error status
if (session.isOpen()) {
session.close(CloseStatus.SERVER_ERROR.withReason(
exception.getMessage()));
}
}
}