or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

adapter-support.mdclient-support.mdconfiguration.mdcore-websocket.mdhandlers.mdindex.mdserver-support.mdsockjs-client.mdsockjs-frames.mdsockjs-support.mdstomp-messaging.md
tile.json

sockjs-support.mddocs/

SockJS Support

SockJS protocol support providing WebSocket fallback transports for environments where WebSocket is unavailable or blocked. Includes HTTP streaming, polling, and Server-Sent Events transports for maximum compatibility.

Capabilities

SockJsService Interface

Main entry point for processing SockJS HTTP requests.

/**
 * Main SockJS service interface for processing HTTP requests according
 * to the SockJS protocol. Handles transport selection, session management,
 * and message routing.
 */
public interface SockJsService {
    /**
     * Process a SockJS HTTP request.
     *
     * @param request    the HTTP request
     * @param response   the HTTP response
     * @param sockJsPath the SockJS path (after the endpoint prefix)
     * @param wsHandler  the WebSocket handler for the session
     * @throws SockJsException if processing fails
     */
    void handleRequest(
            ServerHttpRequest request,
            ServerHttpResponse response,
            String sockJsPath,
            WebSocketHandler wsHandler) throws SockJsException;
}

TransportType Enum

Enumeration of SockJS transport types.

/**
 * Enumeration of SockJS transport types. Each transport provides a different
 * mechanism for bi-directional communication when WebSocket is unavailable.
 */
public enum TransportType {
    /**
     * WebSocket transport - native WebSocket connection.
     */
    WEBSOCKET,

    /**
     * XHR (XMLHttpRequest) polling - request-response polling.
     */
    XHR,

    /**
     * XHR send - one-way message sending via POST.
     */
    XHR_SEND,

    /**
     * XHR streaming - long-lived streaming connection.
     */
    XHR_STREAMING,

    /**
     * Server-Sent Events (EventSource) - server push via SSE.
     */
    EVENT_SOURCE,

    /**
     * HTML file - streaming via forever iframe.
     */
    HTML_FILE;

    /**
     * Return the transport type identifier used in URLs.
     *
     * @return the transport type value
     */
    public String value();

    /**
     * Return the HTTP method used by this transport.
     *
     * @return the HTTP method (GET or POST)
     */
    public HttpMethod getHttpMethod();

    /**
     * Whether this transport sends no-cache headers.
     *
     * @return true if no-cache headers are sent
     */
    public boolean sendsNoCacheInstruction();

    /**
     * Whether this transport requires session cookies.
     *
     * @return true if session cookies are needed
     */
    public boolean sendsSessionCookie();

    /**
     * Whether this transport supports CORS.
     *
     * @return true if CORS is supported
     */
    public boolean supportsCors();

    /**
     * Whether this transport supports the Origin header.
     *
     * @return true if Origin header is supported
     */
    public boolean supportsOrigin();

    /**
     * Get TransportType from its string value.
     *
     * @param value the transport type value
     * @return the TransportType, or null if not found
     */
    public static TransportType fromValue(String value);
}

DefaultSockJsService

Default implementation of SockJsService with pluggable transports.

/**
 * Default SockJS service implementation supporting all standard transports.
 * Auto-configures transport handlers based on the runtime environment.
 */
public class DefaultSockJsService extends TransportHandlingSockJsService {
    /**
     * Create a service with a task scheduler for heartbeats and timeouts.
     *
     * @param scheduler the task scheduler
     */
    public DefaultSockJsService(TaskScheduler scheduler);

    /**
     * Create a service with custom transport handlers.
     *
     * @param scheduler the task scheduler
     * @param handlers  the transport handlers to use
     */
    public DefaultSockJsService(
            TaskScheduler scheduler,
            Collection<TransportHandler> handlers);
}

AbstractSockJsService

Base implementation of SockJsService with extensive configuration options.

/**
 * Abstract base class for SockJS service implementations. Provides
 * comprehensive configuration options for SockJS behavior.
 */
public abstract class AbstractSockJsService implements SockJsService {
    /**
     * Create a service with a task scheduler.
     *
     * @param scheduler the task scheduler for periodic tasks
     */
    public AbstractSockJsService(TaskScheduler scheduler);

    /**
     * Set the name of the SockJS service (shown in info response).
     * Default is "SockJS".
     *
     * @param name the service name
     */
    public void setName(String name);

    /**
     * Get the service name.
     *
     * @return the name
     */
    public String getName();

    /**
     * Set the URL to the SockJS client library.
     * Used in iframe HTML responses.
     *
     * @param clientLibraryUrl the client library URL
     */
    public void setClientLibraryUrl(String clientLibraryUrl);

    /**
     * Get the client library URL.
     *
     * @return the URL
     */
    public String getClientLibraryUrl();

    /**
     * Set the minimum number of bytes that can be sent over a streaming
     * transport before it's closed and reopened. This prevents browser memory
     * issues with long-lived streams. Default is 128KB.
     *
     * @param streamBytesLimit the byte limit
     */
    public void setStreamBytesLimit(int streamBytesLimit);

    /**
     * Get the stream bytes limit.
     *
     * @return the byte limit
     */
    public int getStreamBytesLimit();

    /**
     * Whether the SockJS path requires a session cookie.
     * Default is true.
     *
     * @param sessionCookieNeeded whether session cookie is needed
     */
    public void setSessionCookieNeeded(boolean sessionCookieNeeded);

    /**
     * Whether session cookies are needed.
     *
     * @return true if needed
     */
    public boolean isSessionCookieNeeded();

    /**
     * Set the heartbeat time in milliseconds. Heartbeats prevent proxies
     * from closing idle connections. Default is 25 seconds.
     *
     * @param heartbeatTime the heartbeat interval
     */
    public void setHeartbeatTime(long heartbeatTime);

    /**
     * Get the heartbeat time.
     *
     * @return the heartbeat interval in milliseconds
     */
    public long getHeartbeatTime();

    /**
     * Set the disconnect delay in milliseconds. This is the time to wait
     * before considering a session disconnected after connection is lost.
     * Default is 5 seconds.
     *
     * @param disconnectDelay the disconnect delay
     */
    public void setDisconnectDelay(long disconnectDelay);

    /**
     * Get the disconnect delay.
     *
     * @return the disconnect delay in milliseconds
     */
    public long getDisconnectDelay();

    /**
     * Set the number of server-to-client messages to cache per session.
     * Default is 100.
     *
     * @param httpMessageCacheSize the cache size
     */
    public void setHttpMessageCacheSize(int httpMessageCacheSize);

    /**
     * Get the HTTP message cache size.
     *
     * @return the cache size
     */
    public int getHttpMessageCacheSize();

    /**
     * Whether WebSocket transport is enabled. Default is true.
     *
     * @param webSocketEnabled whether WebSocket is enabled
     */
    public void setWebSocketEnabled(boolean webSocketEnabled);

    /**
     * Whether WebSocket transport is enabled.
     *
     * @return true if enabled
     */
    public boolean isWebSocketEnabled();

    /**
     * Set HandshakeInterceptors for the SockJS handshake.
     *
     * @param interceptors the interceptors
     */
    public void setHandshakeInterceptors(List<HandshakeInterceptor> interceptors);

    /**
     * Get the handshake interceptors.
     *
     * @return the interceptors
     */
    public List<HandshakeInterceptor> getHandshakeInterceptors();

    /**
     * Whether to suppress CORS handling. If true, CORS headers must be
     * configured elsewhere. Default is false.
     *
     * @param suppressCors whether to suppress CORS
     */
    public void setSuppressCors(boolean suppressCors);

    /**
     * Whether CORS handling is suppressed.
     *
     * @return true if suppressed
     */
    public boolean shouldSuppressCors();

    /**
     * Set allowed origins for CORS. Use "*" to allow all origins.
     *
     * @param allowedOrigins the allowed origins
     */
    public void setAllowedOrigins(Collection<String> allowedOrigins);

    /**
     * Set allowed origin patterns with wildcard support.
     *
     * @param allowedOriginPatterns the allowed origin patterns
     */
    public void setAllowedOriginPatterns(Collection<String> allowedOriginPatterns);

    /**
     * Set the message codec for encoding/decoding SockJS messages.
     *
     * @param messageCodec the message codec
     */
    public void setMessageCodec(SockJsMessageCodec messageCodec);

    /**
     * Get the message codec.
     *
     * @return the message codec
     */
    public SockJsMessageCodec getMessageCodec();

    @Override
    public void handleRequest(
            ServerHttpRequest request,
            ServerHttpResponse response,
            String sockJsPath,
            WebSocketHandler wsHandler) throws SockJsException;
}

TransportHandler Interface

Handler for a specific SockJS transport.

/**
 * Handler for a specific SockJS transport type. Each transport handler
 * implements the protocol for a particular transport mechanism.
 */
public interface TransportHandler {
    /**
     * Get the transport type handled by this handler.
     *
     * @return the transport type
     */
    TransportType getTransportType();

    /**
     * Handle a transport request.
     *
     * @param request    the HTTP request
     * @param response   the HTTP response
     * @param wsHandler  the WebSocket handler
     * @param session    the SockJS session
     * @throws SockJsException if handling fails
     */
    void handleRequest(
            ServerHttpRequest request,
            ServerHttpResponse response,
            WebSocketHandler wsHandler,
            SockJsSession session) throws SockJsException;
}

WebSocketTransportHandler

Transport handler for native WebSocket connections.

/**
 * TransportHandler for WebSocket transport. Upgrades the HTTP connection
 * to a WebSocket connection when available.
 */
public class WebSocketTransportHandler implements TransportHandler {
    /**
     * Create a WebSocket transport handler with a handshake handler.
     *
     * @param handshakeHandler the handshake handler
     */
    public WebSocketTransportHandler(HandshakeHandler handshakeHandler);

    @Override
    public TransportType getTransportType();

    @Override
    public void handleRequest(
            ServerHttpRequest request,
            ServerHttpResponse response,
            WebSocketHandler wsHandler,
            SockJsSession session) throws SockJsException;
}

XhrPollingTransportHandler

Transport handler for XHR polling.

/**
 * TransportHandler for XHR polling transport. Implements request-response
 * polling where each request returns immediately with pending messages or
 * an empty response.
 */
public class XhrPollingTransportHandler extends AbstractHttpSendingTransportHandler {
    /**
     * Create an XHR polling transport handler.
     */
    public XhrPollingTransportHandler();

    @Override
    public TransportType getTransportType();
}

XhrStreamingTransportHandler

Transport handler for XHR streaming.

/**
 * TransportHandler for XHR streaming transport. Establishes a long-lived
 * HTTP connection and streams messages as they become available.
 */
public class XhrStreamingTransportHandler extends AbstractHttpSendingTransportHandler {
    /**
     * Create an XHR streaming transport handler.
     */
    public XhrStreamingTransportHandler();

    @Override
    public TransportType getTransportType();
}

EventSourceTransportHandler

Transport handler for Server-Sent Events.

/**
 * TransportHandler for Server-Sent Events (EventSource) transport.
 * Uses the EventSource browser API for server-to-client streaming.
 */
public class EventSourceTransportHandler extends AbstractHttpSendingTransportHandler {
    /**
     * Create an EventSource transport handler.
     */
    public EventSourceTransportHandler();

    @Override
    public TransportType getTransportType();
}

HtmlFileTransportHandler

Transport handler for HTML file streaming.

/**
 * TransportHandler for HTML file (forever iframe) transport.
 * Streams messages through a hidden iframe for older browsers.
 */
public class HtmlFileTransportHandler extends AbstractHttpSendingTransportHandler {
    /**
     * Create an HTML file transport handler.
     */
    public HtmlFileTransportHandler();

    @Override
    public TransportType getTransportType();
}

SockJsSession

Abstract base class representing a SockJS session.

/**
 * Represents a SockJS session. A session may use different transports
 * over its lifetime as connections are established and terminated.
 */
public abstract class SockJsSession {
    /**
     * Get the session ID.
     *
     * @return the session ID
     */
    public abstract String getId();

    /**
     * Get the WebSocket handler for this session.
     *
     * @return the handler
     */
    public WebSocketHandler getHandler();

    /**
     * Whether the session is currently active.
     *
     * @return true if active
     */
    public abstract boolean isActive();

    /**
     * Send a message to the client.
     *
     * @param message the message to send
     * @throws IOException if sending fails
     */
    public abstract void sendMessage(WebSocketMessage<?> message) throws IOException;

    /**
     * Close the session with a status.
     *
     * @param status the close status
     * @throws IOException if closing fails
     */
    public abstract void close(CloseStatus status) throws IOException;
}

SockJS Frame Support

SockJS frame encoding and decoding.

/**
 * Codec for encoding and decoding SockJS message frames.
 */
public interface SockJsMessageCodec {
    /**
     * Encode messages into a SockJS frame.
     *
     * @param messages the messages to encode
     * @return the encoded frame content
     * @throws IOException if encoding fails
     */
    String encode(String... messages) throws IOException;

    /**
     * Decode a SockJS frame into messages.
     *
     * @param content the frame content
     * @return array of decoded messages
     * @throws IOException if decoding fails
     */
    String[] decode(String content) throws IOException;
}

/**
 * SockJS message codec using Jackson 2 for JSON encoding/decoding.
 */
public class Jackson2SockJsMessageCodec extends AbstractSockJsMessageCodec {
    /**
     * Create a codec with default ObjectMapper.
     */
    public Jackson2SockJsMessageCodec();

    /**
     * Create a codec with a custom ObjectMapper.
     *
     * @param objectMapper the ObjectMapper to use
     */
    public Jackson2SockJsMessageCodec(ObjectMapper objectMapper);

    @Override
    public String encode(String... messages) throws IOException;

    @Override
    public String[] decode(String content) throws IOException;
}

/**
 * Represents a SockJS frame with its type and data.
 */
public class SockJsFrame {
    /**
     * Create an open frame (session established).
     */
    public static SockJsFrame openFrame();

    /**
     * Create a heartbeat frame (keep connection alive).
     */
    public static SockJsFrame heartbeatFrame();

    /**
     * Create a message frame with encoded messages.
     *
     * @param codec    the message codec
     * @param messages the messages to encode
     * @return the message frame
     */
    public static SockJsFrame messageFrame(
            SockJsMessageCodec codec,
            String... messages);

    /**
     * Create a close frame (session closed).
     *
     * @param code   the close code
     * @param reason the close reason
     * @return the close frame
     */
    public static SockJsFrame closeFrame(int code, String reason);

    /**
     * Get the frame type.
     *
     * @return the frame type
     */
    public SockJsFrameType getType();

    /**
     * Get the frame data content.
     *
     * @return the frame data
     */
    public String getFrameData();

    /**
     * Get the message content (for message frames).
     *
     * @return the message content
     */
    public String getMessageContent();
}

/**
 * Enumeration of SockJS frame types.
 */
public enum SockJsFrameType {
    OPEN,
    HEARTBEAT,
    MESSAGE,
    CLOSE
}

/**
 * Format for SockJS frames. Different transports may format frames differently.
 */
public interface SockJsFrameFormat {
    /**
     * Format a SockJS frame for transmission.
     *
     * @param frame the frame to format
     * @return the formatted frame string
     */
    String format(SockJsFrame frame);
}

SockJS Exceptions

/**
 * Base exception for SockJS errors.
 */
public class SockJsException extends NestedRuntimeException {
    /**
     * Create an exception with message and cause.
     *
     * @param message the error message
     * @param cause   the underlying cause
     */
    public SockJsException(String message, Throwable cause);

    /**
     * Create an exception with message, session ID, and cause.
     *
     * @param message   the error message
     * @param sessionId the SockJS session ID
     * @param cause     the underlying cause
     */
    public SockJsException(String message, String sessionId, Throwable cause);

    /**
     * Get the SockJS session ID associated with the error.
     *
     * @return the session ID, or null if not associated with a session
     */
    public String getSockJsSessionId();
}

/**
 * Exception thrown when message delivery fails.
 */
public class SockJsMessageDeliveryException extends SockJsException {
    // Message delivery failure
}

/**
 * Exception thrown when transport fails.
 */
public class SockJsTransportFailureException extends SockJsException {
    // Transport failure
}

Usage Examples

Basic SockJS Configuration

import org.springframework.web.socket.config.annotation.*;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myHandler(), "/ws")
                .withSockJS();  // Enable SockJS fallback
    }

    @Bean
    public WebSocketHandler myHandler() {
        return new MyWebSocketHandler();
    }
}

Custom SockJS Configuration

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myHandler(), "/ws")
                .withSockJS()
                .setClientLibraryUrl("https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js")
                .setStreamBytesLimit(512 * 1024)      // 512KB
                .setHeartbeatTime(20000)              // 20 seconds
                .setDisconnectDelay(10000)            // 10 seconds
                .setHttpMessageCacheSize(1000)
                .setWebSocketEnabled(true)
                .setSuppressCors(false);
    }
}

Custom Transport Handlers

import org.springframework.web.socket.sockjs.transport.TransportHandler;
import org.springframework.web.socket.sockjs.transport.handler.*;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myHandler(), "/ws")
                .withSockJS()
                .setTransportHandlers(customTransportHandlers());
    }

    private TransportHandler[] customTransportHandlers() {
        return new TransportHandler[] {
            new WebSocketTransportHandler(handshakeHandler()),
            new XhrPollingTransportHandler(),
            new XhrStreamingTransportHandler(),
            new EventSourceTransportHandler()
            // Omit HtmlFileTransportHandler if not needed
        };
    }

    @Bean
    public HandshakeHandler handshakeHandler() {
        return new DefaultHandshakeHandler();
    }
}

SockJS with Interceptors

import org.springframework.web.socket.server.HandshakeInterceptor;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myHandler(), "/ws")
                .addInterceptors(new HttpSessionHandshakeInterceptor())
                .withSockJS()
                .setInterceptors(customInterceptor());
    }

    private HandshakeInterceptor customInterceptor() {
        return new HandshakeInterceptor() {
            @Override
            public boolean beforeHandshake(
                    ServerHttpRequest request,
                    ServerHttpResponse response,
                    WebSocketHandler wsHandler,
                    Map<String, Object> attributes) throws Exception {

                // Validate SockJS specific requirements
                String transport = extractTransport(request);
                System.out.println("SockJS transport: " + transport);

                return true;
            }

            @Override
            public void afterHandshake(
                    ServerHttpRequest request,
                    ServerHttpResponse response,
                    WebSocketHandler wsHandler,
                    Exception exception) {
                // Post-handshake logic
            }

            private String extractTransport(ServerHttpRequest request) {
                String path = request.getURI().getPath();
                String[] parts = path.split("/");
                return parts.length > 3 ? parts[3] : "unknown";
            }
        };
    }
}

Programmatic SockJS Service

import org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
public class SockJsServiceConfig {

    @Bean
    public DefaultSockJsService sockJsService() {
        DefaultSockJsService service = new DefaultSockJsService(taskScheduler());

        service.setName("CustomSockJS");
        service.setClientLibraryUrl("/js/sockjs.js");
        service.setStreamBytesLimit(256 * 1024);
        service.setSessionCookieNeeded(true);
        service.setHeartbeatTime(25000);
        service.setDisconnectDelay(5000);
        service.setHttpMessageCacheSize(100);
        service.setWebSocketEnabled(true);

        // Configure CORS
        service.setAllowedOrigins(Arrays.asList(
                "https://example.com",
                "https://app.example.com"
        ));

        return service;
    }

    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(4);
        scheduler.setThreadNamePrefix("sockjs-");
        scheduler.initialize();
        return scheduler;
    }
}

Client-Side SockJS Usage (JavaScript)

// Connect to SockJS endpoint
var socket = new SockJS('http://localhost:8080/ws');

socket.onopen = function() {
    console.log('Connected to SockJS');
    socket.send('Hello Server!');
};

socket.onmessage = function(event) {
    console.log('Received:', event.data);
};

socket.onclose = function(event) {
    console.log('Disconnected:', event.code, event.reason);
};

socket.onerror = function(error) {
    console.error('Error:', error);
};

// Close connection
// socket.close();

SockJS with STOMP

@Configuration
@EnableWebSocketMessageBroker
public class StompSockJsConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/stomp")
                .setAllowedOrigins("*")
                .withSockJS()
                .setHeartbeatTime(20000)
                .setDisconnectDelay(10000)
                .setWebSocketEnabled(true)
                .setClientLibraryUrl("/js/sockjs.min.js");
    }

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic", "/queue");
        registry.setApplicationDestinationPrefixes("/app");
    }
}

Monitoring SockJS Sessions

import org.springframework.web.socket.config.WebSocketMessageBrokerStats;

@Service
public class SockJsMonitoringService {

    private final WebSocketMessageBrokerStats stats;

    public SockJsMonitoringService(WebSocketMessageBrokerStats stats) {
        this.stats = stats;
    }

    @Scheduled(fixedRate = 60000)
    public void logStats() {
        System.out.println("Total Sessions: " + stats.getWebSocketSessionStatsInfo());
        System.out.println("Session Info: " + stats.getStompSubProtocolStatsInfo());
        System.out.println("Broker Stats: " + stats.getStompBrokerRelayStatsInfo());
    }
}

Custom Message Codec

import org.springframework.web.socket.sockjs.frame.SockJsMessageCodec;

public class CustomMessageCodec implements SockJsMessageCodec {

    @Override
    public String encode(String... messages) throws IOException {
        // Custom encoding logic
        JSONArray array = new JSONArray();
        for (String message : messages) {
            array.put(message);
        }
        return array.toString();
    }

    @Override
    public String[] decode(String content) throws IOException {
        // Custom decoding logic
        JSONArray array = new JSONArray(content);
        String[] messages = new String[array.length()];
        for (int i = 0; i < array.length(); i++) {
            messages[i] = array.getString(i);
        }
        return messages;
    }
}

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myHandler(), "/ws")
                .withSockJS()
                .setMessageCodec(new CustomMessageCodec());
    }
}

Disabling Specific Transports

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(myHandler(), "/ws")
                .withSockJS()
                // Only allow WebSocket and XHR polling
                .setTransportHandlers(
                    new WebSocketTransportHandler(handshakeHandler()),
                    new XhrPollingTransportHandler()
                );
    }
}