ws
packagemoduleThis package is not in the latest version of its module.
Details
Validgo.mod file
The Go module system was introduced in Go 1.11 and is the official dependency management solution for Go.
Redistributable license
Redistributable licenses place minimal restrictions on how software can be used, modified, and redistributed.
Tagged version
Modules with tagged versions give importers more predictable builds.
Stable version
When a project reaches major version v1 it is considered stable.
- Learn more about best practices
Repository
Links
README¶
ws
RFC6455 WebSocket implementation in Go.
Features
- Zero-copy upgrade
- No intermediate allocations during I/O
- Low-level API which allows to build your own logic of packet handling andbuffers reuse
- High-level wrappers and helpers around API in
wsutil
package, which allowto start fast without digging the protocol internals
Documentation
Why
Existing WebSocket implementations do not allow users to reuse I/O buffersbetween connections in clear way. This library aims to export efficientlow-level interface for working with the protocol without forcing only one wayit could be used.
By the way, if you want get the higher-level tools, you can usewsutil
package.
Status
Library is tagged asv1*
so its API must not be broken during someimprovements or refactoring.
This implementation of RFC6455 passesAutobahn TestSuite and currently hasabout 78% coverage.
Examples
Example applications usingws
are developed in separate repositoryws-examples.
Usage
The higher-level example of WebSocket echo server:
package mainimport ("net/http""github.com/gobwas/ws""github.com/gobwas/ws/wsutil")func main() {http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {conn, _, _, err := ws.UpgradeHTTP(r, w)if err != nil {// handle error}go func() {defer conn.Close()for {msg, op, err := wsutil.ReadClientData(conn)if err != nil {// handle error}err = wsutil.WriteServerMessage(conn, op, msg)if err != nil {// handle error}}}()}))}
Lower-level, but still high-level example:
import ("net/http""io""github.com/gobwas/ws""github.com/gobwas/ws/wsutil")func main() {http.ListenAndServe(":8080", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {conn, _, _, err := ws.UpgradeHTTP(r, w)if err != nil {// handle error}go func() {defer conn.Close()var (state = ws.StateServerSidereader = wsutil.NewReader(conn, state)writer = wsutil.NewWriter(conn, state, ws.OpText))for {header, err := reader.NextFrame()if err != nil {// handle error}// Reset writer to write frame with right operation code.writer.Reset(conn, state, header.OpCode)if _, err = io.Copy(writer, reader); err != nil {// handle error}if err = writer.Flush(); err != nil {// handle error}}}()}))}
We can apply the same pattern to read and write structured responses through a JSON encoder and decoder.:
...var (r = wsutil.NewReader(conn, ws.StateServerSide)w = wsutil.NewWriter(conn, ws.StateServerSide, ws.OpText)decoder = json.NewDecoder(r)encoder = json.NewEncoder(w))for {hdr, err = r.NextFrame()if err != nil {return err}if hdr.OpCode == ws.OpClose {return io.EOF}var req Requestif err := decoder.Decode(&req); err != nil {return err}var resp Responseif err := encoder.Encode(&resp); err != nil {return err}if err = w.Flush(); err != nil {return err}}...
The lower-level example withoutwsutil
:
package mainimport ("net""io""github.com/gobwas/ws")func main() {ln, err := net.Listen("tcp", "localhost:8080")if err != nil {log.Fatal(err)}for {conn, err := ln.Accept()if err != nil {// handle error}_, err = ws.Upgrade(conn)if err != nil {// handle error}go func() {defer conn.Close()for {header, err := ws.ReadHeader(conn)if err != nil {// handle error}payload := make([]byte, header.Length)_, err = io.ReadFull(conn, payload)if err != nil {// handle error}if header.Masked {ws.Cipher(payload, header.Mask, 0)}// Reset the Masked flag, server frames must not be masked as// RFC6455 says.header.Masked = falseif err := ws.WriteHeader(conn, header); err != nil {// handle error}if _, err := conn.Write(payload); err != nil {// handle error}if header.OpCode == ws.OpClose {return}}}()}}
Zero-copy upgrade
Zero-copy upgrade helps to avoid unnecessary allocations and copying whilehandling HTTP Upgrade request.
Processing of all non-websocket headers is made in place with use of registereduser callbacks whose arguments are only valid until callback returns.
The simple example looks like this:
package mainimport ("net""log""github.com/gobwas/ws")func main() {ln, err := net.Listen("tcp", "localhost:8080")if err != nil {log.Fatal(err)}u := ws.Upgrader{OnHeader: func(key, value []byte) (err error) {log.Printf("non-websocket header: %q=%q", key, value)return},}for {conn, err := ln.Accept()if err != nil {// handle error}_, err = u.Upgrade(conn)if err != nil {// handle error}}}
Usage ofws.Upgrader
here brings ability to control incoming connections ontcp level and simply not to accept them by some logic.
Zero-copy upgrade is for high-load services which have to control manyresources such as connections buffers.
The real life example could be like this:
package mainimport ("fmt""io""log""net""net/http""runtime""github.com/gobwas/httphead""github.com/gobwas/ws")func main() {ln, err := net.Listen("tcp", "localhost:8080")if err != nil {// handle error}// Prepare handshake header writer from http.Header mapping.header := ws.HandshakeHeaderHTTP(http.Header{"X-Go-Version": []string{runtime.Version()},})u := ws.Upgrader{OnHost: func(host []byte) error {if string(host) == "github.com" {return nil}return ws.RejectConnectionError(ws.RejectionStatus(403),ws.RejectionHeader(ws.HandshakeHeaderString("X-Want-Host: github.com\r\n",)),)},OnHeader: func(key, value []byte) error {if string(key) != "Cookie" {return nil}ok := httphead.ScanCookie(value, func(key, value []byte) bool {// Check session here or do some other stuff with cookies.// Maybe copy some values for future use.return true})if ok {return nil}return ws.RejectConnectionError(ws.RejectionReason("bad cookie"),ws.RejectionStatus(400),)},OnBeforeUpgrade: func() (ws.HandshakeHeader, error) {return header, nil},}for {conn, err := ln.Accept()if err != nil {log.Fatal(err)}_, err = u.Upgrade(conn)if err != nil {log.Printf("upgrade error: %s", err)}}}
Compression
There is aws/wsflate
package to supportPermessage-Deflate CompressionExtension.
It provides minimalistic I/O wrappers to be used in conjunction with anydeflate implementation (for example, the standard library'scompress/flate).
It is also compatible withwsutil
's reader and writer by providingwsflate.MessageState
type, which implementswsutil.SendExtension
andwsutil.RecvExtension
interfaces.
package mainimport ("bytes""log""net""github.com/gobwas/ws""github.com/gobwas/ws/wsflate")func main() {ln, err := net.Listen("tcp", "localhost:8080")if err != nil {// handle error}e := wsflate.Extension{// We are using default parameters here since we use// wsflate.{Compress,Decompress}Frame helpers below in the code.// This assumes that we use standard compress/flate package as flate// implementation.Parameters: wsflate.DefaultParameters,}u := ws.Upgrader{Negotiate: e.Negotiate,}for {conn, err := ln.Accept()if err != nil {log.Fatal(err)}// Reset extension after previous upgrades.e.Reset()_, err = u.Upgrade(conn)if err != nil {log.Printf("upgrade error: %s", err)continue}if _, ok := e.Accepted(); !ok {log.Printf("didn't negotiate compression for %s", conn.RemoteAddr())conn.Close()continue}go func() {defer conn.Close()for {frame, err := ws.ReadFrame(conn)if err != nil {// Handle error.return}frame = ws.UnmaskFrameInPlace(frame)if wsflate.IsCompressed(frame.Header) {// Note that even after successful negotiation of// compression extension, both sides are able to send// non-compressed messages.frame, err = wsflate.DecompressFrame(frame)if err != nil {// Handle error.return}}// Do something with frame...ack := ws.NewTextFrame([]byte("this is an acknowledgement"))// Compress response unconditionally.ack, err = wsflate.CompressFrame(ack)if err != nil {// Handle error.return}if err = ws.WriteFrame(conn, ack); err != nil {// Handle error.return}}}()}}
You can use compression withwsutil
package this way:
// Upgrade somehow and negotiate compression to get the conn...// Initialize flate reader. We are using nil as a source io.Reader because// we will Reset() it in the message i/o loop below.fr := wsflate.NewReader(nil, func(r io.Reader) wsflate.Decompressor {return flate.NewReader(r)})// Initialize flate writer. We are using nil as a destination io.Writer// because we will Reset() it in the message i/o loop below.fw := wsflate.NewWriter(nil, func(w io.Writer) wsflate.Compressor {f, _ := flate.NewWriter(w, 9)return f})// Declare compression message state variable.//// It has two goals:// - Allow users to check whether received message is compressed or not.// - Help wsutil.Reader and wsutil.Writer to set/unset appropriate// WebSocket header bits while writing next frame to the wire (it// implements wsutil.RecvExtension and wsutil.SendExtension).var msg wsflate.MessageState// Initialize WebSocket reader as previously. // Please note the use of Reader.Extensions field as well as// of ws.StateExtended flag.rd := &wsutil.Reader{Source: conn,State: ws.StateServerSide | ws.StateExtended,Extensions: []wsutil.RecvExtension{&msg, },}// Initialize WebSocket writer with ws.StateExtended flag as well.wr := wsutil.NewWriter(conn, ws.StateServerSide|ws.StateExtended, 0)// Use the message state as wsutil.SendExtension.wr.SetExtensions(&msg)for {h, err := rd.NextFrame()if err != nil {// handle error.}if h.OpCode.IsControl() {// handle control frame.}if !msg.IsCompressed() {// handle uncompressed frame (skipped for the sake of example// simplicity).}// Reset the writer to echo same op code.wr.Reset(h.OpCode)// Reset both flate reader and writer to start the new round of i/o.fr.Reset(rd)fw.Reset(wr)// Copy whole message from reader to writer decompressing it and// compressing again.if _, err := io.Copy(fw, fr); err != nil {// handle error.}// Flush any remaining buffers from flate writer to WebSocket writer.if err := fw.Close(); err != nil {// handle error.}// Flush the whole WebSocket message to the wire.if err := wr.Flush(); err != nil {// handle error.}}
Documentation¶
Overview¶
Package ws implements a client and server for the WebSocket protocol asspecified inRFC 6455.
The main purpose of this package is to provide simple low-level API forefficient work with protocol.
Overview.
Upgrade to WebSocket (or WebSocket handshake) can be done in two ways.
The first way is to use `net/http` server:
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {conn, _, _, err := ws.UpgradeHTTP(r, w)})
The second and much more efficient way is so-called "zero-copy upgrade". Itavoids redundant allocations and copying of not used headers or other requestdata. User decides by himself which data should be copied.
ln, err := net.Listen("tcp", ":8080")if err != nil {// handle error}conn, err := ln.Accept()if err != nil {// handle error}handshake, err := ws.Upgrade(conn)if err != nil {// handle error}
For customization details see `ws.Upgrader` documentation.
After WebSocket handshake you can work with connection in multiple ways.That is, `ws` does not force the only one way of how to work with WebSocket:
header, err := ws.ReadHeader(conn)if err != nil {// handle err}buf := make([]byte, header.Length)_, err := io.ReadFull(conn, buf)if err != nil {// handle err}resp := ws.NewBinaryFrame([]byte("hello, world!"))if err := ws.WriteFrame(conn, frame); err != nil { // handle err}
As you can see, it stream friendly:
const N = 42ws.WriteHeader(ws.Header{Fin: true,Length: N,OpCode: ws.OpBinary,})io.CopyN(conn, rand.Reader, N)
Or:
header, err := ws.ReadHeader(conn)if err != nil {// handle err}io.CopyN(ioutil.Discard, conn, header.Length)
For more info see the documentation.
Index¶
- Constants
- Variables
- func CheckCloseFrameData(code StatusCode, reason string) error
- func CheckHeader(h Header, s State) error
- func Cipher(payload []byte, mask [4]byte, offset int)
- func CompileFrame(f Frame) (bts []byte, err error)
- func HeaderSize(h Header) (n int)
- func MustCompileFrame(f Frame) []byte
- func MustWriteFrame(w io.Writer, f Frame)
- func NewCloseFrameBody(code StatusCode, reason string) []byte
- func NewMask() (ret [4]byte)
- func PutCloseFrameBody(p []byte, code StatusCode, reason string)
- func PutReader(br *bufio.Reader)
- func RejectConnectionError(options ...RejectOption) error
- func Rsv(r1, r2, r3 bool) (rsv byte)
- func RsvBits(rsv byte) (r1, r2, r3 bool)
- func SelectEqual(v string) func(string) bool
- func SelectFromSlice(accept []string) func(string) bool
- func WriteFrame(w io.Writer, f Frame) error
- func WriteHeader(w io.Writer, h Header) error
- type ConnectionRejectedError
- type Dialer
- type Frame
- func MaskFrame(f Frame) Frame
- func MaskFrameInPlace(f Frame) Frame
- func MaskFrameInPlaceWith(f Frame, m [4]byte) Frame
- func MaskFrameWith(f Frame, mask [4]byte) Frame
- func MustReadFrame(r io.Reader) Frame
- func NewBinaryFrame(p []byte) Frame
- func NewCloseFrame(p []byte) Frame
- func NewFrame(op OpCode, fin bool, p []byte) Frame
- func NewPingFrame(p []byte) Frame
- func NewPongFrame(p []byte) Frame
- func NewTextFrame(p []byte) Frame
- func ReadFrame(r io.Reader) (f Frame, err error)
- func UnmaskFrame(f Frame) Frame
- func UnmaskFrameInPlace(f Frame) Frame
- type HTTPUpgrader
- type Handshake
- type HandshakeHeader
- type HandshakeHeaderBytes
- type HandshakeHeaderFunc
- type HandshakeHeaderHTTP
- type HandshakeHeaderString
- type Header
- type OpCode
- type ProtocolError
- type RejectOption
- type State
- type StatusCode
- func (s StatusCode) Empty() bool
- func (s StatusCode) In(r StatusCodeRange) bool
- func (s StatusCode) IsApplicationSpec() bool
- func (s StatusCode) IsNotUsed() bool
- func (s StatusCode) IsPrivateSpec() bool
- func (s StatusCode) IsProtocolDefined() bool
- func (s StatusCode) IsProtocolReserved() bool
- func (s StatusCode) IsProtocolSpec() bool
- type StatusCodeRange
- type StatusError
- type Upgrader
Constants¶
const (DefaultClientReadBufferSize = 4096DefaultClientWriteBufferSize = 4096)
Constants used by Dialer.
const (DefaultServerReadBufferSize = 4096DefaultServerWriteBufferSize = 512)
Constants used by ConnUpgrader.
const (MaxHeaderSize = 14MinHeaderSize = 2)
Header size length bounds in bytes.
const (// All control frames MUST have a payload length of 125 bytes or less and MUST NOT be fragmented.MaxControlFramePayloadSize = 125)
Constants defined by specification.
Variables¶
var (ErrProtocolOpCodeReserved =ProtocolError("use of reserved op code")ErrProtocolControlPayloadOverflow =ProtocolError("control frame payload limit exceeded")ErrProtocolControlNotFinal =ProtocolError("control frame is not final")ErrProtocolNonZeroRsv =ProtocolError("non-zero rsv bits with no extension negotiated")ErrProtocolMaskRequired =ProtocolError("frames from client to server must be masked")ErrProtocolMaskUnexpected =ProtocolError("frames from server to client must be not masked")ErrProtocolContinuationExpected =ProtocolError("unexpected non-continuation data frame")ErrProtocolContinuationUnexpected =ProtocolError("unexpected continuation data frame")ErrProtocolStatusCodeNotInUse =ProtocolError("status code is not in use")ErrProtocolStatusCodeApplicationLevel =ProtocolError("status code is only application level")ErrProtocolStatusCodeNoMeaning =ProtocolError("status code has no meaning yet")ErrProtocolStatusCodeUnknown =ProtocolError("status code is not defined in spec")ErrProtocolInvalidUTF8 =ProtocolError("invalid utf8 sequence in close reason"))
Errors used by the protocol checkers.
var (ErrHandshakeBadStatus =fmt.Errorf("unexpected http status")ErrHandshakeBadSubProtocol =fmt.Errorf("unexpected protocol in %q header", headerSecProtocol)ErrHandshakeBadExtensions =fmt.Errorf("unexpected extensions in %q header", headerSecProtocol))
Errors used by the websocket client.
var (StatusRangeNotInUse =StatusCodeRange{0, 999}StatusRangeProtocol =StatusCodeRange{1000, 2999}StatusRangeApplication =StatusCodeRange{3000, 3999}StatusRangePrivate =StatusCodeRange{4000, 4999})
Status code ranges defined by specification.Seehttps://tools.ietf.org/html/rfc6455#section-7.4.2
var (CompiledPing =MustCompileFrame(NewPingFrame(nil))CompiledPong =MustCompileFrame(NewPongFrame(nil))CompiledClose =MustCompileFrame(NewCloseFrame(nil))CompiledCloseNormalClosure =MustCompileFrame(closeFrameNormalClosure)CompiledCloseGoingAway =MustCompileFrame(closeFrameGoingAway)CompiledCloseProtocolError =MustCompileFrame(closeFrameProtocolError)CompiledCloseUnsupportedData =MustCompileFrame(closeFrameUnsupportedData)CompiledCloseNoMeaningYet =MustCompileFrame(closeFrameNoMeaningYet)CompiledCloseInvalidFramePayloadData =MustCompileFrame(closeFrameInvalidFramePayloadData)CompiledClosePolicyViolation =MustCompileFrame(closeFramePolicyViolation)CompiledCloseMessageTooBig =MustCompileFrame(closeFrameMessageTooBig)CompiledCloseMandatoryExt =MustCompileFrame(closeFrameMandatoryExt)CompiledCloseInternalServerError =MustCompileFrame(closeFrameInternalServerError)CompiledCloseTLSHandshake =MustCompileFrame(closeFrameTLSHandshake))
Compiled control frames for common use cases.For construct-serialize optimizations.
var (ErrHeaderLengthMSB =fmt.Errorf("header error: the most significant bit must be 0")ErrHeaderLengthUnexpected =fmt.Errorf("header error: unexpected payload length bits"))
Errors used by frame reader.
var (ErrHandshakeBadProtocol =RejectConnectionError(RejectionStatus(http.StatusHTTPVersionNotSupported),RejectionReason("handshake error: bad HTTP protocol version"),)ErrHandshakeBadMethod =RejectConnectionError(RejectionStatus(http.StatusMethodNotAllowed),RejectionReason("handshake error: bad HTTP request method"),)ErrHandshakeBadHost =RejectConnectionError(RejectionStatus(http.StatusBadRequest),RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerHost)),)ErrHandshakeBadUpgrade =RejectConnectionError(RejectionStatus(http.StatusBadRequest),RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerUpgrade)),)ErrHandshakeBadConnection =RejectConnectionError(RejectionStatus(http.StatusBadRequest),RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerConnection)),)ErrHandshakeBadSecAccept =RejectConnectionError(RejectionStatus(http.StatusBadRequest),RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecAccept)),)ErrHandshakeBadSecKey =RejectConnectionError(RejectionStatus(http.StatusBadRequest),RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecKey)),)ErrHandshakeBadSecVersion =RejectConnectionError(RejectionStatus(http.StatusBadRequest),RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecVersion)),))
Errors used by both client and server when preparing WebSocket handshake.
var ErrHandshakeUpgradeRequired =RejectConnectionError(RejectionStatus(http.StatusUpgradeRequired),RejectionHeader(HandshakeHeaderString(headerSecVersion+": 13\r\n")),RejectionReason(fmt.Sprintf("handshake error: bad %q header", headerSecVersion)),)
ErrHandshakeUpgradeRequired is returned by Upgrader to indicate thatconnection is rejected because given WebSocket version is malformed.
According to RFC6455:If this version does not match a version understood by the server, theserver MUST abort the WebSocket handshake described in this section andinstead send an appropriate HTTP error code (such as 426 Upgrade Required)and a |Sec-WebSocket-Version| header field indicating the version(s) theserver is capable of understanding.
var ErrMalformedRequest =RejectConnectionError(RejectionStatus(http.StatusBadRequest),RejectionReason("malformed HTTP request"),)
ErrMalformedRequest is returned when HTTP request can not be parsed.
var ErrMalformedResponse =fmt.Errorf("malformed HTTP response")
ErrMalformedResponse is returned by Dialer to indicate that server responsecan not be parsed.
var ErrNotHijacker =RejectConnectionError(RejectionStatus(http.StatusInternalServerError),RejectionReason("given http.ResponseWriter is not a http.Hijacker"),)
ErrNotHijacker is an error returned when http.ResponseWriter does notimplement http.Hijacker interface.
Functions¶
funcCheckCloseFrameData¶
func CheckCloseFrameData(codeStatusCode, reasonstring)error
CheckCloseFrameData checks received close informationto be valid RFC6455 compatible close info.
Note that code.Empty() or code.IsAppLevel() will raise error.
If endpoint sends close frame without status code (with frame.Length = 0),application should not check its payload.
funcCheckHeader¶
CheckHeader checks h to contain valid header data for given state s.
Note that zero state (0) means that state is clean,neither server or client side, nor fragmented, nor extended.
funcCipher¶
Cipher applies XOR cipher to the payload using mask.Offset is used to cipher chunked data (e.g. in io.Reader implementations).
To convert masked data into unmasked data, or vice versa, the followingalgorithm is applied. The same algorithm applies regardless of thedirection of the translation, e.g., the same steps are applied tomask the data as to unmask the data.
funcCompileFrame¶
CompileFrame returns byte representation of given frame.In terms of memory consumption it is useful to precompile static frameswhich are often used.
funcHeaderSize¶
HeaderSize returns number of bytes that are needed to encode given header.It returns -1 if header is malformed.
funcMustCompileFrame¶
MustCompileFrame is like CompileFrame but panics if frame can not beencoded.
funcMustWriteFrame¶added inv1.0.0
MustWriteFrame is like WriteFrame but panics if frame can not be read.
funcNewCloseFrameBody¶added inv1.0.0
func NewCloseFrameBody(codeStatusCode, reasonstring) []byte
NewCloseFrameBody encodes a closure code and a reason into a binaryrepresentation.
It returns slice which is at most MaxControlFramePayloadSize bytes length.If the reason is too big it will be cropped to fit the limit defined by thespec.
funcPutCloseFrameBody¶added inv1.0.0
func PutCloseFrameBody(p []byte, codeStatusCode, reasonstring)
PutCloseFrameBody encodes code and reason into buf.
It will panic if the buffer is too small to accommodate a code or a reason.
PutCloseFrameBody does not check buffer to be RFC compliant, but note thatby RFC it must be at most MaxControlFramePayloadSize.
funcPutReader¶
PutReader returns bufio.Reader instance to the inner reuse pool.It is useful in rare cases, when Dialer.Dial() returns non-nil buffer whichcontains unprocessed buffered data, that was sent by the server quicklyright after handshake.
funcRejectConnectionError¶added inv1.0.0
func RejectConnectionError(options ...RejectOption)error
RejectConnectionError constructs an error that could be used to control theway handshake is rejected by Upgrader.
funcSelectEqual¶
SelectEqual creates accept function that could be used as Protocol/Extensionselect during upgrade.
funcSelectFromSlice¶
SelectFromSlice creates accept function that could be used as Protocol/Extensionselect during upgrade.
funcWriteFrame¶
WriteFrame writes frame binary representation into w.
Types¶
typeConnectionRejectedError¶added inv1.1.0
type ConnectionRejectedError struct {// contains filtered or unexported fields}
ConnectionRejectedError represents a rejection of connection duringWebSocket handshake error.
It can be returned by Upgrader's On* hooks to indicate that WebSockethandshake should be rejected.
func (*ConnectionRejectedError)Error¶added inv1.1.0
func (r *ConnectionRejectedError) Error()string
Error implements error interface.
func (*ConnectionRejectedError)StatusCode¶added inv1.1.0
func (r *ConnectionRejectedError) StatusCode()int
typeDialer¶
type Dialer struct {// ReadBufferSize and WriteBufferSize is an I/O buffer sizes.// They used to read and write http data while upgrading to WebSocket.// Allocated buffers are pooled with sync.Pool to avoid extra allocations.//// If a size is zero then default value is used.ReadBufferSize, WriteBufferSizeint// Timeout is the maximum amount of time a Dial() will wait for a connect// and an handshake to complete.//// The default is no timeout.Timeouttime.Duration// Protocols is the list of subprotocols that the client wants to speak,// ordered by preference.//// Seehttps://tools.ietf.org/html/rfc6455#section-4.1Protocols []string// Extensions is the list of extensions that client wants to speak.//// Note that if server decides to use some of this extensions, Dial() will// return Handshake struct containing a slice of items, which are the// shallow copies of the items from this list. That is, internals of// Extensions items are shared during Dial().//// Seehttps://tools.ietf.org/html/rfc6455#section-4.1// Seehttps://tools.ietf.org/html/rfc6455#section-9.1Extensions []httphead.Option// Header is an optional HandshakeHeader instance that could be used to// write additional headers to the handshake request.//// It used instead of any key-value mappings to avoid allocations in user// land.HeaderHandshakeHeader// Host is an optional string that could be used to specify the host during// HTTP upgrade request by setting 'Host' header.//// Default value is an empty string, which results in setting 'Host' header// equal to the URL hostname given to Dialer.Dial().Hoststring// OnStatusError is the callback that will be called after receiving non// "101 Continue" HTTP response status. It receives an io.Reader object// representing server response bytes. That is, it gives ability to parse// HTTP response somehow (probably with http.ReadResponse call) and make a// decision of further logic.//// The arguments are only valid until the callback returns.OnStatusError func(statusint, reason []byte, respio.Reader)// OnHeader is the callback that will be called after successful parsing of// header, that is not used during WebSocket handshake procedure. That is,// it will be called with non-websocket headers, which could be relevant// for application-level logic.//// The arguments are only valid until the callback returns.//// Returned value could be used to prevent processing response.OnHeader func(key, value []byte) (errerror)// NetDial is the function that is used to get plain tcp connection.// If it is not nil, then it is used instead of net.Dialer.NetDial func(ctxcontext.Context, network, addrstring) (net.Conn,error)// TLSClient is the callback that will be called after successful dial with// received connection and its remote host name. If it is nil, then the// default tls.Client() will be used.// If it is not nil, then TLSConfig field is ignored.TLSClient func(connnet.Conn, hostnamestring)net.Conn// TLSConfig is passed to tls.Client() to start TLS over established// connection. If TLSClient is not nil, then it is ignored. If TLSConfig is// non-nil and its ServerName is empty, then for every Dial() it will be// cloned and appropriate ServerName will be set.TLSConfig *tls.Config// WrapConn is the optional callback that will be called when connection is// ready for an i/o. That is, it will be called after successful dial and// TLS initialization (for "wss" schemes). It may be helpful for different// user land purposes such as end to end encryption.//// Note that for debugging purposes of an http handshake (e.g. sent request// and received response), there is an wsutil.DebugDialer struct.WrapConn func(connnet.Conn)net.Conn}
Dialer contains options for establishing websocket connection to an url.
var DefaultDialerDialer
DefaultDialer is dialer that holds no options and is used by Dial function.
func (Dialer)Dial¶
func (dDialer) Dial(ctxcontext.Context, urlstrstring) (connnet.Conn, br *bufio.Reader, hsHandshake, errerror)
Dial connects to the url host and upgrades connection to WebSocket.
If server has sent frames right after successful handshake then returnedbuffer will be non-nil. In other cases buffer is always nil. For bettermemory efficiency received non-nil bufio.Reader should be returned to theinner pool with PutReader() function after use.
Note that Dialer does not implement IDNA (RFC5895) logic as net/http does.If you want to dial non-ascii host name, take care of its name serializationavoiding bad request issues. For more info see net/http Request.Write()implementation, especially cleanHost() function.
func (Dialer)Upgrade¶
Upgrade writes an upgrade request to the given io.ReadWriter conn at givenurl u and reads a response from it.
It is a caller responsibility to manage I/O deadlines on conn.
It returns handshake info and some bytes which could be written by the peerright after response and be caught by us during buffered read.
typeFrame¶
Frame represents websocket frame.Seehttps://tools.ietf.org/html/rfc6455#section-5.2
funcMaskFrame¶
MaskFrame masks frame and returns frame with masked payload and Mask header's field set.Note that it copies f payload to prevent collisions.For less allocations you could use MaskFrameInPlace or construct frame manually.
funcMaskFrameInPlace¶
MaskFrameInPlace masks frame and returns frame with masked payload and Maskheader's field set.Note that it applies xor cipher to f.Payload without copying, that is, itmodifies f.Payload inplace.
funcMaskFrameInPlaceWith¶
MaskFrameInPlaceWith masks frame with given mask and returns framewith masked payload and Mask header's field set.Note that it applies xor cipher to f.Payload without copying, that is, itmodifies f.Payload inplace.
funcMaskFrameWith¶
MaskFrameWith masks frame with given mask and returns framewith masked payload and Mask header's field set.Note that it copies f payload to prevent collisions.For less allocations you could use MaskFrameInPlaceWith or construct frame manually.
funcMustReadFrame¶added inv1.0.0
MustReadFrame is like ReadFrame but panics if frame can not be read.
funcNewBinaryFrame¶
NewBinaryFrame creates binary frame with p as payload.Note that p is not copied.
funcNewCloseFrame¶
NewCloseFrame creates close frame with given close body.Note that p is not copied.Note that p must have length of MaxControlFramePayloadSize bytes or less dueto RFC.
funcNewFrame¶
NewFrame creates frame with given operation code,flag of completeness and payload bytes.
funcNewPingFrame¶
NewPingFrame creates ping frame with p as payload.Note that p is not copied.Note that p must have length of MaxControlFramePayloadSize bytes or less dueto RFC.
funcNewPongFrame¶
NewPongFrame creates pong frame with p as payload.Note that p is not copied.Note that p must have length of MaxControlFramePayloadSize bytes or less dueto RFC.
funcNewTextFrame¶
NewTextFrame creates text frame with p as payload.Note that p is not copied.
funcReadFrame¶
ReadFrame reads a frame from r.It is not designed for high optimized use case cause it makes allocationfor frame.Header.Length size inside to read frame payload into.
Note that ReadFrame does not unmask payload.
funcUnmaskFrame¶added inv1.1.0
UnmaskFrame unmasks frame and returns frame with unmasked payload and Maskheader's field cleared.Note that it copies f payload.
funcUnmaskFrameInPlace¶added inv1.1.0
UnmaskFrameInPlace unmasks frame and returns frame with unmasked payload andMask header's field cleared.Note that it applies xor cipher to f.Payload without copying, that is, itmodifies f.Payload inplace.
typeHTTPUpgrader¶
type HTTPUpgrader struct {// Timeout is the maximum amount of time an Upgrade() will spent while// writing handshake response.//// The default is no timeout.Timeouttime.Duration// Header is an optional http.Header mapping that could be used to// write additional headers to the handshake response.//// Note that if present, it will be written in any result of handshake.Headerhttp.Header// Protocol is the select function that is used to select subprotocol from// list requested by client. If this field is set, then the first matched// protocol is sent to a client as negotiated.Protocol func(string)bool// Extension is the select function that is used to select extensions from// list requested by client. If this field is set, then the all matched// extensions are sent to a client as negotiated.//// Deprecated: use Negotiate instead.Extension func(httphead.Option)bool// Negotiate is the callback that is used to negotiate extensions from// the client's offer. If this field is set, then the returned non-zero// extensions are sent to the client as accepted extensions in the// response.//// The argument is only valid until the Negotiate callback returns.//// If returned error is non-nil then connection is rejected and response is// sent with appropriate HTTP error code and body set to error message.//// RejectConnectionError could be used to get more control on response.Negotiate func(httphead.Option) (httphead.Option,error)}
HTTPUpgrader contains options for upgrading connection to websocket fromnet/http Handler arguments.
var DefaultHTTPUpgraderHTTPUpgrader
DefaultHTTPUpgrader is an HTTPUpgrader that holds no options and is used byUpgradeHTTP function.
func (HTTPUpgrader)Upgrade¶
func (uHTTPUpgrader) Upgrade(r *http.Request, whttp.ResponseWriter) (connnet.Conn, rw *bufio.ReadWriter, hsHandshake, errerror)
Upgrade upgrades http connection to the websocket connection.
It hijacks net.Conn from w and returns received net.Conn andbufio.ReadWriter. On successful handshake it returns Handshake structdescribing handshake info.
typeHandshake¶
type Handshake struct {// Protocol is the subprotocol selected during handshake.Protocolstring// Extensions is the list of negotiated extensions.Extensions []httphead.Option}
Handshake represents handshake result.
funcUpgradeHTTP¶
func UpgradeHTTP(r *http.Request, whttp.ResponseWriter) (net.Conn, *bufio.ReadWriter,Handshake,error)
UpgradeHTTP is like HTTPUpgrader{}.Upgrade().
typeHandshakeHeader¶added inv1.0.0
HandshakeHeader is the interface that writes both upgrade request orresponse headers into a given io.Writer.
typeHandshakeHeaderBytes¶added inv1.0.0
type HandshakeHeaderBytes []byte
HandshakeHeaderBytes is an adapter to allow the use of headers representedby ordinary slice of bytes as HandshakeHeader.
typeHandshakeHeaderFunc¶added inv1.0.0
HandshakeHeaderFunc is an adapter to allow the use of headers represented byordinary function as HandshakeHeader.
typeHandshakeHeaderHTTP¶added inv1.0.0
HandshakeHeaderHTTP is an adapter to allow the use of http.Header asHandshakeHeader.
typeHandshakeHeaderString¶added inv1.0.0
type HandshakeHeaderStringstring
HandshakeHeaderString is an adapter to allow the use of headers representedby ordinary string as HandshakeHeader.
typeHeader¶
Header represents websocket frame header.Seehttps://tools.ietf.org/html/rfc6455#section-5.2
funcReadHeader¶
ReadHeader reads a frame header from r.
typeOpCode¶
type OpCodebyte
OpCode represents operation code.
const (OpContinuationOpCode = 0x0OpTextOpCode = 0x1OpBinaryOpCode = 0x2OpCloseOpCode = 0x8OpPingOpCode = 0x9OpPongOpCode = 0xa)
Operation codes defined by specification.Seehttps://tools.ietf.org/html/rfc6455#section-5.2
func (OpCode)IsControl¶
IsControl checks whether the c is control operation code.Seehttps://tools.ietf.org/html/rfc6455#section-5.5
func (OpCode)IsData¶
IsData checks whether the c is data operation code.Seehttps://tools.ietf.org/html/rfc6455#section-5.6
func (OpCode)IsReserved¶
IsReserved checks whether the c is reserved operation code.Seehttps://tools.ietf.org/html/rfc6455#section-5.2
typeProtocolError¶
type ProtocolErrorstring
ProtocolError describes error during checking/parsing websocket frames orheaders.
typeRejectOption¶added inv1.0.0
type RejectOption func(*ConnectionRejectedError)
RejectOption represents an option used to control the way connection isrejected.
funcRejectionHeader¶added inv1.0.0
func RejectionHeader(hHandshakeHeader)RejectOption
RejectionHeader returns an option that makes connection to be rejected withgiven HTTP headers.
funcRejectionReason¶added inv1.0.0
func RejectionReason(reasonstring)RejectOption
RejectionReason returns an option that makes connection to be rejected withgiven reason.
funcRejectionStatus¶added inv1.0.0
func RejectionStatus(codeint)RejectOption
RejectionStatus returns an option that makes connection to be rejected withgiven HTTP status code.
typeState¶
type Stateuint8
State represents state of websocket endpoint.It used by some functions to be more strict when checking compatibility with RFC6455.
const (// StateServerSide means that endpoint (caller) is a server.StateServerSideState = 0x1 <<iota// StateClientSide means that endpoint (caller) is a client.StateClientSide// StateExtended means that extension was negotiated during handshake.StateExtended// StateFragmented means that endpoint (caller) has received fragmented// frame and waits for continuation parts.StateFragmented)
func (State)ClientSide¶added inv1.0.0
ClientSide reports whether state represents client side.
func (State)Fragmented¶added inv1.0.0
Fragmented reports whether state is fragmented.
func (State)ServerSide¶added inv1.0.0
ServerSide reports whether states represents server side.
typeStatusCode¶
type StatusCodeuint16
StatusCode represents the encoded reason for closure of websocket connection.
There are few helper methods on StatusCode that helps to define a range inwhich given code is lay in. accordingly to ranges defined in specification.
Seehttps://tools.ietf.org/html/rfc6455#section-7.4
const (StatusNormalClosureStatusCode = 1000StatusGoingAwayStatusCode = 1001StatusProtocolErrorStatusCode = 1002StatusUnsupportedDataStatusCode = 1003StatusNoMeaningYetStatusCode = 1004StatusInvalidFramePayloadDataStatusCode = 1007StatusPolicyViolationStatusCode = 1008StatusMessageTooBigStatusCode = 1009StatusMandatoryExtStatusCode = 1010StatusInternalServerErrorStatusCode = 1011StatusTLSHandshakeStatusCode = 1015// StatusAbnormalClosure is a special code designated for use in// applications.StatusAbnormalClosureStatusCode = 1006// StatusNoStatusRcvd is a special code designated for use in applications.StatusNoStatusRcvdStatusCode = 1005)
Status codes defined by specification.Seehttps://tools.ietf.org/html/rfc6455#section-7.4.1
funcParseCloseFrameData¶
func ParseCloseFrameData(payload []byte) (codeStatusCode, reasonstring)
ParseCloseFrameData parses close frame status code and closure reason if any provided.If there is no status code in the payloadthe empty status code is returned (code.Empty()) with empty string as a reason.
funcParseCloseFrameDataUnsafe¶
func ParseCloseFrameDataUnsafe(payload []byte) (codeStatusCode, reasonstring)
ParseCloseFrameDataUnsafe is like ParseCloseFrameData except the thingthat it does not copies payload bytes into reason, but prepares unsafe cast.
func (StatusCode)Empty¶
func (sStatusCode) Empty()bool
Empty reports whether the code is empty.Empty code has no any meaning neither app level codes nor other.This method is useful just to check that code is golang default value 0.
func (StatusCode)In¶
func (sStatusCode) In(rStatusCodeRange)bool
In reports whether the code is defined in given range.
func (StatusCode)IsApplicationSpec¶
func (sStatusCode) IsApplicationSpec()bool
IsApplicationSpec reports whether the code should be defined byapplication, framework or libraries specification.
func (StatusCode)IsNotUsed¶
func (sStatusCode) IsNotUsed()bool
IsNotUsed reports whether the code is predefined in not used range.
func (StatusCode)IsPrivateSpec¶
func (sStatusCode) IsPrivateSpec()bool
IsPrivateSpec reports whether the code should be defined privately.
func (StatusCode)IsProtocolDefined¶
func (sStatusCode) IsProtocolDefined()bool
IsProtocolDefined reports whether the code is already defined by protocol specification.
func (StatusCode)IsProtocolReserved¶
func (sStatusCode) IsProtocolReserved()bool
IsProtocolReserved reports whether the code is defined by protocol specificationto be reserved only for application usage purpose.
func (StatusCode)IsProtocolSpec¶
func (sStatusCode) IsProtocolSpec()bool
IsProtocolSpec reports whether the code should be defined by protocol specification.
typeStatusCodeRange¶
type StatusCodeRange struct {Min, MaxStatusCode}
StatusCodeRange describes range of StatusCode values.
typeStatusError¶
type StatusErrorint
StatusError contains an unexpected status-line code from the server.
func (StatusError)Error¶
func (sStatusError) Error()string
typeUpgrader¶
type Upgrader struct {// ReadBufferSize and WriteBufferSize is an I/O buffer sizes.// They used to read and write http data while upgrading to WebSocket.// Allocated buffers are pooled with sync.Pool to avoid extra allocations.//// If a size is zero then default value is used.//// Usually it is useful to set read buffer size bigger than write buffer// size because incoming request could contain long header values, such as// Cookie. Response, in other way, could be big only if user write multiple// custom headers. Usually response takes less than 256 bytes.ReadBufferSize, WriteBufferSizeint// Protocol is a select function that is used to select subprotocol// from list requested by client. If this field is set, then the first matched// protocol is sent to a client as negotiated.//// The argument is only valid until the callback returns.Protocol func([]byte)bool// ProtocolCustrom allow user to parse Sec-WebSocket-Protocol header manually.// Note that returned bytes must be valid until Upgrade returns.// If ProtocolCustom is set, it used instead of Protocol function.ProtocolCustom func([]byte) (string,bool)// Extension is a select function that is used to select extensions// from list requested by client. If this field is set, then the all matched// extensions are sent to a client as negotiated.//// Note that Extension may be called multiple times and implementations// must track uniqueness of accepted extensions manually.//// The argument is only valid until the callback returns.//// According to the RFC6455 order of extensions passed by a client is// significant. That is, returning true from this function means that no// other extension with the same name should be checked because server// accepted the most preferable extension right now:// "Note that the order of extensions is significant. Any interactions between// multiple extensions MAY be defined in the documents defining the extensions.// In the absence of such definitions, the interpretation is that the header// fields listed by the client in its request represent a preference of the// header fields it wishes to use, with the first options listed being most// preferable."//// Deprecated: use Negotiate instead.Extension func(httphead.Option)bool// ExtensionCustom allow user to parse Sec-WebSocket-Extensions header// manually.//// If ExtensionCustom() decides to accept received extension, it must// append appropriate option to the given slice of httphead.Option.// It returns results of append() to the given slice and a flag that// reports whether given header value is wellformed or not.//// Note that ExtensionCustom may be called multiple times and// implementations must track uniqueness of accepted extensions manually.//// Note that returned options should be valid until Upgrade returns.// If ExtensionCustom is set, it used instead of Extension function.ExtensionCustom func([]byte, []httphead.Option) ([]httphead.Option,bool)// Negotiate is the callback that is used to negotiate extensions from// the client's offer. If this field is set, then the returned non-zero// extensions are sent to the client as accepted extensions in the// response.//// The argument is only valid until the Negotiate callback returns.//// If returned error is non-nil then connection is rejected and response is// sent with appropriate HTTP error code and body set to error message.//// RejectConnectionError could be used to get more control on response.Negotiate func(httphead.Option) (httphead.Option,error)// Header is an optional HandshakeHeader instance that could be used to// write additional headers to the handshake response.//// It used instead of any key-value mappings to avoid allocations in user// land.//// Note that if present, it will be written in any result of handshake.HeaderHandshakeHeader// OnRequest is a callback that will be called after request line// successful parsing.//// The arguments are only valid until the callback returns.//// If returned error is non-nil then connection is rejected and response is// sent with appropriate HTTP error code and body set to error message.//// RejectConnectionError could be used to get more control on response.OnRequest func(uri []byte)error// OnHost is a callback that will be called after "Host" header successful// parsing.//// It is separated from OnHeader callback because the Host header must be// present in each request since HTTP/1.1. Thus Host header is non-optional// and required for every WebSocket handshake.//// The arguments are only valid until the callback returns.//// If returned error is non-nil then connection is rejected and response is// sent with appropriate HTTP error code and body set to error message.//// RejectConnectionError could be used to get more control on response.OnHost func(host []byte)error// OnHeader is a callback that will be called after successful parsing of// header, that is not used during WebSocket handshake procedure. That is,// it will be called with non-websocket headers, which could be relevant// for application-level logic.//// The arguments are only valid until the callback returns.//// If returned error is non-nil then connection is rejected and response is// sent with appropriate HTTP error code and body set to error message.//// RejectConnectionError could be used to get more control on response.OnHeader func(key, value []byte)error// OnBeforeUpgrade is a callback that will be called before sending// successful upgrade response.//// Setting OnBeforeUpgrade allows user to make final application-level// checks and decide whether this connection is allowed to successfully// upgrade to WebSocket.//// It must return non-nil either HandshakeHeader or error and never both.//// If returned error is non-nil then connection is rejected and response is// sent with appropriate HTTP error code and body set to error message.//// RejectConnectionError could be used to get more control on response.OnBeforeUpgrade func() (headerHandshakeHeader, errerror)}
Upgrader contains options for upgrading connection to websocket.
var DefaultUpgraderUpgrader
DefaultUpgrader is an Upgrader that holds no options and is used by Upgradefunction.
func (Upgrader)Upgrade¶
func (uUpgrader) Upgrade(connio.ReadWriter) (hsHandshake, errerror)
Upgrade zero-copy upgrades connection to WebSocket. It interprets given connas connection with incoming HTTP Upgrade request.
It is a caller responsibility to manage i/o timeouts on conn.
Non-nil error means that request for the WebSocket upgrade is invalid ormalformed and usually connection should be closed.Even when error is non-nil Upgrade will write appropriate response intoconnection in compliance with RFC.