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 ( | |||
| 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/onsi/ginkgo v1.11.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/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 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.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/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= | |||
| @@ -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 | |||
| [](https://godoc.org/github.com/gorilla/websocket) | |||
| [](https://circleci.com/gh/gorilla/websocket) | |||
| Gorilla WebSocket is a [Go](http://golang.org/) implementation of the | |||
| [WebSocket](http://www.rfc-editor.org/rfc/rfc6455.txt) protocol. | |||
| [](https://travis-ci.org/gorilla/websocket) | |||
| [](https://godoc.org/github.com/gorilla/websocket) | |||
| ### 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) | |||
| * [Command example](https://github.com/gorilla/websocket/tree/master/examples/command) | |||
| * [Client and server example](https://github.com/gorilla/websocket/tree/master/examples/echo) | |||
| @@ -27,7 +27,7 @@ package API is stable. | |||
| ### Protocol Compliance | |||
| 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). | |||
| ### Gorilla WebSocket compared with other packages | |||
| @@ -40,7 +40,7 @@ subdirectory](https://github.com/gorilla/websocket/tree/master/examples/autobahn | |||
| </tr> | |||
| <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>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> | |||
| @@ -70,7 +70,7 @@ type Dialer struct { | |||
| // HandshakeTimeout specifies the duration for the handshake to complete. | |||
| 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 | |||
| // do not limit the size of the messages that can be sent or received. | |||
| ReadBufferSize, WriteBufferSize int | |||
| @@ -140,7 +140,7 @@ var nilDialer = *DefaultDialer | |||
| // Use the response.Header to get the selected subprotocol | |||
| // (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 | |||
| // non-nil *http.Response so that callers can handle redirects, authentication, | |||
| @@ -244,8 +244,8 @@ type Conn struct { | |||
| subprotocol string | |||
| // 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 | |||
| writeBufSize int | |||
| writeDeadline time.Time | |||
| @@ -260,10 +260,12 @@ type Conn struct { | |||
| newCompressionWriter func(io.WriteCloser, int) io.WriteCloser | |||
| // 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. | |||
| readLength int64 // 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) | |||
| } | |||
| mu := make(chan bool, 1) | |||
| mu <- true | |||
| mu := make(chan struct{}, 1) | |||
| mu <- struct{}{} | |||
| c := &Conn{ | |||
| isServer: isServer, | |||
| br: br, | |||
| @@ -320,6 +322,17 @@ func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int, | |||
| 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. | |||
| func (c *Conn) Subprotocol() string { | |||
| 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 { | |||
| <-c.mu | |||
| defer func() { c.mu <- true }() | |||
| defer func() { c.mu <- struct{}{} }() | |||
| c.writeErrMu.Lock() | |||
| err := c.writeErr | |||
| @@ -416,7 +429,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er | |||
| maskBytes(key, 0, buf[6:]) | |||
| } | |||
| d := time.Hour * 1000 | |||
| d := 1000 * time.Hour | |||
| if !deadline.IsZero() { | |||
| d = deadline.Sub(time.Now()) | |||
| if d < 0 { | |||
| @@ -431,7 +444,7 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er | |||
| case <-timer.C: | |||
| return errWriteTimeout | |||
| } | |||
| defer func() { c.mu <- true }() | |||
| defer func() { c.mu <- struct{}{} }() | |||
| c.writeErrMu.Lock() | |||
| err := c.writeErr | |||
| @@ -451,7 +464,8 @@ func (c *Conn) WriteControl(messageType int, data []byte, deadline time.Time) er | |||
| 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 | |||
| // probably better to return an error in this situation, but we cannot | |||
| // change this without breaking existing applications. | |||
| @@ -471,6 +485,10 @@ func (c *Conn) prepWrite(messageType int) error { | |||
| return err | |||
| } | |||
| mw.c = c | |||
| mw.frameType = messageType | |||
| mw.pos = maxFrameHeaderSize | |||
| if c.writeBuf == nil { | |||
| wpd, ok := c.writePool.Get().(writePoolData) | |||
| if ok { | |||
| @@ -491,16 +509,11 @@ func (c *Conn) prepWrite(messageType int) error { | |||
| // All message types (TextMessage, BinaryMessage, CloseMessage, PingMessage and | |||
| // PongMessage) are supported. | |||
| 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 | |||
| } | |||
| mw := &messageWriter{ | |||
| c: c, | |||
| frameType: messageType, | |||
| pos: maxFrameHeaderSize, | |||
| } | |||
| c.writer = mw | |||
| c.writer = &mw | |||
| if c.newCompressionWriter != nil && c.enableWriteCompression && isData(messageType) { | |||
| w := c.newCompressionWriter(c.writer, c.compressionLevel) | |||
| mw.compress = true | |||
| @@ -517,10 +530,16 @@ type messageWriter struct { | |||
| err error | |||
| } | |||
| func (w *messageWriter) fatal(err error) error { | |||
| func (w *messageWriter) endMessage(err error) error { | |||
| 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 | |||
| } | |||
| @@ -534,7 +553,7 @@ func (w *messageWriter) flushFrame(final bool, extra []byte) error { | |||
| // Check for invalid control frames. | |||
| if isControl(w.frameType) && | |||
| (!final || length > maxControlFramePayloadSize) { | |||
| return w.fatal(errInvalidControlFrame) | |||
| return w.endMessage(errInvalidControlFrame) | |||
| } | |||
| b0 := byte(w.frameType) | |||
| @@ -579,7 +598,7 @@ func (w *messageWriter) flushFrame(final bool, extra []byte) error { | |||
| copy(c.writeBuf[maxFrameHeaderSize-4:], key[:]) | |||
| maskBytes(key, 0, c.writeBuf[maxFrameHeaderSize:w.pos]) | |||
| 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 | |||
| if err != nil { | |||
| return w.fatal(err) | |||
| return w.endMessage(err) | |||
| } | |||
| if final { | |||
| c.writer = nil | |||
| if c.writePool != nil { | |||
| c.writePool.Put(writePoolData{buf: c.writeBuf}) | |||
| c.writeBuf = nil | |||
| } | |||
| w.endMessage(errWriteClosed) | |||
| return nil | |||
| } | |||
| @@ -706,11 +721,7 @@ func (w *messageWriter) Close() error { | |||
| if w.err != nil { | |||
| 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. | |||
| @@ -742,10 +753,10 @@ func (c *Conn) WriteMessage(messageType int, data []byte) error { | |||
| if c.isServer && (c.newCompressionWriter == nil || !c.enableWriteCompression) { | |||
| // 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 | |||
| } | |||
| mw := messageWriter{c: c, frameType: messageType, pos: maxFrameHeaderSize} | |||
| n := copy(c.writeBuf[mw.pos:], data) | |||
| mw.pos += n | |||
| data = data[n:] | |||
| @@ -792,7 +803,7 @@ func (c *Conn) advanceFrame() (int, error) { | |||
| final := p[0]&finalBit != 0 | |||
| frameType := int(p[0] & 0xf) | |||
| mask := p[1]&maskBit != 0 | |||
| c.readRemaining = int64(p[1] & 0x7f) | |||
| c.setReadRemaining(int64(p[1] & 0x7f)) | |||
| c.readDecompress = false | |||
| 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)) | |||
| } | |||
| // 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 { | |||
| case 126: | |||
| @@ -834,13 +855,19 @@ func (c *Conn) advanceFrame() (int, error) { | |||
| if err != nil { | |||
| 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: | |||
| p, err := c.read(8) | |||
| if err != nil { | |||
| 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. | |||
| @@ -863,6 +890,12 @@ func (c *Conn) advanceFrame() (int, error) { | |||
| if frameType == continuationFrame || frameType == TextMessage || frameType == BinaryMessage { | |||
| 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 { | |||
| c.WriteControl(CloseMessage, FormatCloseMessage(CloseMessageTooBig, ""), time.Now().Add(writeWait)) | |||
| return noFrame, ErrReadLimit | |||
| @@ -876,7 +909,7 @@ func (c *Conn) advanceFrame() (int, error) { | |||
| var payload []byte | |||
| if c.readRemaining > 0 { | |||
| payload, err = c.read(int(c.readRemaining)) | |||
| c.readRemaining = 0 | |||
| c.setReadRemaining(0) | |||
| if err != nil { | |||
| return noFrame, err | |||
| } | |||
| @@ -949,6 +982,7 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) { | |||
| c.readErr = hideTempErr(err) | |||
| break | |||
| } | |||
| if frameType == TextMessage || frameType == BinaryMessage { | |||
| c.messageReader = &messageReader{c} | |||
| c.reader = c.messageReader | |||
| @@ -989,7 +1023,9 @@ func (r *messageReader) Read(b []byte) (int, error) { | |||
| if c.isServer { | |||
| 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 { | |||
| c.readErr = errUnexpectedEOF | |||
| } | |||
| @@ -1041,7 +1077,7 @@ func (c *Conn) SetReadDeadline(t time.Time) error { | |||
| 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 | |||
| // and returns ErrReadLimit to the application. | |||
| func (c *Conn) SetReadLimit(limit int64) { | |||
| @@ -151,6 +151,53 @@ | |||
| // checking. The application is responsible for checking the Origin header | |||
| // 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 | |||
| // | |||
| // 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. | |||
| // TODO: Refactor code in conn.go to allow more direct construction of | |||
| // the frame. | |||
| mu := make(chan bool, 1) | |||
| mu <- true | |||
| mu := make(chan struct{}, 1) | |||
| mu <- struct{}{} | |||
| var nc prepareConn | |||
| c := &Conn{ | |||
| conn: &nc, | |||
| @@ -22,18 +22,18 @@ func (fn netDialerFunc) Dial(network, addr string) (net.Conn, error) { | |||
| func init() { | |||
| 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 { | |||
| 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) { | |||
| hostPort, _ := hostPortNoPort(hpd.proxyURL) | |||
| conn, err := hpd.fowardDial(network, hostPort) | |||
| conn, err := hpd.forwardDial(network, hostPort) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| @@ -27,7 +27,7 @@ type Upgrader struct { | |||
| // HandshakeTimeout specifies the duration for the handshake to complete. | |||
| 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 | |||
| // I/O buffer sizes do not limit the size of the messages that can be sent | |||
| // or received. | |||
| @@ -153,7 +153,7 @@ func (u *Upgrader) Upgrade(w http.ResponseWriter, r *http.Request, responseHeade | |||
| challengeKey := r.Header.Get("Sec-Websocket-Key") | |||
| 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) | |||
| @@ -31,68 +31,113 @@ func generateChallengeKey() (string, error) { | |||
| 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) { | |||
| i := 0 | |||
| for ; i < len(s); i++ { | |||
| if octetTypes[s[i]]&isSpaceOctet == 0 { | |||
| if b := s[i]; b != ' ' && b != '\t' { | |||
| break | |||
| } | |||
| } | |||
| 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) { | |||
| i := 0 | |||
| for ; i < len(s); i++ { | |||
| if octetTypes[s[i]]&isTokenOctet == 0 { | |||
| if !isTokenOctet[s[i]] { | |||
| break | |||
| } | |||
| } | |||
| 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) { | |||
| if !strings.HasPrefix(s, "\"") { | |||
| return nextToken(s) | |||
| @@ -128,7 +173,8 @@ func nextTokenOrQuoted(s string) (value string, rest string) { | |||
| 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 { | |||
| for s != "" && t != "" { | |||
| sr, size := utf8.DecodeRuneInString(s) | |||
| @@ -60,7 +60,7 @@ github.com/google/uuid | |||
| github.com/googleapis/gnostic/compiler | |||
| github.com/googleapis/gnostic/extensions | |||
| github.com/googleapis/gnostic/openapiv2 | |||
| # github.com/gorilla/websocket v1.4.0 | |||
| # github.com/gorilla/websocket v1.4.2 | |||
| ## explicit | |||
| github.com/gorilla/websocket | |||
| # github.com/hashicorp/golang-lru v0.5.1 | |||