Source File
handler.go
Belonging Package
github.com/gobwas/ws/wsutil
package wsutilimport ()// ClosedError returned when peer has closed the connection with appropriate// code and a textual reason.type ClosedError struct {Code ws.StatusCodeReason string}// Error implements error interface.func ( ClosedError) () string {return "ws closed: " + strconv.FormatUint(uint64(.Code), 10) + " " + .Reason}// ControlHandler contains logic of handling control frames.//// The intentional way to use it is to read the next frame header from the// connection, optionally check its validity via ws.CheckHeader() and if it is// not a ws.OpText of ws.OpBinary (or ws.OpContinuation) – pass it to Handle()// method.//// That is, passed header should be checked to get rid of unexpected errors.//// The Handle() method will read out all control frame payload (if any) and// write necessary bytes as a rfc compatible response.type ControlHandler struct {Src io.ReaderDst io.WriterState ws.State// DisableSrcCiphering disables unmasking payload data read from Src.// It is useful when wsutil.Reader is used or when frame payload already// pulled and ciphered out from the connection (and introduced by// bytes.Reader, for example).DisableSrcCiphering bool}// ErrNotControlFrame is returned by ControlHandler to indicate that given// header could not be handled.var ErrNotControlFrame = errors.New("not a control frame")// Handle handles control frames regarding to the c.State and writes responses// to the c.Dst when needed.//// It returns ErrNotControlFrame when given header is not of ws.OpClose,// ws.OpPing or ws.OpPong operation code.func ( ControlHandler) ( ws.Header) error {switch .OpCode {case ws.OpPing:return .HandlePing()case ws.OpPong:return .HandlePong()case ws.OpClose:return .HandleClose()}return ErrNotControlFrame}// HandlePing handles ping frame and writes specification compatible response// to the c.Dst.func ( ControlHandler) ( ws.Header) error {if .Length == 0 {// The most common case when ping is empty.// Note that when sending masked frame the mask for empty payload is// just four zero bytes.return ws.WriteHeader(.Dst, ws.Header{Fin: true,OpCode: ws.OpPong,Masked: .State.ClientSide(),})}// In other way reply with Pong frame with copied payload.:= pbytes.GetLen(int(.Length) + ws.HeaderSize(ws.Header{Length: .Length,Masked: .State.ClientSide(),}))defer pbytes.Put()// Deal with ciphering i/o:// Masking key is used to mask the "Payload data" defined in the same// section as frame-payload-data, which includes "Extension data" and// "Application data".//// See https://tools.ietf.org/html/rfc6455#section-5.3//// NOTE: We prefer ControlWriter with preallocated buffer to// ws.WriteHeader because it performs one syscall instead of two.:= NewControlWriterBuffer(.Dst, .State, ws.OpPong, ):= .Srcif .State.ServerSide() && !.DisableSrcCiphering {= NewCipherReader(, .Mask)}, := io.Copy(, )if == nil {= .Flush()}return}// HandlePong handles pong frame by discarding it.func ( ControlHandler) ( ws.Header) error {if .Length == 0 {return nil}:= pbytes.GetLen(int(.Length))defer pbytes.Put()// Discard pong message according to the RFC6455:// A Pong frame MAY be sent unsolicited. This serves as a// unidirectional heartbeat. A response to an unsolicited Pong frame// is not expected., := io.CopyBuffer(ioutil.Discard, .Src, )return}// HandleClose handles close frame, makes protocol validity checks and writes// specification compatible response to the c.Dst.func ( ControlHandler) ( ws.Header) error {if .Length == 0 {:= ws.WriteHeader(.Dst, ws.Header{Fin: true,OpCode: ws.OpClose,Masked: .State.ClientSide(),})if != nil {return}// Due to RFC, we should interpret the code as no status code// received:// If this Close control frame contains no status code, _The WebSocket// Connection Close Code_ is considered to be 1005.//// See https://tools.ietf.org/html/rfc6455#section-7.1.5return ClosedError{Code: ws.StatusNoStatusRcvd,}}// Prepare bytes both for reading reason and sending response.:= pbytes.GetLen(int(.Length) + ws.HeaderSize(ws.Header{Length: .Length,Masked: .State.ClientSide(),}))defer pbytes.Put()// Get the subslice to read the frame payload out.:= [:.Length]:= .Srcif .State.ServerSide() && !.DisableSrcCiphering {= NewCipherReader(, .Mask)}if , := io.ReadFull(, ); != nil {return}, := ws.ParseCloseFrameData()if := ws.CheckCloseFrameData(, ); != nil {// Here we could not use the prepared bytes because there is no// guarantee that it may fit our protocol error closure code and a// reason..closeWithProtocolError()return}// Deal with ciphering i/o:// Masking key is used to mask the "Payload data" defined in the same// section as frame-payload-data, which includes "Extension data" and// "Application data".//// See https://tools.ietf.org/html/rfc6455#section-5.3//// NOTE: We prefer ControlWriter with preallocated buffer to// ws.WriteHeader because it performs one syscall instead of two.:= NewControlWriterBuffer(.Dst, .State, ws.OpClose, )// RFC6455#5.5.1:// If an endpoint receives a Close frame and did not previously// send a Close frame, the endpoint MUST send a Close frame in// response. (When sending a Close frame in response, the endpoint// typically echoes the status code it received.), := .Write([:2])if != nil {return}if := .Flush(); != nil {return}return ClosedError{Code: ,Reason: ,}}func ( ControlHandler) ( error) error {:= ws.NewCloseFrame(ws.NewCloseFrameBody(ws.StatusProtocolError, .Error(),))if .State.ClientSide() {ws.MaskFrameInPlace()}return ws.WriteFrame(.Dst, )}
![]() |
The pages are generated with Golds v0.8.2. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |