Interface WebSocket


public interface WebSocket

What an application implements to serve a websocket route.

One instance per ROUTE, not per connection -- the same shape a @RestController has. Everything that belongs to one client lives on the WebSocketSession handed to each callback, so an endpoint can be stateless and a stateful one puts its state in WebSocketSession.setAttachment(Object).

`@WebSocketMapping("/echo")`
public class Echo implements WebSocket {
public void onOpen(WebSocketSession session) { }
public void onText(WebSocketSession session, String message) throws Exception {
session.sendText(message);
}
public void onBinary(WebSocketSession session, byte\[\] message, int offset, int length)
throws Exception {
session.sendBinary(message, offset, length);
}
}

The build finds that and registers it. A server assembled by hand gets the same thing through a callback rather than a setter -- see Backend.WebSocketEndpoints -- because this runtime is asked for its configuration while it starts and never reconfigured once it is running.

Threading is the part worth reading twice. A callback runs on the thread that owns the connection -- a virtual thread where the server has them, a pool worker otherwise -- and the server reads nothing more from that connection until the callback returns. So an endpoint may block, and a slow endpoint slows down its own client and nobody else's. What it must not do is assume it is alone: a session may be sent to from another thread entirely (that is what makes broadcast possible), and WebSocketSession is written to allow it.

The optional callbacks have empty defaults, so an endpoint overrides only what it cares about. PING is answered automatically before onPing(WebSocketSession, byte[], int, int) is called; an endpoint never has to write the PONG itself.

  • Method Details

    • onOpen

      void onOpen(WebSocketSession session) throws Exception
      The handshake is done and the 101 is on the wire. Sending here is allowed and reaches the client after the handshake, which is the earliest anything can.
      Throws:
      Exception
    • onText

      void onText(WebSocketSession session, String message) throws Exception
      A whole text message, reassembled from however many frames carried it, and already validated as UTF-8.
      Throws:
      Exception
    • onBinary

      void onBinary(WebSocketSession session, byte[] message, int offset, int length) throws Exception

      A whole binary message.

      The array is the session's own reassembly buffer and is valid only until this call returns -- the same contract HttpServer.Request carries, and for the same reason: it is what lets a 10MB message arrive without a copy. An endpoint that needs to keep the bytes copies the range it wants.

      Throws:
      Exception
    • onPing

      default void onPing(WebSocketSession session, byte[] payload, int offset, int length) throws Exception
      A PING arrived. The PONG has already been sent. The payload is borrowed.
      Throws:
      Exception
    • onPong

      default void onPong(WebSocketSession session, byte[] payload, int offset, int length) throws Exception
      A PONG arrived, answering a WebSocketSession.sendPing(byte[], int, int). Borrowed.
      Throws:
      Exception
    • onClose

      default void onClose(WebSocketSession session, int code, String reason) throws Exception

      The connection is finished, for any reason, exactly once per session.

      code is the close code, or 1006 when the connection dropped without one -- which is normal, not exceptional: a process killed mid-suite, a network that went away, a browser tab closed. reason may be empty and is never null. Sending from here does nothing; the socket is already going.

      Throws:
      Exception
    • onError

      default void onError(WebSocketSession session, Exception error)

      Something went wrong: a protocol violation by the peer, an I/O failure, or an exception thrown by one of the callbacks above.

      Reported rather than thrown, because by the time a websocket fails there is nobody left to throw to -- the request that started it returned long ago. onClose(WebSocketSession, int, String) still follows.

    • getSubprotocols

      default String[] getSubprotocols()

      The subprotocols this endpoint speaks, best first, or null for none.

      The SERVER's order decides, not the client's. RFC 6455 leaves the choice open, and honouring the client's order would let a peer select a deprecated protocol over a current one simply by listing it first.