// Package websocket implements the hub that pushes messages to connected // clients: WebSocket clients upgraded by Hub.ServeHTTP (the /ws endpoint) and // plain TCP clients run by Hub.ServeTCP, which send the same messages as // newline-delimited JSON (see tcp.go). Package tcpserver runs a TCP server // for those clients. // // Every message is a JSON Envelope: a WebSocket text frame, or one line on // TCP. // // Client -> server: // // {"type":"ping","id":"1"} // {"type":"echo","id":"2","payload":{...}} // // Server -> client: // // {"type":"welcome","payload":{"clientId":"..."}} // {"type":"pong","id":"1","payload":{"time":"..."}} // {"type":"echo","id":"2","payload":{...}} // {"type":"error","id":"3","error":"..."} // {"type":"item.created","payload":{...}} // // Any other type from a client gets an "unknown message type" error. The last // message is one sent by Hub.Push: its type is the pushed message type. package websocket import "encoding/json" // Envelope is the wire format for every message in both directions. type Envelope struct { Type string `json:"type"` ID string `json:"id,omitempty"` Payload json.RawMessage `json:"payload,omitempty"` Error string `json:"error,omitempty"` } // Envelope types. TypeEvent, TypeRequest and TypeAck are reserved: nothing // sends or handles them yet. const ( TypeWelcome = "welcome" TypePing = "ping" TypePong = "pong" TypeEcho = "echo" TypeEvent = "event" TypeRequest = "request" TypeAck = "ack" TypeError = "error" ) // mustRaw returns v as raw JSON: v itself if it is already json.RawMessage, // nil if v is nil or can't be marshalled. func mustRaw(v any) json.RawMessage { if v == nil { return nil } if raw, ok := v.(json.RawMessage); ok { return raw } b, err := json.Marshal(v) if err != nil { return nil } return b }