WebSocket

Helidon WebSocket Support

Overview

Helidon provides support for WebSocket. The WebSocket API enables Java applications to participate in WebSocket interactions as both servers and clients. The server API supports annotated and programmatic endpoints.

Annotated endpoints use Java annotations to define WebSocket handlers; programmatic endpoints implement API interfaces and are annotation-free. Helidon uses WebSocketRouting to configure routing for both styles.

Maven Coordinates

To enable WebSocket, add the following dependency to your project’s pom.xml (see Managing Dependencies).

pom.xml
<dependency>
  <groupId>io.helidon.webserver</groupId>
  <artifactId>helidon-webserver-websocket</artifactId>
</dependency>

Example

This section describes the implementation of a simple application that uses a REST resource to push messages into a shared queue and a programmatic WebSocket endpoint to download messages from the queue, one at a time, over a connection. The example will show how REST and WebSocket connections can be seamlessly combined into a Helidon application.

The complete Helidon example is available here. Let us start by looking at MessageQueueService:

record MessageQueueService(Queue<String> messageQueue) implements HttpService {
    @Override
    public void routing(HttpRules routingRules) {
        routingRules.post("/board", (req, res) -> {
            messageQueue.add(req.content().as(String.class));
            res.status(204).send();
        });
    }
}

This class exposes a REST resource where messages can be posted. Upon receiving a message, it simply pushes it into a shared queue and returns 204 (No Content).

Messages pushed into the queue can be obtained by opening a WebSocket connection served by MessageBoardEndpoint:

record MessageBoardEndpoint(Queue<String> messageQueue) implements WsListener {
    @Override
    public void onMessage(WsSession session, String text, boolean last) {
        // Send all messages in the queue
        if (text.equals("send")) {
            while (!messageQueue.isEmpty()) {
                session.send(messageQueue.poll(), last);
            }
        }
    }
}

This is an example of a programmatic endpoint that extends WsListener. The method onMessage will be invoked for every message. In this example, when the special send message is received, it empties the shared queue sending messages one at a time over the WebSocket connection.

In Helidon, REST and WebSocket classes need to be manually registered into the web server. This is accomplished via a Routing builder:

HttpService staticContent = StaticContentFeature.createService(
        ClasspathHandlerConfig.builder()
                .location("/WEB")
                .welcome("index.html")
                .build());
Queue<String> messageQueue = new ConcurrentLinkedQueue<>();
server.routing(it -> it
                .register("/web", staticContent)
                .register("/rest", new MessageQueueService(messageQueue)))
        .addRouting(WsRouting.builder()
                            .endpoint("/websocket/board", new MessageBoardEndpoint(messageQueue)));

This code snippet registers MessageBoardEndpoint at /websocket/board and associates.

Buffered Message Size

When Helidon combines WebSocket fragments before delivering a whole message to an endpoint, the server and client each use a default buffering threshold of 1 MiB. Configure the server limit with server.protocols.websocket.max-buffered-message-size, and the client limit with protocol-config.max-buffered-message-size in WsClient configuration. If a combined message exceeds the applicable limit, that side closes the connection with WebSocket close code 1009.

The server limit is independent of max-frame-length, which applies to each individual frame. Binary messages are measured exactly in bytes. Text message size is approximated using the number of UTF-16 code units in each decoded string fragment and may be smaller than the UTF-8 payload size. Listener methods that accept the trailing boolean fragment indicator, Reader, or InputStream consume fragments without whole-message buffering and are not limited by max-buffered-message-size.

Security

When a WebSocket upgrade request includes an Origin header, Helidon validates it. By default, Helidon requires the Origin authority to match the request Host header and rejects cross-host requests. If you configure server.protocols.websocket.origins, the configured values act as an explicit allowlist instead. Requests without an Origin header are allowed so non-browser clients continue to work.

Reference

Copyright © 2018, 2026 Oracle and/or its affiliates.