This upgrade fixes a potential DoS vector bug in gorilla/websocket 1.4.0, see https://github.com/gorilla/websocket/security/advisories/GHSA-jf24-p9p9-4rjh Signed-off-by: llhuii <liulinghui@huawei.com>tags/v0.4.0
| @@ -4,7 +4,7 @@ go 1.14 | |||||
| require ( | require ( | ||||
| github.com/emicklei/go-restful/v3 v3.4.0 | github.com/emicklei/go-restful/v3 v3.4.0 | ||||
| github.com/gorilla/websocket v1.4.0 | |||||
| github.com/gorilla/websocket v1.4.2 | |||||
| github.com/minio/minio-go/v7 v7.0.10 | github.com/minio/minio-go/v7 v7.0.10 | ||||
| github.com/onsi/ginkgo v1.11.0 | github.com/onsi/ginkgo v1.11.0 | ||||
| github.com/onsi/gomega v1.7.0 | github.com/onsi/gomega v1.7.0 | ||||
| @@ -289,8 +289,9 @@ github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORR | |||||
| github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= | github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= | ||||
| github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= | github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= | ||||
| github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||||
| github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH/Q= | |||||
| github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= | ||||
| github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= | |||||
| github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | |||||
| github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= | github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= | ||||
| github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= | github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= | ||||
| github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= | github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4 h1:z53tR0945TRRQO/fLEVPI6SMv7ZflF0TEaTAoU7tOzg= | ||||
| @@ -1,19 +0,0 @@ | |||||
| language: go | |||||
| sudo: false | |||||
| matrix: | |||||
| include: | |||||
| - go: 1.7.x | |||||
| - go: 1.8.x | |||||
| - go: 1.9.x | |||||
| - go: 1.10.x | |||||
| - go: 1.11.x | |||||
| - go: tip | |||||
| allow_failures: | |||||
| - go: tip | |||||
| script: | |||||
| - go get -t -v ./... | |||||
| - diff -u <(echo -n) <(gofmt -d .) | |||||
| - go vet $(go list ./... | grep -v /vendor/) | |||||
| - go test -v -race ./... | |||||
| @@ -1,14 +1,14 @@ | |||||
| # Gorilla WebSocket | # Gorilla WebSocket | ||||
| [](https://godoc.org/github.com/gorilla/websocket) | |||||
| [](https://circleci.com/gh/gorilla/websocket) | |||||
| Gorilla WebSocket is a [Go](http://golang.org/) implementation of the | Gorilla WebSocket is a [Go](http://golang.org/) implementation of the | ||||
| [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. | [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. | ||||
| [](https://travis-ci.org/gorilla/websocket) | |||||
| [](https://godoc.org/github.com/gorilla/websocket) | |||||
| ### Documentation | ### Documentation | ||||
| * [API Reference](http://godoc.org/github.com/gorilla/websocket) | |||||
| * [API Reference](https://pkg.go.dev/github.com/gorilla/websocket?tab=doc) | |||||
| * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) | * [Chat example](https://github.com/gorilla/websocket/tree/master/examples/chat) | ||||
| * [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) | * [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) | ||||
| * [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) | * [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) | ||||
| @@ -27,7 +27,7 @@ package API is stable. | |||||
| ### Protocol Compliance | ### Protocol Compliance | ||||
| The Gorilla WebSocket package passes the server tests in the [Autobahn Test | The Gorilla WebSocket package passes the server tests in the [Autobahn Test | ||||
| Suite](http://autobahn.ws/testsuite) using the application in the [examples/autobahn | |||||
| Suite](https://github.com/crossbario/autobahn-testsuite) using the application in the [examples/autobahn | |||||
| subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). | subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn). | ||||
| ### Gorilla WebSocket compared with other packages | ### Gorilla WebSocket compared with other packages | ||||
| @@ -40,7 +40,7 @@ subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn | |||||
| </tr> | </tr> | ||||
| <tr> | <tr> | ||||
| <tr><td colspan="3"><a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a> Features</td></tr> | <tr><td colspan="3"><a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a> Features</td></tr> | ||||
| <tr><td>Passes <a href="http://autobahn.ws/testsuite/">Autobahn Test Suite</a></td><td><a href="https://github.com/gorilla/websocket/tree/master/examples/autobahn">Yes</a></td><td>No</td></tr> | |||||
| <tr><td>Passes <a href="https://github.com/crossbario/autobahn-testsuite">Autobahn Test Suite</a></td><td><a href="https://github.com/gorilla/websocket/tree/master/examples/autobahn">Yes</a></td><td>No</td></tr> | |||||
| <tr><td>Receive <a href="https://tools.ietf.org/html/rfc6455#section-5.4">fragmented</a> message<td>Yes</td><td><a href="https://code.google.com/p/go/issues/detail?id=7632">No</a>, see note 1</td></tr> | <tr><td>Receive <a href="https://tools.ietf.org/html/rfc6455#section-5.4">fragmented</a> message<td>Yes</td><td><a href="https://code.google.com/p/go/issues/detail?id=7632">No</a>, see note 1</td></tr> | ||||
| <tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">close</a> message</td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td><a href="https://code.google.com/p/go/issues/detail?id=4588">No</a></td></tr> | <tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.1">close</a> message</td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td><a href="https://code.google.com/p/go/issues/detail?id=4588">No</a></td></tr> | ||||
| <tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">pings</a> and receive <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pongs</a></td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td>No</td></tr> | <tr><td>Send <a href="https://tools.ietf.org/html/rfc6455#section-5.5.2">pings</a> and receive <a href="https://tools.ietf.org/html/rfc6455#section-5.5.3">pongs</a></td><td><a href="http://godoc.org/github.com/gorilla/websocket#hdr-Control_Messages">Yes</a></td><td>No</td></tr> | ||||
| @@ -70,7 +70,7 @@ type Dialer struct { | |||||
| // HandshakeTimeout specifies the duration for the handshake to complete. | // HandshakeTimeout specifies the duration for the handshake to complete. | ||||
| HandshakeTimeout time.Duration | HandshakeTimeout time.Duration | ||||
| // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer | |||||
| // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer | |||||
| // size is zero, then a useful default size is used. The I/O buffer sizes | // size is zero, then a useful default size is used. The I/O buffer sizes | ||||
| // do not limit the size of the messages that can be sent or received. | // do not limit the size of the messages that can be sent or received. | ||||
| ReadBufferSize, WriteBufferSize int | ReadBufferSize, WriteBufferSize int | ||||
| @@ -140,7 +140,7 @@ var nilDialer = *DefaultDialer | |||||
| // Use the response.Header to get the selected subprotocol | // Use the response.Header to get the selected subprotocol | ||||
| // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). | // (Sec-WebSocket-Protocol) and cookies (Set-Cookie). | ||||
| // | // | ||||
| // The context will be used in the request and in the Dialer | |||||
| // The context will be used in the request and in the Dialer. | |||||
| // | // | ||||
| // If the WebSocket handshake fails, ErrBadHandshake is returned along with a | // If the WebSocket handshake fails, ErrBadHandshake is returned along with a | ||||
| // non-nil *http.Response so that callers can handle redirects, authentication, | // non-nil *http.Response so that callers can handle redirects, authentication, | ||||
| @@ -244,8 +244,8 @@ type Conn struct { | |||||
| subprotocol string | subprotocol string | ||||
| // Write fields | // Write fields | ||||
| mu chan bool // used as mutex to protect write to conn | |||||
| writeBuf []byte // frame is constructed in this buffer. | |||||
| mu chan struct{} // used as mutex to protect write to conn | |||||
| writeBuf []byte // frame is constructed in this buffer. | |||||
| writePool BufferPool | writePool BufferPool | ||||
| writeBufSize int | writeBufSize int | ||||
| writeDeadline time.Time | writeDeadline time.Time | ||||
| @@ -260,10 +260,12 @@ type Conn struct { | |||||
| newCompressionWriter func(io.WriteCloser, int) io.WriteCloser | newCompressionWriter func(io.WriteCloser, int) io.WriteCloser | ||||
| // Read fields | // Read fields | ||||
| reader io.ReadCloser // the current reader returned to the application | |||||
| readErr error | |||||
| br *bufio.Reader | |||||
| readRemaining int64 // bytes remaining in current frame. | |||||
| reader io.ReadCloser // the current reader returned to the application | |||||
| readErr error | |||||
| br *bufio.Reader | |||||
| // bytes remaining in current frame. | |||||
| // set setReadRemaining to safely update this value and prevent overflow | |||||
| readRemaining int64 | |||||
| readFinal bool // true the current message has more frames. | readFinal bool // true the current message has more frames. | ||||
| readLength int64 // Message size. | readLength int64 // Message size. | ||||
| readLimit int64 // Maximum message size. | readLimit int64 // Maximum message size. | ||||
| @@ -300,8 +302,8 @@ func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, | |||||
| writeBuf = make([]byte, writeBufferSize) | writeBuf = make([]byte, writeBufferSize) | ||||
| } | } | ||||
| mu := make(chan bool, 1) | |||||
| mu <- true | |||||
| mu := make(chan struct{}, 1) | |||||
| mu <- struct{}{} | |||||
| c := &Conn{ | c := &Conn{ | ||||
| isServer: isServer, | isServer: isServer, | ||||
| br: br, | br: br, | ||||
| @@ -320,6 +322,17 @@ func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, | |||||
| return c | return c | ||||
| } | } | ||||
| // setReadRemaining tracks the number of bytes remaining on the connection. If n | |||||
| // overflows, an ErrReadLimit is returned. | |||||
| func (c *Conn) setReadRemaining(n int64) error { | |||||
| if n < 0 { | |||||
| return ErrReadLimit | |||||
| } | |||||
| c.readRemaining = n | |||||
| return nil | |||||
| } | |||||
| // Subprotocol returns the negotiated protocol for the connection. | // Subprotocol returns the negotiated protocol for the connection. | ||||
| func (c *Conn) Subprotocol() string { | func (c *Conn) Subprotocol() string { | ||||
| return c.subprotocol | return c.subprotocol | ||||
| @@ -364,7 +377,7 @@ func (c *Conn) read(n int) ([]byte, error) { | |||||
| func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error { | func (c *Conn) write(frameType int, deadline time.Time, buf0, buf1 []byte) error { | ||||
| <-c.mu | <-c.mu | ||||
| defer func() { c.mu <- true }() | |||||
| defer func() { c.mu <- struct{}{} }() | |||||
| c.writeErrMu.Lock() | c.writeErrMu.Lock() | ||||
| err := c.writeErr | err := c.writeErr | ||||
| @@ -416,7 +429,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er | |||||
| maskBytes(key, 0, buf[6:]) | maskBytes(key, 0, buf[6:]) | ||||
| } | } | ||||
| d := time.Hour * 1000 | |||||
| d := 1000 * time.Hour | |||||
| if !deadline.IsZero() { | if !deadline.IsZero() { | ||||
| d = deadline.Sub(time.Now()) | d = deadline.Sub(time.Now()) | ||||
| if d < 0 { | if d < 0 { | ||||
| @@ -431,7 +444,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er | |||||
| case <-timer.C: | case <-timer.C: | ||||
| return errWriteTimeout | return errWriteTimeout | ||||
| } | } | ||||
| defer func() { c.mu <- true }() | |||||
| defer func() { c.mu <- struct{}{} }() | |||||
| c.writeErrMu.Lock() | c.writeErrMu.Lock() | ||||
| err := c.writeErr | err := c.writeErr | ||||
| @@ -451,7 +464,8 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er | |||||
| return err | return err | ||||
| } | } | ||||
| func (c *Conn) prepWrite(messageType int) error { | |||||
| // beginMessage prepares a connection and message writer for a new message. | |||||
| func (c *Conn) beginMessage(mw *messageWriter, messageType int) error { | |||||
| // Close previous writer if not already closed by the application. It's | // Close previous writer if not already closed by the application. It's | ||||
| // probably better to return an error in this situation, but we cannot | // probably better to return an error in this situation, but we cannot | ||||
| // change this without breaking existing applications. | // change this without breaking existing applications. | ||||
| @@ -471,6 +485,10 @@ func (c *Conn) prepWrite(messageType int) error { | |||||
| return err | return err | ||||
| } | } | ||||
| mw.c = c | |||||
| mw.frameType = messageType | |||||
| mw.pos = maxFrameHeaderSize | |||||
| if c.writeBuf == nil { | if c.writeBuf == nil { | ||||
| wpd, ok := c.writePool.Get().(writePoolData) | wpd, ok := c.writePool.Get().(writePoolData) | ||||
| if ok { | if ok { | ||||
| @@ -491,16 +509,11 @@ func (c *Conn) prepWrite(messageType int) error { | |||||
| // All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and | // All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and | ||||
| // PongMessage) are supported. | // PongMessage) are supported. | ||||
| func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { | func (c *Conn) NextWriter(messageType int) (io.WriteCloser, error) { | ||||
| if err := c.prepWrite(messageType); err != nil { | |||||
| var mw messageWriter | |||||
| if err := c.beginMessage(&mw, messageType); err != nil { | |||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| mw := &messageWriter{ | |||||
| c: c, | |||||
| frameType: messageType, | |||||
| pos: maxFrameHeaderSize, | |||||
| } | |||||
| c.writer = mw | |||||
| c.writer = &mw | |||||
| if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { | if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { | ||||
| w := c.newCompressionWriter(c.writer, c.compressionLevel) | w := c.newCompressionWriter(c.writer, c.compressionLevel) | ||||
| mw.compress = true | mw.compress = true | ||||
| @@ -517,10 +530,16 @@ type messageWriter struct { | |||||
| err error | err error | ||||
| } | } | ||||
| func (w *messageWriter) fatal(err error) error { | |||||
| func (w *messageWriter) endMessage(err error) error { | |||||
| if w.err != nil { | if w.err != nil { | ||||
| w.err = err | |||||
| w.c.writer = nil | |||||
| return err | |||||
| } | |||||
| c := w.c | |||||
| w.err = err | |||||
| c.writer = nil | |||||
| if c.writePool != nil { | |||||
| c.writePool.Put(writePoolData{buf: c.writeBuf}) | |||||
| c.writeBuf = nil | |||||
| } | } | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -534,7 +553,7 @@ func (w *messageWriter) flushFrame(final bool, extra []byte) error { | |||||
| // Check for invalid control frames. | // Check for invalid control frames. | ||||
| if isControl(w.frameType) && | if isControl(w.frameType) && | ||||
| (!final || length > maxControlFramePayloadSize) { | (!final || length > maxControlFramePayloadSize) { | ||||
| return w.fatal(errInvalidControlFrame) | |||||
| return w.endMessage(errInvalidControlFrame) | |||||
| } | } | ||||
| b0 := byte(w.frameType) | b0 := byte(w.frameType) | ||||
| @@ -579,7 +598,7 @@ func (w *messageWriter) flushFrame(final bool, extra []byte) error { | |||||
| copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) | copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) | ||||
| maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos]) | maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos]) | ||||
| if len(extra) > 0 { | if len(extra) > 0 { | ||||
| return c.writeFatal(errors.New("websocket: internal error, extra used in client mode")) | |||||
| return w.endMessage(c.writeFatal(errors.New("websocket: internal error, extra used in client mode"))) | |||||
| } | } | ||||
| } | } | ||||
| @@ -600,15 +619,11 @@ func (w *messageWriter) flushFrame(final bool, extra []byte) error { | |||||
| c.isWriting = false | c.isWriting = false | ||||
| if err != nil { | if err != nil { | ||||
| return w.fatal(err) | |||||
| return w.endMessage(err) | |||||
| } | } | ||||
| if final { | if final { | ||||
| c.writer = nil | |||||
| if c.writePool != nil { | |||||
| c.writePool.Put(writePoolData{buf: c.writeBuf}) | |||||
| c.writeBuf = nil | |||||
| } | |||||
| w.endMessage(errWriteClosed) | |||||
| return nil | return nil | ||||
| } | } | ||||
| @@ -706,11 +721,7 @@ func (w *messageWriter) Close() error { | |||||
| if w.err != nil { | if w.err != nil { | ||||
| return w.err | return w.err | ||||
| } | } | ||||
| if err := w.flushFrame(true, nil); err != nil { | |||||
| return err | |||||
| } | |||||
| w.err = errWriteClosed | |||||
| return nil | |||||
| return w.flushFrame(true, nil) | |||||
| } | } | ||||
| // WritePreparedMessage writes prepared message into connection. | // WritePreparedMessage writes prepared message into connection. | ||||
| @@ -742,10 +753,10 @@ func (c *Conn) WriteMessage(messageType int, data []byte) error { | |||||
| if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { | if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { | ||||
| // Fast path with no allocations and single frame. | // Fast path with no allocations and single frame. | ||||
| if err := c.prepWrite(messageType); err != nil { | |||||
| var mw messageWriter | |||||
| if err := c.beginMessage(&mw, messageType); err != nil { | |||||
| return err | return err | ||||
| } | } | ||||
| mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize} | |||||
| n := copy(c.writeBuf[mw.pos:], data) | n := copy(c.writeBuf[mw.pos:], data) | ||||
| mw.pos += n | mw.pos += n | ||||
| data = data[n:] | data = data[n:] | ||||
| @@ -792,7 +803,7 @@ func (c *Conn) advanceFrame() (int, error) { | |||||
| final := p[0]&finalBit != 0 | final := p[0]&finalBit != 0 | ||||
| frameType := int(p[0] & 0xf) | frameType := int(p[0] & 0xf) | ||||
| mask := p[1]&maskBit != 0 | mask := p[1]&maskBit != 0 | ||||
| c.readRemaining = int64(p[1] & 0x7f) | |||||
| c.setReadRemaining(int64(p[1] & 0x7f)) | |||||
| c.readDecompress = false | c.readDecompress = false | ||||
| if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 { | if c.newDecompressionReader != nil && (p[0]&rsv1Bit) != 0 { | ||||
| @@ -826,7 +837,17 @@ func (c *Conn) advanceFrame() (int, error) { | |||||
| return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType)) | return noFrame, c.handleProtocolError("unknown opcode " + strconv.Itoa(frameType)) | ||||
| } | } | ||||
| // 3. Read and parse frame length. | |||||
| // 3. Read and parse frame length as per | |||||
| // https://tools.ietf.org/html/rfc6455#section-5.2 | |||||
| // | |||||
| // The length of the "Payload data", in bytes: if 0-125, that is the payload | |||||
| // length. | |||||
| // - If 126, the following 2 bytes interpreted as a 16-bit unsigned | |||||
| // integer are the payload length. | |||||
| // - If 127, the following 8 bytes interpreted as | |||||
| // a 64-bit unsigned integer (the most significant bit MUST be 0) are the | |||||
| // payload length. Multibyte length quantities are expressed in network byte | |||||
| // order. | |||||
| switch c.readRemaining { | switch c.readRemaining { | ||||
| case 126: | case 126: | ||||
| @@ -834,13 +855,19 @@ func (c *Conn) advanceFrame() (int, error) { | |||||
| if err != nil { | if err != nil { | ||||
| return noFrame, err | return noFrame, err | ||||
| } | } | ||||
| c.readRemaining = int64(binary.BigEndian.Uint16(p)) | |||||
| if err := c.setReadRemaining(int64(binary.BigEndian.Uint16(p))); err != nil { | |||||
| return noFrame, err | |||||
| } | |||||
| case 127: | case 127: | ||||
| p, err := c.read(8) | p, err := c.read(8) | ||||
| if err != nil { | if err != nil { | ||||
| return noFrame, err | return noFrame, err | ||||
| } | } | ||||
| c.readRemaining = int64(binary.BigEndian.Uint64(p)) | |||||
| if err := c.setReadRemaining(int64(binary.BigEndian.Uint64(p))); err != nil { | |||||
| return noFrame, err | |||||
| } | |||||
| } | } | ||||
| // 4. Handle frame masking. | // 4. Handle frame masking. | ||||
| @@ -863,6 +890,12 @@ func (c *Conn) advanceFrame() (int, error) { | |||||
| if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage { | if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage { | ||||
| c.readLength += c.readRemaining | c.readLength += c.readRemaining | ||||
| // Don't allow readLength to overflow in the presence of a large readRemaining | |||||
| // counter. | |||||
| if c.readLength < 0 { | |||||
| return noFrame, ErrReadLimit | |||||
| } | |||||
| if c.readLimit > 0 && c.readLength > c.readLimit { | if c.readLimit > 0 && c.readLength > c.readLimit { | ||||
| c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)) | c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)) | ||||
| return noFrame, ErrReadLimit | return noFrame, ErrReadLimit | ||||
| @@ -876,7 +909,7 @@ func (c *Conn) advanceFrame() (int, error) { | |||||
| var payload []byte | var payload []byte | ||||
| if c.readRemaining > 0 { | if c.readRemaining > 0 { | ||||
| payload, err = c.read(int(c.readRemaining)) | payload, err = c.read(int(c.readRemaining)) | ||||
| c.readRemaining = 0 | |||||
| c.setReadRemaining(0) | |||||
| if err != nil { | if err != nil { | ||||
| return noFrame, err | return noFrame, err | ||||
| } | } | ||||
| @@ -949,6 +982,7 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { | |||||
| c.readErr = hideTempErr(err) | c.readErr = hideTempErr(err) | ||||
| break | break | ||||
| } | } | ||||
| if frameType == TextMessage || frameType == BinaryMessage { | if frameType == TextMessage || frameType == BinaryMessage { | ||||
| c.messageReader = &messageReader{c} | c.messageReader = &messageReader{c} | ||||
| c.reader = c.messageReader | c.reader = c.messageReader | ||||
| @@ -989,7 +1023,9 @@ func (r *messageReader) Read(b []byte) (int, error) { | |||||
| if c.isServer { | if c.isServer { | ||||
| c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n]) | c.readMaskPos = maskBytes(c.readMaskKey, c.readMaskPos, b[:n]) | ||||
| } | } | ||||
| c.readRemaining -= int64(n) | |||||
| rem := c.readRemaining | |||||
| rem -= int64(n) | |||||
| c.setReadRemaining(rem) | |||||
| if c.readRemaining > 0 && c.readErr == io.EOF { | if c.readRemaining > 0 && c.readErr == io.EOF { | ||||
| c.readErr = errUnexpectedEOF | c.readErr = errUnexpectedEOF | ||||
| } | } | ||||
| @@ -1041,7 +1077,7 @@ func (c *Conn) SetReadDeadline(t time.Time) error { | |||||
| return c.conn.SetReadDeadline(t) | return c.conn.SetReadDeadline(t) | ||||
| } | } | ||||
| // SetReadLimit sets the maximum size for a message read from the peer. If a | |||||
| // SetReadLimit sets the maximum size in bytes for a message read from the peer. If a | |||||
| // message exceeds the limit, the connection sends a close message to the peer | // message exceeds the limit, the connection sends a close message to the peer | ||||
| // and returns ErrReadLimit to the application. | // and returns ErrReadLimit to the application. | ||||
| func (c *Conn) SetReadLimit(limit int64) { | func (c *Conn) SetReadLimit(limit int64) { | ||||
| @@ -151,6 +151,53 @@ | |||||
| // checking. The application is responsible for checking the Origin header | // checking. The application is responsible for checking the Origin header | ||||
| // before calling the Upgrade function. | // before calling the Upgrade function. | ||||
| // | // | ||||
| // Buffers | |||||
| // | |||||
| // Connections buffer network input and output to reduce the number | |||||
| // of system calls when reading or writing messages. | |||||
| // | |||||
| // Write buffers are also used for constructing WebSocket frames. See RFC 6455, | |||||
| // Section 5 for a discussion of message framing. A WebSocket frame header is | |||||
| // written to the network each time a write buffer is flushed to the network. | |||||
| // Decreasing the size of the write buffer can increase the amount of framing | |||||
| // overhead on the connection. | |||||
| // | |||||
| // The buffer sizes in bytes are specified by the ReadBufferSize and | |||||
| // WriteBufferSize fields in the Dialer and Upgrader. The Dialer uses a default | |||||
| // size of 4096 when a buffer size field is set to zero. The Upgrader reuses | |||||
| // buffers created by the HTTP server when a buffer size field is set to zero. | |||||
| // The HTTP server buffers have a size of 4096 at the time of this writing. | |||||
| // | |||||
| // The buffer sizes do not limit the size of a message that can be read or | |||||
| // written by a connection. | |||||
| // | |||||
| // Buffers are held for the lifetime of the connection by default. If the | |||||
| // Dialer or Upgrader WriteBufferPool field is set, then a connection holds the | |||||
| // write buffer only when writing a message. | |||||
| // | |||||
| // Applications should tune the buffer sizes to balance memory use and | |||||
| // performance. Increasing the buffer size uses more memory, but can reduce the | |||||
| // number of system calls to read or write the network. In the case of writing, | |||||
| // increasing the buffer size can reduce the number of frame headers written to | |||||
| // the network. | |||||
| // | |||||
| // Some guidelines for setting buffer parameters are: | |||||
| // | |||||
| // Limit the buffer sizes to the maximum expected message size. Buffers larger | |||||
| // than the largest message do not provide any benefit. | |||||
| // | |||||
| // Depending on the distribution of message sizes, setting the buffer size to | |||||
| // a value less than the maximum expected message size can greatly reduce memory | |||||
| // use with a small impact on performance. Here's an example: If 99% of the | |||||
| // messages are smaller than 256 bytes and the maximum message size is 512 | |||||
| // bytes, then a buffer size of 256 bytes will result in 1.01 more system calls | |||||
| // than a buffer size of 512 bytes. The memory savings is 50%. | |||||
| // | |||||
| // A write buffer pool is useful when the application has a modest number | |||||
| // writes over a large number of connections. when buffers are pooled, a larger | |||||
| // buffer size has a reduced impact on total memory use and has the benefit of | |||||
| // reducing system calls and frame overhead. | |||||
| // | |||||
| // Compression EXPERIMENTAL | // Compression EXPERIMENTAL | ||||
| // | // | ||||
| // Per message compression extensions (RFC 7692) are experimentally supported | // Per message compression extensions (RFC 7692) are experimentally supported | ||||
| @@ -0,0 +1,3 @@ | |||||
| module github.com/gorilla/websocket | |||||
| go 1.12 | |||||
| @@ -0,0 +1,42 @@ | |||||
| // Copyright 2019 The Gorilla WebSocket Authors. All rights reserved. | |||||
| // Use of this source code is governed by a BSD-style | |||||
| // license that can be found in the LICENSE file. | |||||
| package websocket | |||||
| import ( | |||||
| "io" | |||||
| "strings" | |||||
| ) | |||||
| // JoinMessages concatenates received messages to create a single io.Reader. | |||||
| // The string term is appended to each message. The returned reader does not | |||||
| // support concurrent calls to the Read method. | |||||
| func JoinMessages(c *Conn, term string) io.Reader { | |||||
| return &joinReader{c: c, term: term} | |||||
| } | |||||
| type joinReader struct { | |||||
| c *Conn | |||||
| term string | |||||
| r io.Reader | |||||
| } | |||||
| func (r *joinReader) Read(p []byte) (int, error) { | |||||
| if r.r == nil { | |||||
| var err error | |||||
| _, r.r, err = r.c.NextReader() | |||||
| if err != nil { | |||||
| return 0, err | |||||
| } | |||||
| if r.term != "" { | |||||
| r.r = io.MultiReader(r.r, strings.NewReader(r.term)) | |||||
| } | |||||
| } | |||||
| n, err := r.r.Read(p) | |||||
| if err == io.EOF { | |||||
| err = nil | |||||
| r.r = nil | |||||
| } | |||||
| return n, err | |||||
| } | |||||
| @@ -73,8 +73,8 @@ func (pm *PreparedMessage) frame(key prepareKey) (int, []byte, error) { | |||||
| // Prepare a frame using a 'fake' connection. | // Prepare a frame using a 'fake' connection. | ||||
| // TODO: Refactor code in conn.go to allow more direct construction of | // TODO: Refactor code in conn.go to allow more direct construction of | ||||
| // the frame. | // the frame. | ||||
| mu := make(chan bool, 1) | |||||
| mu <- true | |||||
| mu := make(chan struct{}, 1) | |||||
| mu <- struct{}{} | |||||
| var nc prepareConn | var nc prepareConn | ||||
| c := &Conn{ | c := &Conn{ | ||||
| conn: &nc, | conn: &nc, | ||||
| @@ -22,18 +22,18 @@ func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) { | |||||
| func init() { | func init() { | ||||
| proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { | proxy_RegisterDialerType("http", func(proxyURL *url.URL, forwardDialer proxy_Dialer) (proxy_Dialer, error) { | ||||
| return &httpProxyDialer{proxyURL: proxyURL, fowardDial: forwardDialer.Dial}, nil | |||||
| return &httpProxyDialer{proxyURL: proxyURL, forwardDial: forwardDialer.Dial}, nil | |||||
| }) | }) | ||||
| } | } | ||||
| type httpProxyDialer struct { | type httpProxyDialer struct { | ||||
| proxyURL *url.URL | |||||
| fowardDial func(network, addr string) (net.Conn, error) | |||||
| proxyURL *url.URL | |||||
| forwardDial func(network, addr string) (net.Conn, error) | |||||
| } | } | ||||
| func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) { | func (hpd *httpProxyDialer) Dial(network string, addr string) (net.Conn, error) { | ||||
| hostPort, _ := hostPortNoPort(hpd.proxyURL) | hostPort, _ := hostPortNoPort(hpd.proxyURL) | ||||
| conn, err := hpd.fowardDial(network, hostPort) | |||||
| conn, err := hpd.forwardDial(network, hostPort) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| @@ -27,7 +27,7 @@ type Upgrader struct { | |||||
| // HandshakeTimeout specifies the duration for the handshake to complete. | // HandshakeTimeout specifies the duration for the handshake to complete. | ||||
| HandshakeTimeout time.Duration | HandshakeTimeout time.Duration | ||||
| // ReadBufferSize and WriteBufferSize specify I/O buffer sizes. If a buffer | |||||
| // ReadBufferSize and WriteBufferSize specify I/O buffer sizes in bytes. If a buffer | |||||
| // size is zero, then buffers allocated by the HTTP server are used. The | // size is zero, then buffers allocated by the HTTP server are used. The | ||||
| // I/O buffer sizes do not limit the size of the messages that can be sent | // I/O buffer sizes do not limit the size of the messages that can be sent | ||||
| // or received. | // or received. | ||||
| @@ -153,7 +153,7 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade | |||||
| challengeKey := r.Header.Get("Sec-Websocket-Key") | challengeKey := r.Header.Get("Sec-Websocket-Key") | ||||
| if challengeKey == "" { | if challengeKey == "" { | ||||
| return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: `Sec-WebSocket-Key' header is missing or blank") | |||||
| return u.returnError(w, r, http.StatusBadRequest, "websocket: not a websocket handshake: 'Sec-WebSocket-Key' header is missing or blank") | |||||
| } | } | ||||
| subprotocol := u.selectSubprotocol(r, responseHeader) | subprotocol := u.selectSubprotocol(r, responseHeader) | ||||
| @@ -31,68 +31,113 @@ func generateChallengeKey() (string, error) { | |||||
| return base64.StdEncoding.EncodeToString(p), nil | return base64.StdEncoding.EncodeToString(p), nil | ||||
| } | } | ||||
| // Octet types from RFC 2616. | |||||
| var octetTypes [256]byte | |||||
| const ( | |||||
| isTokenOctet = 1 << iota | |||||
| isSpaceOctet | |||||
| ) | |||||
| func init() { | |||||
| // From RFC 2616 | |||||
| // | |||||
| // OCTET = <any 8-bit sequence of data> | |||||
| // CHAR = <any US-ASCII character (octets 0 - 127)> | |||||
| // CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)> | |||||
| // CR = <US-ASCII CR, carriage return (13)> | |||||
| // LF = <US-ASCII LF, linefeed (10)> | |||||
| // SP = <US-ASCII SP, space (32)> | |||||
| // HT = <US-ASCII HT, horizontal-tab (9)> | |||||
| // <"> = <US-ASCII double-quote mark (34)> | |||||
| // CRLF = CR LF | |||||
| // LWS = [CRLF] 1*( SP | HT ) | |||||
| // TEXT = <any OCTET except CTLs, but including LWS> | |||||
| // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <"> | |||||
| // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT | |||||
| // token = 1*<any CHAR except CTLs or separators> | |||||
| // qdtext = <any TEXT except <">> | |||||
| for c := 0; c < 256; c++ { | |||||
| var t byte | |||||
| isCtl := c <= 31 || c == 127 | |||||
| isChar := 0 <= c && c <= 127 | |||||
| isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0 | |||||
| if strings.IndexRune(" \t\r\n", rune(c)) >= 0 { | |||||
| t |= isSpaceOctet | |||||
| } | |||||
| if isChar && !isCtl && !isSeparator { | |||||
| t |= isTokenOctet | |||||
| } | |||||
| octetTypes[c] = t | |||||
| } | |||||
| // Token octets per RFC 2616. | |||||
| var isTokenOctet = [256]bool{ | |||||
| '!': true, | |||||
| '#': true, | |||||
| '$': true, | |||||
| '%': true, | |||||
| '&': true, | |||||
| '\'': true, | |||||
| '*': true, | |||||
| '+': true, | |||||
| '-': true, | |||||
| '.': true, | |||||
| '0': true, | |||||
| '1': true, | |||||
| '2': true, | |||||
| '3': true, | |||||
| '4': true, | |||||
| '5': true, | |||||
| '6': true, | |||||
| '7': true, | |||||
| '8': true, | |||||
| '9': true, | |||||
| 'A': true, | |||||
| 'B': true, | |||||
| 'C': true, | |||||
| 'D': true, | |||||
| 'E': true, | |||||
| 'F': true, | |||||
| 'G': true, | |||||
| 'H': true, | |||||
| 'I': true, | |||||
| 'J': true, | |||||
| 'K': true, | |||||
| 'L': true, | |||||
| 'M': true, | |||||
| 'N': true, | |||||
| 'O': true, | |||||
| 'P': true, | |||||
| 'Q': true, | |||||
| 'R': true, | |||||
| 'S': true, | |||||
| 'T': true, | |||||
| 'U': true, | |||||
| 'W': true, | |||||
| 'V': true, | |||||
| 'X': true, | |||||
| 'Y': true, | |||||
| 'Z': true, | |||||
| '^': true, | |||||
| '_': true, | |||||
| '`': true, | |||||
| 'a': true, | |||||
| 'b': true, | |||||
| 'c': true, | |||||
| 'd': true, | |||||
| 'e': true, | |||||
| 'f': true, | |||||
| 'g': true, | |||||
| 'h': true, | |||||
| 'i': true, | |||||
| 'j': true, | |||||
| 'k': true, | |||||
| 'l': true, | |||||
| 'm': true, | |||||
| 'n': true, | |||||
| 'o': true, | |||||
| 'p': true, | |||||
| 'q': true, | |||||
| 'r': true, | |||||
| 's': true, | |||||
| 't': true, | |||||
| 'u': true, | |||||
| 'v': true, | |||||
| 'w': true, | |||||
| 'x': true, | |||||
| 'y': true, | |||||
| 'z': true, | |||||
| '|': true, | |||||
| '~': true, | |||||
| } | } | ||||
| // skipSpace returns a slice of the string s with all leading RFC 2616 linear | |||||
| // whitespace removed. | |||||
| func skipSpace(s string) (rest string) { | func skipSpace(s string) (rest string) { | ||||
| i := 0 | i := 0 | ||||
| for ; i < len(s); i++ { | for ; i < len(s); i++ { | ||||
| if octetTypes[s[i]]&isSpaceOctet == 0 { | |||||
| if b := s[i]; b != ' ' && b != '\t' { | |||||
| break | break | ||||
| } | } | ||||
| } | } | ||||
| return s[i:] | return s[i:] | ||||
| } | } | ||||
| // nextToken returns the leading RFC 2616 token of s and the string following | |||||
| // the token. | |||||
| func nextToken(s string) (token, rest string) { | func nextToken(s string) (token, rest string) { | ||||
| i := 0 | i := 0 | ||||
| for ; i < len(s); i++ { | for ; i < len(s); i++ { | ||||
| if octetTypes[s[i]]&isTokenOctet == 0 { | |||||
| if !isTokenOctet[s[i]] { | |||||
| break | break | ||||
| } | } | ||||
| } | } | ||||
| return s[:i], s[i:] | return s[:i], s[i:] | ||||
| } | } | ||||
| // nextTokenOrQuoted returns the leading token or quoted string per RFC 2616 | |||||
| // and the string following the token or quoted string. | |||||
| func nextTokenOrQuoted(s string) (value string, rest string) { | func nextTokenOrQuoted(s string) (value string, rest string) { | ||||
| if !strings.HasPrefix(s, "\"") { | if !strings.HasPrefix(s, "\"") { | ||||
| return nextToken(s) | return nextToken(s) | ||||
| @@ -128,7 +173,8 @@ func nextTokenOrQuoted(s string) (value string, rest string) { | |||||
| return "", "" | return "", "" | ||||
| } | } | ||||
| // equalASCIIFold returns true if s is equal to t with ASCII case folding. | |||||
| // equalASCIIFold returns true if s is equal to t with ASCII case folding as | |||||
| // defined in RFC 4790. | |||||
| func equalASCIIFold(s, t string) bool { | func equalASCIIFold(s, t string) bool { | ||||
| for s != "" && t != "" { | for s != "" && t != "" { | ||||
| sr, size := utf8.DecodeRuneInString(s) | sr, size := utf8.DecodeRuneInString(s) | ||||
| @@ -60,7 +60,7 @@ github.com/google/uuid | |||||
| github.com/googleapis/gnostic/compiler | github.com/googleapis/gnostic/compiler | ||||
| github.com/googleapis/gnostic/extensions | github.com/googleapis/gnostic/extensions | ||||
| github.com/googleapis/gnostic/openapiv2 | github.com/googleapis/gnostic/openapiv2 | ||||
| # github.com/gorilla/websocket v1.4.0 | |||||
| # github.com/gorilla/websocket v1.4.2 | |||||
| ## explicit | ## explicit | ||||
| github.com/gorilla/websocket | github.com/gorilla/websocket | ||||
| # github.com/hashicorp/golang-lru v0.5.1 | # github.com/hashicorp/golang-lru v0.5.1 | ||||