Documentation ¶
Overview ¶
Package websocket provide a library for creating WebSocket server or client.
The websocket server is implemented with epoll and kqueue, which means it's only run on Linux, Darwin, or BSD.
Pub-Sub Example ¶
The following example show how create an authenticated WebSocket server that echo the data frame TEXT back to client.
import ( ... "github.com/shuLhan/share/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() }
References ¶
- https://tools.ietf.org/html/rfc6455
- https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers
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) (packet []byte, err error)
- func Send(fd int, packet []byte) (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) error
- func (cl *Client) SendPing(payload []byte) error
- func (cl *Client) SendPong(payload []byte) 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 (1000) indicates a normal closure, meaning that the // purpose for which the connection was established has been // fulfilled. StatusNormal CloseCode = 1000 // StatusGone (1001) 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 (1002) indicates that an endpoint is terminating // the connection due to a protocol error. StatusBadRequest = 1002 // StatusUnsupportedType (1003) 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 (1007) 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 (1008) 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 (1009) 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 (1010) 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 or 1011 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 define an error if client connection is not // connected. ErrConnClosed = fmt.Errorf("websocket: client is not connected") )
Functions ¶
func NewBroadcast ¶ added in v0.10.1
NewBroadcast create a new message for broadcast by server encoded as JSON and wrapped in TEXT frame.
func NewFrame ¶ added in v0.5.0
NewFrame create a single finished frame with specific operation code and optional payload.
func NewFrameBin ¶ added in v0.5.0
NewFrameBin create a single binary data frame with optional payload. Client frame must be masked.
func NewFrameClose ¶ added in v0.5.0
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 ¶ added in v0.5.0
NewFramePing create a masked PING control frame.
func NewFramePong ¶ added in v0.5.0
NewFramePong create a masked PONG control frame to be used by client.
func NewFrameText ¶ added in v0.5.0
NewFrameText create a single text data frame with optional payload. Client frame must be masked.
Types ¶
type Client ¶
type Client struct { sync.Mutex // // Endpoint contains URI 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 // // 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 // 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 // The interval where PING control frame will be send to server. // The minimum and default value is 10 seconds. PingInterval time.Duration // contains filtered or unexported fields }
Client for WebSocket protocol.
Unlike HTTP client or other most commmon TCP oriented client, the WebSocket client is actually asynchronous or passive-active instead of synchronous. At any time client connection is open to server, client can receive a message broadcast 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.Error()) } err := cl.SendText([]byte("Hello from client")) if err != nil { log.Fatal(err.Error()) }
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 closed connection from error returned from Send methods to match with ErrConnClosed.
func (*Client) Quit ¶ added in v0.5.0
func (cl *Client) Quit()
Quit force close the client connection without sending CLOSE control frame. This function MUST be used only when error receiving packet from server (e.g. lost connection) to release the resource.
func (*Client) SendBin ¶ added in v0.5.0
SendBin send data frame as binary to server. If handler is nil, no response will be read from server.
func (*Client) SendPing ¶ added in v0.5.0
SendPing send control PING frame to server, expecting PONG as response.
type ClientHandler ¶ added in v0.5.0
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 ¶ added in v0.5.0
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 ¶ added in v0.5.0
func (cls *ClientManager) All() (conns []int)
All return a copy of all client connections.
func (*ClientManager) Conns ¶ added in v0.5.0
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 ¶ added in v0.5.0
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 ¶ added in v0.5.0
HandlerFrameFn define a server callback type to handle client request with single frame.
type HandlerPayloadFn ¶ added in v0.5.0
HandlerPayloadFn define server callback type to handle data frame from client.
type HandlerStatusFn ¶ added in v0.19.0
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 Host []byte Key []byte Extensions []byte Protocol []byte Header http.Header // contains filtered or unexported fields }
Handshake contains the websocket HTTP handshake request.
type Opcode ¶ added in v0.5.0
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 { // // 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"` // 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:"-"` // Params are parameters as key-value in Target path that has been // parsed. Params targetParam `json:"-"` // Query is Target query. Query url.Values `json:"-"` // Conn is the client connection, where the request come from. Conn int }
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 { ID uint64 `json:"id"` Code int32 `json:"code"` Message string `json:"message"` Body string `json:"body"` }
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 will create new web-socket server that listen on specific port number.
func (*Server) AllowReservedBits ¶ added in v0.5.0
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 ¶ added in v0.5.0
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 ¶ added in v0.19.0
type ServerOptions struct { // 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 // 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 }
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
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. |