Web Sockets

Websockets give you a bi-directional communication channel between a client and a server.

To publish, create a WebSocketHandlerBuilder and add it as a normal handler. That builder requires that you pass it a MuWebSocketFactory which allows you to inspect the websocket upgrade request and either reject it (by returning null) or by creating a MuWebSocket.

From your websocket factory, it's highly recommended that you return a web socket by extending BaseWebSocket as it will provide some useful default implementation (e.g. handling closing sockets, pings, and pongs) and it also provides the session() method which is the way to send messages to the client.

The following example creates a websocket handler at /echo-socket which simply echos any text messages it receives back to the client:

public class WebSocketExample {
    public static void main(String[] args) {
        MuServer server = httpServer()
            .addHandler(
                WebSocketHandlerBuilder.webSocketHandler()
                    .withPath("/echo-socket")
                    .withWebSocketFactory(new MuWebSocketFactory() {
                        @Override
                        public MuWebSocket create(MuRequest request, Headers responseHeaders) {
                            return new BaseWebSocket() {
                                @Override
                                public void onText(String message, boolean isLast, DoneCallback onComplete) {
                                    session().sendText("Received " + message, onComplete);
                                }
                            };
                        }
                    })
            )
            .addHandler(ResourceHandlerBuilder.fileOrClasspath("src/main/resources/samples", "/samples"))
            .start();
        System.out.println("Open " + server.uri().resolve("/websocket.html") + " to try.");
    }
}
(see full file)

This page connects to the endpoint above and sends messages, and listens for responses:

<main>
    <p>Run <code>WebSocketExample.java</code> and then load this page. This simply echos back
        any messages you send. Send <code>close</code> to close the socket from the server.</p>
    <form>
        <fieldset>
            <label>Message: <input type="text" id="message" required></label>
            <input type="submit">
            <input type="button" value="Close connection" id="close">
        </fieldset>
    </form>

    <p>Status: <span class="status">Connecting...</span></p>
    <ul class="received"></ul>
</main>

<script>
    document.addEventListener('DOMContentLoaded', () => {
        const $ = document.querySelector.bind(document);

        const status = $('.status');
        const socket = new WebSocket('ws://' + location.host + '/echo-socket');

        socket.addEventListener('open', () => {
            status.textContent = 'Connected';
        });
        socket.addEventListener('error', event => {
            status.textContent = 'Error! ' + event;
        });
        socket.addEventListener('close', event => {
            status.textContent = 'Closed: ' + event.code + ' ' + event.reason +
                ' (' + (event.wasClean ? '' : 'not ') + 'clean)';
            $('fieldset').setAttribute('disabled', 'disabled')
        });
        socket.addEventListener('message', event => {
            const li = $('.received').appendChild(document.createElement('li'));
            li.textContent = event.data;
        });
        $('#close').addEventListener('click', () => {
            socket.close(1000, 'Client closed');
        });
        $('form').addEventListener('submit', e => {
            e.preventDefault();
            const mb = $('#message');
            socket.send(mb.value);
            mb.value = '';
            mb.focus();
        });
    });
</script>
(see full file)

Note: if you are only sending from the server to the client, consider using Server Sent Events (SSE) instead.