Source File
reader.go
Belonging Package
github.com/gobwas/ws/wsutil
package wsutilimport ()// ErrNoFrameAdvance means that Reader's Read() method was called without// preceding NextFrame() call.var ErrNoFrameAdvance = errors.New("no frame advance")// ErrFrameTooLarge indicates that a message of length higher than// MaxFrameSize was being read.var ErrFrameTooLarge = errors.New("frame too large")// FrameHandlerFunc handles parsed frame header and its body represented by// io.Reader.//// Note that reader represents already unmasked body.type FrameHandlerFunc func(ws.Header, io.Reader) error// Reader is a wrapper around source io.Reader which represents WebSocket// connection. It contains options for reading messages from source.//// Reader implements io.Reader, which Read() method reads payload of incoming// WebSocket frames. It also takes care on fragmented frames and possibly// intermediate control frames between them.//// Note that Reader's methods are not goroutine safe.type Reader struct {Source io.ReaderState ws.State// SkipHeaderCheck disables checking header bits to be RFC6455 compliant.SkipHeaderCheck bool// CheckUTF8 enables UTF-8 checks for text frames payload. If incoming// bytes are not valid UTF-8 sequence, ErrInvalidUTF8 returned.CheckUTF8 bool// Extensions is a list of negotiated extensions for reader Source.// It is used to meet the specs and clear appropriate bits in fragment// header RSV segment.Extensions []RecvExtension// MaxFrameSize controls the maximum frame size in bytes// that can be read. A message exceeding that size will return// a ErrFrameTooLarge to the application.//// Not setting this field means there is no limit.MaxFrameSize int64OnContinuation FrameHandlerFuncOnIntermediate FrameHandlerFuncopCode ws.OpCode // Used to store message op code on fragmentation.frame io.Reader // Used to as frame reader.raw io.LimitedReader // Used to discard frames without cipher.utf8 UTF8Reader // Used to check UTF8 sequences if CheckUTF8 is true.}// NewReader creates new frame reader that reads from r keeping given state to// make some protocol validity checks when it needed.func ( io.Reader, ws.State) *Reader {return &Reader{Source: ,State: ,}}// NewClientSideReader is a helper function that calls NewReader with r and// ws.StateClientSide.func ( io.Reader) *Reader {return NewReader(, ws.StateClientSide)}// NewServerSideReader is a helper function that calls NewReader with r and// ws.StateServerSide.func ( io.Reader) *Reader {return NewReader(, ws.StateServerSide)}// Read implements io.Reader. It reads the next message payload into p.// It takes care on fragmented messages.//// The error is io.EOF only if all of message bytes were read.// If an io.EOF happens during reading some but not all the message bytes// Read() returns io.ErrUnexpectedEOF.//// The error is ErrNoFrameAdvance if no NextFrame() call was made before// reading next message bytes.func ( *Reader) ( []byte) ( int, error) {if .frame == nil {if !.fragmented() {// Every new Read() must be preceded by NextFrame() call.return 0, ErrNoFrameAdvance}// Read next continuation or intermediate control frame., := .NextFrame()if != nil {return 0,}if .frame == nil {// We handled intermediate control and now got nothing to read.return 0, nil}}, = .frame.Read()if != nil && != io.EOF {return ,}if == nil && .raw.N != 0 {return , nil}// EOF condition (either err is io.EOF or r.raw.N is zero).switch {case .raw.N != 0:= io.ErrUnexpectedEOFcase .fragmented():= nil.resetFragment()case .CheckUTF8 && !.utf8.Valid():// NOTE: check utf8 only when full message received, since partial// reads may be invalid.= .utf8.Accepted()= ErrInvalidUTF8default:.reset()= io.EOF}return ,}// Discard discards current message unread bytes.// It discards all frames of fragmented message.func ( *Reader) () ( error) {for {_, = io.Copy(ioutil.Discard, &.raw)if != nil {break}if !.fragmented() {break}if _, = .NextFrame(); != nil {break}}.reset()return}// NextFrame prepares r to read next message. It returns received frame header// and non-nil error on failure.//// Note that next NextFrame() call must be done after receiving or discarding// all current message bytes.func ( *Reader) () ( ws.Header, error) {, = ws.ReadHeader(.Source)if == io.EOF && .fragmented() {// If we are in fragmented state EOF means that is was totally// unexpected.//// NOTE: This is necessary to prevent callers such that// ioutil.ReadAll to receive some amount of bytes without an error.// ReadAll() ignores an io.EOF error, thus caller may think that// whole message fetched, but actually only part of it.= io.ErrUnexpectedEOF}if == nil && !.SkipHeaderCheck {= ws.CheckHeader(, .State)}if != nil {return ,}if := .MaxFrameSize; > 0 && .Length > {return , ErrFrameTooLarge}// Save raw reader to use it on discarding frame without ciphering and// other streaming checks..raw = io.LimitedReader{R: .Source,N: .Length,}:= io.Reader(&.raw)if .Masked {= NewCipherReader(, .Mask)}for , := range .Extensions {, = .UnsetBits()if != nil {return ,}}if .fragmented() {if .OpCode.IsControl() {if := .OnIntermediate; != nil {= (, )}if == nil {// Ensure that src is empty._, = io.Copy(ioutil.Discard, &.raw)}return ,}} else {.opCode = .OpCode}if .CheckUTF8 && (.OpCode == ws.OpText || (.fragmented() && .opCode == ws.OpText)) {.utf8.Source == &.utf8}// Save reader with ciphering and other streaming checks..frame =if .OpCode == ws.OpContinuation {if := .OnContinuation; != nil {= (, )}}if .Fin {.State = .State.Clear(ws.StateFragmented)} else {.State = .State.Set(ws.StateFragmented)}return ,}func ( *Reader) () bool {return .State.Fragmented()}func ( *Reader) () {.raw = io.LimitedReader{}.frame = nil// Reset source of the UTF8Reader, but not the state..utf8.Source = nil}func ( *Reader) () {.raw = io.LimitedReader{}.frame = nil.utf8 = UTF8Reader{}.opCode = 0}// NextReader prepares next message read from r. It returns header that// describes the message and io.Reader to read message's payload. It returns// non-nil error when it is not possible to read message's initial frame.//// Note that next NextReader() on the same r should be done after reading all// bytes from previously returned io.Reader. For more performant way to discard// message use Reader and its Discard() method.//// Note that it will not handle any "intermediate" frames, that possibly could// be received between text/binary continuation frames. That is, if peer sent// text/binary frame with fin flag "false", then it could send ping frame, and// eventually remaining part of text/binary frame with fin "true" – with// NextReader() the ping frame will be dropped without any notice. To handle// this rare, but possible situation (and if you do not know exactly which// frames peer could send), you could use Reader with OnIntermediate field set.func ( io.Reader, ws.State) (ws.Header, io.Reader, error) {:= &Reader{Source: ,State: ,}, := .NextFrame()if != nil {return , nil,}return , , nil}
![]() |
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. |