simple_websocket

RFC 6455 WebSocket Protocol for Eiffel

RFC 6455 v1.0 MIT

Features

Quick Start

Installation

Add to your ECF:

<library name="simple_websocket" location="$SIMPLE_WEBSOCKET\simple_websocket.ecf"/>

Creating Frames

local
    frame: WS_FRAME
    bytes: ARRAY [NATURAL_8]
do
    -- Text frame
    create frame.make_text ("Hello, WebSocket!", True)
    bytes := frame.to_bytes

    -- Binary frame
    create frame.make_binary (<<0x01, 0x02, 0x03>>, True)

    -- Control frames
    create frame.make_ping
    create frame.make_pong
    create frame.make_close (1000, "Normal closure")
end

Client Handshake

local
    handshake: WS_HANDSHAKE
    request: STRING
do
    create handshake.make

    -- Generate client handshake request
    request := handshake.create_client_request ("example.com", "/chat")
    -- Send request to server...

    -- Validate server response
    if handshake.validate_server_response (server_response) then
        io.put_string ("WebSocket connection established!%N")
    else
        io.put_string ("Handshake failed: " + handshake.last_error + "%N")
    end
end

Server Handshake

local
    handshake: WS_HANDSHAKE
    response: STRING
do
    create handshake.make

    -- Parse incoming client request
    if handshake.parse_client_request (client_request) then
        -- Generate server response
        response := handshake.create_server_response
        -- Send response to client...
    else
        -- Reject connection
        io.put_string ("Invalid request: " + handshake.last_error + "%N")
    end
end

Parsing Frames

local
    parser: WS_FRAME_PARSER
    bytes: ARRAY [NATURAL_8]
do
    create parser.make

    -- Add received bytes
    parser.add_bytes (bytes)

    -- Try to parse a complete frame
    if parser.parse and parser.has_frame then
        if attached parser.last_frame as frame then
            if frame.is_text then
                io.put_string ("Received: " + frame.text_payload + "%N")
            elseif frame.is_close then
                io.put_string ("Close code: " + frame.close_code.out + "%N")
            end
        end
    end
end

Message Fragmentation

local
    msg: WS_MESSAGE
    frames: ARRAYED_LIST [WS_FRAME]
do
    -- Create a large message
    create msg.make_text ("Very long message content...")

    -- Split into 1024-byte frames
    frames := msg.to_frames (1024)

    -- First frame has original opcode, rest are CONTINUATION
    -- Last frame has FIN bit set
end

Frame Opcodes (RFC 6455)

Opcode Type Description
0x0 Continuation Fragment continuation
0x1 Text UTF-8 text data
0x2 Binary Binary data
0x8 Close Connection close
0x9 Ping Heartbeat request
0xA Pong Heartbeat response

Close Codes (RFC 6455)

Code Name Description
1000 Normal Clean close
1001 Going Away Server shutting down
1002 Protocol Error Protocol violation
1003 Unsupported Unsupported data type
1007 Invalid Payload Malformed data
1008 Policy Violation Generic policy error
1009 Message Too Big Message exceeds limit
1011 Server Error Unexpected server error

Frame Masking

Per RFC 6455, all client-to-server frames must be masked using XOR with a 4-byte key:

local
    frame: WS_FRAME
    mask: ARRAY [NATURAL_8]
do
    create frame.make_text ("Hello", True)

    -- Set 4-byte mask key (usually random)
    mask := <<0x12, 0x34, 0x56, 0x78>>
    frame.set_mask (mask)

    -- to_bytes will now produce masked output
    bytes := frame.to_bytes
end

The parser automatically unmasks incoming masked frames.

Specifications