AtlatestRepositorysigil-websocket
sigil-websocket / tree / docswebsocket.md
1
# WebSocket3
> WebSocket client library supporting ws:// and wss:// connections (RFC 6455).5
```scheme6
(import (sigil websocket))7
```9
## Connecting11
`ws-connect` opens a WebSocket connection via the HTTP upgrade handshake. Supports both plain (`ws://`) and TLS (`wss://`) URLs.13
```scheme14
;; Plain WebSocket15
(define conn (ws-connect "ws://localhost:8080/socket"))17
;; Secure WebSocket (TLS)18
(define conn (ws-connect "wss://example.com/socket"))19
```21
Returns a `ws-connection` on success, or `#f` if the connection or handshake fails.23
## Sending Messages25
### ws-send27
Send a text message:29
```scheme30
(ws-send conn "Hello, server!")31
```33
### ws-send-binary35
Send binary data as a bytevector:37
```scheme38
(ws-send-binary conn #u8(1 2 3 4))39
```41
### ws-ping43
Send a ping frame to check connectivity:45
```scheme46
(ws-ping conn)47
(ws-ping conn "payload") ; optional payload48
```50
The server responds with a pong frame, which is handled automatically by `ws-receive`.52
## Receiving Messages54
### ws-receive56
Block until a complete message arrives. Returns a `ws-message`, the symbol `'closed` if the connection was closed, or `#f` on error.58
```scheme59
(let ((msg (ws-receive conn)))60
(cond61
((ws-message? msg)62
(display (ws-message-data msg)))63
((eq? msg 'closed)64
(display "Connection closed"))))65
```67
When running inside an async scheduler, `ws-receive` yields the current coroutine while waiting for data.69
### ws-message Accessors71
```scheme72
(ws-message? msg) ; => #t73
(ws-message-type msg) ; => 'text or 'binary74
(ws-message-data msg) ; => string (text) or bytevector (binary)75
```77
Dispatch on message type:79
```scheme80
(let ((msg (ws-receive conn)))81
(when (ws-message? msg)82
(case (ws-message-type msg)83
((text) (display (ws-message-data msg)))84
((binary) (process-bytes (ws-message-data msg))))))85
```87
## Connection State89
```scheme90
(ws-connection? conn) ; => #t (type predicate)91
(ws-connected? conn) ; => #t (state is 'open)92
(ws-connection-state conn) ; => 'connecting, 'open, 'closing, or 'closed93
(ws-connection-url conn) ; => "ws://localhost:8080/socket"94
(ws-connection-socket conn) ; => underlying TCP socket or TLS connection95
```97
## Closing99
`ws-close` sends a close frame (status 1000 — normal closure) and closes the underlying socket.101
```scheme102
(ws-close conn)103
(ws-connected? conn) ; => #f104
```106
If the server initiates a close, `ws-receive` returns `'closed` and the close handshake is completed automatically.108
## Frame API110
The `(sigil websocket frame)` module provides low-level frame encoding and decoding for advanced use cases.112
```scheme113
(import (sigil websocket frame))114
```116
### Opcodes118
| Constant | Value | Description |119
|----------|-------|-------------|120
| `opcode-text` | `#x1` | Text data frame |121
| `opcode-binary` | `#x2` | Binary data frame |122
| `opcode-close` | `#x8` | Connection close |123
| `opcode-ping` | `#x9` | Ping |124
| `opcode-pong` | `#xA` | Pong |125
| `opcode-continuation` | `#x0` | Fragment continuation |127
```scheme128
(opcode-control? opcode-ping) ; => #t129
(opcode-control? opcode-text) ; => #f130
```132
### Encoding Frames134
All client frames are masked per RFC 6455.136
```scheme137
(encode-text-frame "hello") ; => bytevector138
(encode-binary-frame #u8(1 2 3)) ; => bytevector139
(encode-close-frame) ; => bytevector (no status)140
(encode-close-frame 1000) ; => bytevector (normal closure)141
(encode-ping-frame) ; => bytevector142
(encode-pong-frame payload-bv) ; => bytevector144
;; General-purpose: (encode-frame opcode payload fin? mask?)145
(encode-frame opcode-text "data" #t #t)146
```148
### Decoding Frames150
```scheme151
(let ((result (decode-frame bytevector-data)))152
(when (frame-decode-result-frame result)153
(let ((frame (frame-decode-result-frame result))154
(consumed (frame-decode-result-bytes-consumed result)))155
(display (ws-frame-opcode frame))156
(display (ws-frame-payload frame))157
(display (ws-frame-fin? frame)))))158
```160
Returns a `frame-decode-result` with `#f` for the frame if the data is incomplete.162
### Masking164
```scheme165
(define key (generate-mask-key)) ; => 4-byte bytevector166
(define masked (apply-mask data key))167
(define original (apply-mask masked key)) ; XOR is reversible168
```170
## Common Patterns172
### Echo Client174
```scheme175
(import (sigil websocket))177
(let ((conn (ws-connect "ws://localhost:8080/echo")))178
(when conn179
(ws-send conn "Hello!")180
(let ((msg (ws-receive conn)))181
(when (ws-message? msg)182
(display (ws-message-data msg)))) ; => "Hello!"183
(ws-close conn)))184
```186
### JSON Message Exchange188
```scheme189
(import (sigil websocket)190
(sigil json))192
(let ((conn (ws-connect "ws://localhost:8080/api")))193
(when conn194
;; Send JSON195
(ws-send conn (json->string '((action . "subscribe")196
(channel . "updates"))))197
;; Receive JSON198
(let ((msg (ws-receive conn)))199
(when (ws-message? msg)200
(let ((data (string->json (ws-message-data msg))))201
(display (assoc-ref 'status data)))))202
(ws-close conn)))203
```205
### Reconnection Loop207
```scheme208
(import (sigil websocket))210
(define (connect-with-retry url max-attempts)211
(let loop ((attempt 1))212
(let ((conn (ws-connect url)))213
(cond214
(conn conn)215
((< attempt max-attempts)216
(display (string-append "Retry " (number->string attempt) "...\n"))217
(sleep 1)218
(loop (+ attempt 1)))219
(else #f)))))220
```