Documentation ¶
Overview ¶
Package websocket provide a library for WebSocket server or client based on RFC6455.
The websocket server is implemented using epoll and kqueue, which means it's only run on Linux, Darwin, or BSD.
Pub-Sub Example ¶
The following code snippet show how to create an authenticated WebSocket server that echo the data frame TEXT back to client.
import ( ... "git.sr.ht/~shulhan/pakakeh.go/lib/websocket" ) var srv *websocket.Server func handleAuth(req *Handshake) (ctx context.Context, err error) { URL, err := url.ParseRequestURI(string(req.URI)) if err != nil { return nil, err } q := URL.Query() extJWT := q.Get("ticket") if len(extJWT) == 0 { return nil, fmt.Errorf("Missing authorization") } ctx = context.WithValue(context.Background(), CtxKeyExternalJWT, extJWT) ctx = context.WithValue(ctx, CtxKeyInternalJWT, _testInternalJWT) ctx = context.WithValue(ctx, CtxKeyUID, _testUID) return ctx, nil } func handleText(conn int, payload []byte) { packet := websocket.NewFrameText(false, payload) ctx := srv.Clients.Context(conn) // ... do something with connection context "ctx" err := websocket.Send(conn, packet) if err != nil { log.Println("handleText: " + err.Error()) } } func main() { opts := &ServerOptions{ Address: ":9001", HandleAuth: handleAuth, HandleText: handleText, } srv, err := websocket.NewServer(opts) if err != nil { log.Println("websocket: " + err.Error()) os.Exit(2) } srv.Start() }
Limitation ¶
Only support WebSocket version 13 (the first and most common version used in web browser).
References ¶
Index ¶
- Constants
- Variables
- func NewBroadcast(message, body string) (packet []byte, err error)
- func NewFrame(opcode Opcode, isMasked bool, payload []byte) []byte
- func NewFrameBin(isMasked bool, payload []byte) []byte
- func NewFrameClose(isMasked bool, code CloseCode, payload []byte) []byte
- func NewFramePing(isMasked bool, payload []byte) (packet []byte)
- func NewFramePong(isMasked bool, payload []byte) (packet []byte)
- func NewFrameText(isMasked bool, payload []byte) []byte
- func Recv(fd int, timeout time.Duration) (packet []byte, err error)
- func Send(fd int, packet []byte, timeout time.Duration) (err error)
- type Client
- func (cl *Client) Close() (err error)
- func (cl *Client) Connect() (err error)
- func (cl *Client) Quit()
- func (cl *Client) SendBin(payload []byte) (err error)
- func (cl *Client) SendPing(payload []byte) (err error)
- func (cl *Client) SendPong(payload []byte) (err error)
- func (cl *Client) SendText(payload []byte) (err error)
- type ClientHandler
- type ClientManager
- type CloseCode
- type ContextKey
- type Frame
- type Frames
- type HandlerAuthFn
- type HandlerClientFn
- type HandlerFrameFn
- type HandlerPayloadFn
- type HandlerStatusFn
- type Handshake
- type Opcode
- type Request
- type Response
- type RouteHandler
- type Server
- type ServerOptions
Constants ¶
const ( // StatusNormal indicates a normal closure, meaning that the purpose // for which the connection was established has been fulfilled. StatusNormal CloseCode = 1000 // StatusGone indicates that an endpoint is "going away", such as a // server going down or a browser having navigated away from a page. StatusGone = 1001 // StatusBadRequest indicates that an endpoint is terminating the // connection due to a protocol error. StatusBadRequest = 1002 // StatusUnsupportedType indicates that an endpoint is terminating the // connection because it has received a type of data it cannot accept // (e.g., an endpoint that understands only text data MAY send this if // it receives a binary message). StatusUnsupportedType = 1003 // StatusInvalidData indicates that an endpoint is terminating // the connection because it has received data within a message that // was not consistent with the type of the message (e.g., non-UTF-8 // [RFC3629] data within a text message). StatusInvalidData = 1007 // StatusForbidden indicates that an endpoint is terminating the // connection because it has received a message that violates its // policy. // This is a generic status code that can be returned when there is no // other more suitable status code (e.g., 1003 or 1009) or if there is // a need to hide specific details about the policy. StatusForbidden = 1008 // StatusRequestEntityTooLarge indicates that an endpoint is // terminating the connection because it has received a message that // is too big for it to process. StatusRequestEntityTooLarge = 1009 // StatusBadGateway indicates that an endpoint (client) is // terminating the connection because it has expected the server to // negotiate one or more extension, but the server didn't return them // in the response message of the WebSocket handshake. // The list of extensions that are needed SHOULD appear in the // "reason" part of the Close frame. // Note that this status code is not used by the server, because it // can fail the WebSocket handshake instead. StatusBadGateway = 1010 // StatusInternalError indicates that a server is terminating the // connection because it encountered an unexpected condition that // prevented it from fulfilling the request. StatusInternalError = 1011 )
List of close code in network byte order. The name of status is mimicking the "net/http" status code.
Endpoints MAY use the following pre-defined status codes when sending a Close frame.
Status code 1004-1006, and 1015 is reserved and MUST NOT be used on Close payload.
See RFC6455 7.4.1-P45 for more information.
Variables ¶
var ( ErrBadRequest = errors.New("bad request") ErrRequestLength = errors.New("bad request: length is less than minimum") ErrRequestHeaderLength = errors.New("bad request: header length is less than minimum") ErrInvalidHTTPMethod = errors.New("invalid HTTP method") ErrInvalidHTTPVersion = errors.New("invalid HTTP version") ErrInvalidHeaderUpgrade = errors.New("invalid Upgrade header") ErrInvalidHeaderFormat = errors.New("invalid Header format") ErrInvalidHeaderHost = errors.New("invalid Host header") ErrInvalidHeaderWSKey = errors.New("invalid Sec-Websocket-Key header") ErrInvalidHeaderWSVersion = errors.New("invalid Sec-Websocket-Version header") ErrInvalidHeaderWSExtensions = errors.New("invalid Sec-Websocket-Extensions header") ErrInvalidHeaderWSProtocol = errors.New("invalid Sec-Websocket-Protocol header") ErrInvalidHeaderConn = errors.New("invalid Connection header") ErrMissingRequiredHeader = errors.New("missing required headers") ErrUnsupportedWSVersion = errors.New("unsupported Sec-WebSocket-Version") )
List of errors.
var ( ErrRouteInvMethod = errors.New("invalid method") ErrRouteInvTarget = errors.New("invalid target") ErrRouteDupParam = errors.New("duplicate parameter on route") )
List of route error values.
var ErrConnClosed = errors.New(`client is not connected`)
ErrConnClosed define an error if client is not connected and try to send a message.
Functions ¶
func NewBroadcast ¶
NewBroadcast create a new message for broadcast by server encoded as JSON and wrapped in TEXT frame.
func NewFrame ¶
NewFrame create a single finished frame with specific operation code and optional payload.
func NewFrameBin ¶
NewFrameBin create a single binary data frame with optional payload. Client frame must be masked.
func NewFrameClose ¶
NewFrameClose create control CLOSE frame. The optional code represent the reason why the endpoint send the CLOSE frame, for closure. The optional payload represent the human readable reason, usually for debugging.
func NewFramePing ¶
NewFramePing create a masked PING control frame.
func NewFramePong ¶
NewFramePong create a masked PONG control frame to be used by client.
func NewFrameText ¶
NewFrameText create a single text data frame with optional payload. Client frame must be masked.
func Recv ¶
Recv read packet from socket fd. The timeout parameter is optional, define the timeout when reading from socket. If timeout is zero the Recv operation will block until a data arrived. If timeout is greater than zero, the Recv operation will return os.ErrDeadlineExceeded when no data received after timeout duration.
func Send ¶
Send the packet through socket file descriptor fd. The timeout parameter is optional, its define the maximum duration when socket write should wait before considered fail. If timeout is zero, Send will block until buffer is available. If timeout is greater than zero, and Send has wait for this duration for buffer available then it will return os.ErrDeadlineExceeded.
Types ¶
type Client ¶
type Client struct { // Headers The headers field can be used to pass custom headers during // handshake with server. // Any primary header fields ("host", "upgrade", "connection", // "sec-websocket-key", "sec-websocket-version") will be deleted // before handshake. Headers http.Header // TLSConfig define custom TLS configuration when connecting to secure // WebSocket server. // The scheme of Endpoint must be "https" or "wss", or it will be // resetting back to nil. TLSConfig *tls.Config // HandleBin callback that will be called after receiving data // frame binary from server. HandleBin ClientHandler // HandleQuit function that will be called when client connection is // closed. // Default is nil. HandleQuit func() // HandleRsvControl function that will be called when client received // reserved control frame (opcode 0xB-F) from server. // Default handler is nil. HandleRsvControl ClientHandler // HandleText callback that will be called after receiving data // frame text from server. HandleText ClientHandler // Endpoint the address of remote server. // The endpoint use the following format, // // ws-URI = "ws://" host [ ":" port ] path [ "?" query ] // wss-URI = "wss://" host [ ":" port ] path [ "?" query ] // // The port component is OPTIONAL, default is 80 for "ws" scheme, and // 443 for "wss" scheme. Endpoint string // The interval where PING control frame will be send to server. // The minimum and default value is 10 seconds. PingInterval time.Duration sync.Mutex // contains filtered or unexported fields }
Client for WebSocket protocol.
Unlike HTTP client or other most commmon TCP oriented client, the WebSocket client is asynchronous or passive-active instead of synchronous. At any time client connection is open to server, client can receive a message broadcasted from server.
Case examples: if client send "A" to server, and expect that server response with "A+", server may send message "B" before sending "A+". Another case is when client connection is open, server may send "B" and "C" in any order without any request send by client previously.
Due to this model, the way to handle response from server is centralized using handlers instead of using single send request-response.
Client Example ¶
The following snippet show how to create a client and handling response from request or broadcast from server,
cl := &Client{ Endpoint: `ws://127.0.0.1:9001`, HandleText: func(cl *Client, frame *Frame) error { // Process response from request or broadcast from // server. return nil } } err := cl.Connect() if err != nil { log.Fatal(err) } err = cl.SendText([]byte("Hello from client")) if err != nil { log.Fatal(err) }
At any time, server may send PING or CLOSE the connection. For this messages, client already handled it by sending PONG message or by closing underlying connection automatically. Implementor can check a closed connection from error returned from Send methods to match with ErrConnClosed.
func (*Client) Close ¶
Close gracefully close the client connection by sending control CLOSE frame with status normal to server and wait for response for as long as 10 seconds.
func (*Client) Quit ¶
func (cl *Client) Quit()
Quit force close the client connection without sending control CLOSE frame. This function MUST be used only when error receiving packet from server (e.g. lost connection) to release the resource.
func (*Client) SendBin ¶
SendBin send data frame as binary to server. If handler is nil, no response will be read from server.
type ClientHandler ¶
ClientHandler define a callback type for client to handle packet from server (either broadcast or from response of request) in the form of frame.
Returning a non-nil error will cause the underlying connection to be closed.
type ClientManager ¶
ClientManager manage list of active websocket connections on server.
This library assume that each connection belong to a user in the server, where each user is representated by uint64.
For a custom management of user use HandleClientAdd and HandleClientRemove on Server.
func (*ClientManager) All ¶
func (cls *ClientManager) All() (conns []int)
All return a copy of all client connections.
func (*ClientManager) Conns ¶
func (cls *ClientManager) Conns(uid uint64) (conns []int)
Conns return list of connections by user ID.
Each user may have more than one connection (e.g. from Android, iOS, or web). By knowing which connections that user have, implementor of websocket server can broadcast a message to all connections.
type ContextKey ¶
type ContextKey byte
ContextKey define a type for context.
const ( CtxKeyExternalJWT ContextKey = 1 << iota CtxKeyInternalJWT CtxKeyUID )
List of valid context key.
type Frame ¶
type Frame struct {
// contains filtered or unexported fields
}
Frame represent a WebSocket data protocol.
type Frames ¶
type Frames struct {
// contains filtered or unexported fields
}
Frames represent continuous (fragmented) frame.
A fragmented message consists of a single frame with the FIN bit clear and an opcode other than 0, followed by zero or more frames with the FIN bit clear and the opcode set to 0, and terminated by a single frame with the FIN bit set and an opcode of 0.
func Unpack ¶
Unpack websocket data protocol from raw bytes to one or more frames.
When receiving packet from client, the underlying protocol or operating system may buffered the packet. Client may send a single frame one at time, but server may receive one or more frame in one packet; and vice versa. That's the reason why the Unpack return multiple frame instead of single frame.
On success it will return one or more frames. On fail it will return zero frame.
type HandlerAuthFn ¶
HandlerAuthFn define server callback type to handle authentication request.
type HandlerClientFn ¶
HandlerClientFn define server callback type to handle new client connection or removed client connection.
type HandlerFrameFn ¶
HandlerFrameFn define a server callback type to handle client request with single frame.
type HandlerPayloadFn ¶
HandlerPayloadFn define server callback type to handle data frame from client.
type HandlerStatusFn ¶
HandlerStatusFn define server callback type to handle status request. It must return the content type of data, for example "text/plain", and the status data to be send to client.
type Handshake ¶
type Handshake struct { URL *url.URL Header http.Header Host []byte Key []byte Extensions []byte Protocol []byte // contains filtered or unexported fields }
Handshake contains the websocket HTTP handshake request.
type Opcode ¶
type Opcode byte
Opcode represent the websocket operation code.
const ( OpcodeCont Opcode = 0x0 OpcodeText Opcode = 0x1 OpcodeBin Opcode = 0x2 OpcodeDataRsv3 Opcode = 0x3 // %x3-7 are reserved for further non-control frames OpcodeDataRsv4 Opcode = 0x4 OpcodeDataRsv5 Opcode = 0x5 OpcodeDataRsv6 Opcode = 0x6 OpcodeDataRsv7 Opcode = 0x7 OpcodeClose Opcode = 0x8 OpcodePing Opcode = 0x9 OpcodePong Opcode = 0xA OpcodeControlRsvB Opcode = 0xB // %xB-F are reserved for further control frames OpcodeControlRsvC Opcode = 0xC OpcodeControlRsvD Opcode = 0xD OpcodeControlRsvE Opcode = 0xE OpcodeControlRsvF Opcode = 0xF )
List of valid operation code in frame.
type Request ¶
type Request struct { // Query is Target query. Query url.Values `json:"-"` // Params are parameters as key-value in Target path that has been // parsed. Params targetParam `json:"-"` // Method is equal to HTTP method. Method string `json:"method"` // Target is equal to HTTP request RequestURI, e.g. "/path?query". Target string `json:"target"` // Body is equal to HTTP body on POST/PUT. Body string `json:"body"` // Path is Target without query. Path string `json:"-"` // // Id is unique between request to differentiate multiple request // since each request is asynchronous. Client can use incremental // value or, the recommended way, using Unix timestamp with // millisecond. // ID uint64 `json:"id"` // Conn is the client connection, where the request come from. Conn int `json:"-"` }
Request define text payload format for client requesting resource on server.
Example of request format,
{ "id": 1512459721269, "method": "GET", "target": "/v1/auth/login", "body": "{ \"token\": \"xxx.yyy.zzz\" }" }
type Response ¶
type Response struct { Message string `json:"message"` Body string `json:"body"` ID uint64 `json:"id"` Code int32 `json:"code"` }
Response contains the data that server send to client as a reply from Request or as broadcast from client subscription.
If response type is a reply from Request, the ID from Request will be copied to the Response, and `code` and `message` field values are equal with HTTP response code and message.
Example of response format for replying request,
{ id: 1512459721269, code: 200, message: "", body: "" }
If response type is broadcast the ID and code MUST be 0, and the `message` field will contain the name of subscription. For example, when recipient of message read the message, server will publish a notification response as,
{ id: 0, code: 0, message: "message.read", body: "{ \"id\": ... }" }
type RouteHandler ¶
RouteHandler is a function that will be called when registered method and target match with request.
type Server ¶
type Server struct { Clients *ClientManager // Options for server, set by calling NewServer. // This field is exported only for reference, for example logging in // the Options when server started. // Modifying the value of Options after server has been started may // cause undefined effects. Options *ServerOptions // contains filtered or unexported fields }
Server for websocket.
func NewServer ¶
func NewServer(opts *ServerOptions) (serv *Server)
NewServer create new WebSocket server.
func (*Server) AllowReservedBits ¶
AllowReservedBits allow receiving frame with RSV1, RSV2, or RSV3 bit set. Calling this function means server has negotiated the extension that use the reserved bits through handshake with client using HandleAuth.
If a nonzero value is received in reserved bits and none of the negotiated extensions defines the meaning of such a nonzero value, server will close the connection (RFC 6455, section 5.2).
func (*Server) ClientRemove ¶
ClientRemove remove client connection from server.
func (*Server) RegisterTextHandler ¶
func (serv *Server) RegisterTextHandler(method, target string, handler RouteHandler) (err error)
RegisterTextHandler register specific function to be called by server when request opcode is text, and method and target matched with Request.
type ServerOptions ¶
type ServerOptions struct { // HandleAuth callback that will be called when receiving // client handshake. HandleAuth HandlerAuthFn // HandleClientAdd callback that will called after client handshake // and, if HandleAuth is defined, after client is authenticated. HandleClientAdd HandlerClientFn // HandleClientRemove callback that will be called before client // connection being removed and closed by server. HandleClientRemove HandlerClientFn // HandleRsvControl callback that will be called when server received // reserved control frame (opcode 0xB-F) from client. // Default handle is nil. HandleRsvControl HandlerFrameFn // HandleText callback that will be called after receiving data // frame(s) text from client. // Default handle parse the payload into Request and pass it to // registered routes. HandleText HandlerPayloadFn // HandleBin callback that will be called after receiving data // frame(s) binary from client. HandleBin HandlerPayloadFn // HandleStatus function that will be called when server receive // request for status as defined in ServerOptions.StatusPath. HandleStatus HandlerStatusFn // Address to listen for WebSocket connection. // Default to ":80". Address string // ConnectPath define the HTTP path where WebSocket connection // handshake will be processed. // Default to "/". ConnectPath string // StatusPath define a HTTP path to check for server status. // Default to ConnectPath +"/status" if its empty. // The StatusPath is handled by HandleStatus callback in the server. StatusPath string // ReadWriteTimeout define the maximum duration the server wait for // receiving/sending packet from/to client before considering the // connection as broken. // Default to 30 seconds. ReadWriteTimeout time.Duration // contains filtered or unexported fields }
ServerOptions contain options to configure the WebSocket server.
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
Package examples provide an example on how to use WebSocket server and client.
|
Package examples provide an example on how to use WebSocket server and client. |
cmd/client
Program client provide an example of chat client that connect to WebSocket server.
|
Program client provide an example of chat client that connect to WebSocket server. |
cmd/server
Program server provide an example of WebSocket server as group chat.
|
Program server provide an example of WebSocket server as group chat. |
internal
|
|
autobahn
Package autobahn provides an helper to parse and print reports from result of Autobahn test suite.
|
Package autobahn provides an helper to parse and print reports from result of Autobahn test suite. |
autobahn/client
Package client provide a program to test WebSocket client implementation with autobahn testsuite.
|
Package client provide a program to test WebSocket client implementation with autobahn testsuite. |
autobahn/server
Package server provide a program for testing WebSocket server implement with autobahn testsuite.
|
Package server provide a program for testing WebSocket server implement with autobahn testsuite. |