Class WebSocketSession

java.lang.Object
com.codename1.backend.WebSocketSession

public final class WebSocketSession extends Object

One websocket connection: the decoder that turns bytes into messages, and the handle an application sends through.

Why the decoder is a state machine rather than a loop over whole frames

A frame arrives in as many reads as the network chooses. The header may be split across two of them, the mask key across four, and a 10MB payload across thousands -- so every piece of state that spans a read boundary lives in a field here: how much of the current payload is still owed, how far into the mask key the next byte lands, whether a message is open and which opcode started it, and how far a UTF-8 character got. pump() consumes whatever has arrived and stops cleanly when it wants more, which is what lets the same decoder be driven by a blocking loop on a virtual thread and by one reactor turn on a pool worker.

Why this buffer is its own

The HTTP read path borrows a per-host-thread array and parses in place. That is free for a request, which is read, served and written without stopping -- and exactly wrong for a websocket, which stops between every message by design. A borrowed buffer held across a park has to be copied at every park, so this class owns its buffer from the moment of upgrade and never touches Conn.buffer again. See HttpServer.tryUpgrade(HttpServer.Conn, int, long, HttpServer.Request, Span).

Why sending takes a lock and checks a flag three times

Broadcast is the normal reason to run a websocket server, so a send can come from any thread. Two writes to one descriptor would interleave frames, hence writeLock. Worse, the descriptor can be closed under a sender and its NUMBER reused immediately -- so dead is set before the close and checked on the way in, after registering as a writer, and again under the lock. A handle whose connection has gone is inert rather than dangerous.

  • Method Details

    • getId

      public long getId()
      A number unique within this process, for logging and for keying a registry.
    • getPath

      public String getPath()
      The path the client upgraded on, with no query string.
    • getQuery

      public String getQuery()
      The query string, or null when there was none.
    • getHandshakeHeader

      public String getHandshakeHeader(String name)

      A header from the handshake request, case-insensitively.

      Snapshotted at upgrade, because the Request it came from stops being valid the moment the handshake finishes.

    • getSubprotocol

      public String getSubprotocol()
      The negotiated subprotocol, or null when none was.
    • isOpen

      public boolean isOpen()
      Whether this connection is still usable.
    • getAttachment

      public Object getAttachment()
    • setAttachment

      public void setAttachment(Object value)
      Anything the endpoint wants to keep per connection.
    • sendText

      public void sendText(String value) throws IOException
      Throws:
      IOException
    • sendBinary

      public void sendBinary(byte[] value, int offset, int length) throws IOException
      Throws:
      IOException
    • sendBinary

      public void sendBinary(byte[] value) throws IOException
      Throws:
      IOException
    • sendPing

      public void sendPing(byte[] payload, int offset, int length) throws IOException
      Throws:
      IOException
    • sendPong

      public void sendPong(byte[] payload, int offset, int length) throws IOException
      Throws:
      IOException
    • close

      public void close()
      A normal close, code 1000, no reason.
    • close

      public void close(int code, String reason)

      Starts the closing handshake.

      Sends a Close frame and then waits for the peer's, which is what lets the last message already in flight arrive. A peer that never answers is shed by the connection's read timeout rather than held forever.

    • abort

      public void abort()
      Drops the connection with no closing handshake. For a peer already lost.