|
@@ -214,6 +214,7 @@ type Conn struct {
|
|
|
writeFrameType int // type of the current frame.
|
|
writeFrameType int // type of the current frame.
|
|
|
writeSeq int // incremented to invalidate message writers.
|
|
writeSeq int // incremented to invalidate message writers.
|
|
|
writeDeadline time.Time
|
|
writeDeadline time.Time
|
|
|
|
|
+ isWriting bool // for best-effort concurrent write detection
|
|
|
|
|
|
|
|
// Read fields
|
|
// Read fields
|
|
|
readErr error
|
|
readErr error
|
|
@@ -227,6 +228,7 @@ type Conn struct {
|
|
|
readMaskKey [4]byte
|
|
readMaskKey [4]byte
|
|
|
handlePong func(string) error
|
|
handlePong func(string) error
|
|
|
handlePing func(string) error
|
|
handlePing func(string) error
|
|
|
|
|
+ readErrCount int
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn {
|
|
func newConn(conn net.Conn, isServer bool, readBufferSize, writeBufferSize int) *Conn {
|
|
@@ -440,9 +442,22 @@ func (c *Conn) flushFrame(final bool, extra []byte) error {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Write the buffers to the connection.
|
|
|
|
|
|
|
+ // Write the buffers to the connection with best-effort detection of
|
|
|
|
|
+ // concurrent writes. See the concurrency section in the package
|
|
|
|
|
+ // documentation for more info.
|
|
|
|
|
+
|
|
|
|
|
+ if c.isWriting {
|
|
|
|
|
+ panic("concurrent write to websocket connection")
|
|
|
|
|
+ }
|
|
|
|
|
+ c.isWriting = true
|
|
|
|
|
+
|
|
|
c.writeErr = c.write(c.writeFrameType, c.writeDeadline, c.writeBuf[framePos:c.writePos], extra)
|
|
c.writeErr = c.write(c.writeFrameType, c.writeDeadline, c.writeBuf[framePos:c.writePos], extra)
|
|
|
|
|
|
|
|
|
|
+ if !c.isWriting {
|
|
|
|
|
+ panic("concurrent write to websocket connection")
|
|
|
|
|
+ }
|
|
|
|
|
+ c.isWriting = false
|
|
|
|
|
+
|
|
|
// Setup for next frame.
|
|
// Setup for next frame.
|
|
|
c.writePos = maxFrameHeaderSize
|
|
c.writePos = maxFrameHeaderSize
|
|
|
c.writeFrameType = continuationFrame
|
|
c.writeFrameType = continuationFrame
|
|
@@ -734,7 +749,7 @@ func (c *Conn) advanceFrame() (int, error) {
|
|
|
closeCode = int(binary.BigEndian.Uint16(payload))
|
|
closeCode = int(binary.BigEndian.Uint16(payload))
|
|
|
closeText = string(payload[2:])
|
|
closeText = string(payload[2:])
|
|
|
}
|
|
}
|
|
|
- c.WriteControl(CloseMessage, echoMessage, time.Now().Add(writeWait))
|
|
|
|
|
|
|
+ c.WriteControl(CloseMessage, echoMessage, time.Now().Add(writeWait))
|
|
|
return noFrame, &CloseError{Code: closeCode, Text: closeText}
|
|
return noFrame, &CloseError{Code: closeCode, Text: closeText}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -752,9 +767,10 @@ func (c *Conn) handleProtocolError(message string) error {
|
|
|
// There can be at most one open reader on a connection. NextReader discards
|
|
// There can be at most one open reader on a connection. NextReader discards
|
|
|
// the previous message if the application has not already consumed it.
|
|
// the previous message if the application has not already consumed it.
|
|
|
//
|
|
//
|
|
|
-// Errors returned from NextReader are permanent. If NextReader returns a
|
|
|
|
|
-// non-nil error, then all subsequent calls to NextReader return the same
|
|
|
|
|
-// error.
|
|
|
|
|
|
|
+// Applications must break out of the application's read loop when this method
|
|
|
|
|
+// returns a non-nil error value. Errors returned from this method are
|
|
|
|
|
+// permanent. Once this method returns a non-nil error, all subsequent calls to
|
|
|
|
|
+// this method return the same error.
|
|
|
func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
|
func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
|
|
|
|
|
|
|
c.readSeq++
|
|
c.readSeq++
|
|
@@ -770,6 +786,15 @@ func (c *Conn) NextReader() (messageType int, r io.Reader, err error) {
|
|
|
return frameType, messageReader{c, c.readSeq}, nil
|
|
return frameType, messageReader{c, c.readSeq}, nil
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // Applications that do handle the error returned from this method spin in
|
|
|
|
|
+ // tight loop on connection failure. To help application developers detect
|
|
|
|
|
+ // this error, panic on repeated reads to the failed connection.
|
|
|
|
|
+ c.readErrCount++
|
|
|
|
|
+ if c.readErrCount >= 1000 {
|
|
|
|
|
+ panic("repeated read on failed websocket connection")
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
return noFrame, nil, c.readErr
|
|
return noFrame, nil, c.readErr
|
|
|
}
|
|
}
|
|
|
|
|
|