2018-09-29 12:36:47 +00:00
|
|
|
package websocket
|
2018-05-03 17:31:09 +00:00
|
|
|
|
|
|
|
import (
|
2018-09-29 12:36:47 +00:00
|
|
|
"encoding/binary"
|
2018-05-03 17:31:09 +00:00
|
|
|
"io"
|
2018-09-29 12:36:47 +00:00
|
|
|
"unicode/utf8"
|
2018-05-03 17:31:09 +00:00
|
|
|
)
|
|
|
|
|
2021-05-14 15:19:02 +00:00
|
|
|
// constant error
|
|
|
|
type constErr string
|
|
|
|
|
|
|
|
func (c constErr) Error() string { return string(c) }
|
|
|
|
|
|
|
|
const (
|
2021-05-14 14:47:02 +00:00
|
|
|
// ErrUnmaskedFrame error
|
2021-06-15 20:13:36 +00:00
|
|
|
ErrUnmaskedFrame constErr = "Received unmasked frame"
|
2021-05-14 14:47:02 +00:00
|
|
|
// ErrTooLongControlFrame error
|
2021-06-15 20:13:36 +00:00
|
|
|
ErrTooLongControlFrame constErr = "Received a control frame that is fragmented or too long"
|
2021-05-14 14:47:02 +00:00
|
|
|
// ErrInvalidFragment error
|
2021-06-15 20:13:36 +00:00
|
|
|
ErrInvalidFragment constErr = "Received invalid fragmentation"
|
2021-05-14 14:47:02 +00:00
|
|
|
// ErrUnexpectedContinuation error
|
2021-06-15 20:13:36 +00:00
|
|
|
ErrUnexpectedContinuation constErr = "Received unexpected continuation frame"
|
2021-05-14 14:47:02 +00:00
|
|
|
// ErrInvalidSize error
|
2021-06-15 20:13:36 +00:00
|
|
|
ErrInvalidSize constErr = "Received invalid payload size"
|
2021-05-14 14:47:02 +00:00
|
|
|
// ErrInvalidPayload error
|
2021-06-15 20:13:36 +00:00
|
|
|
ErrInvalidPayload constErr = "Received invalid utf8 payload"
|
2021-05-14 14:47:02 +00:00
|
|
|
// ErrInvalidCloseStatus error
|
2021-06-15 20:13:36 +00:00
|
|
|
ErrInvalidCloseStatus constErr = "Received invalid close status"
|
2021-05-14 14:47:02 +00:00
|
|
|
// ErrInvalidOpCode error
|
2021-06-15 20:13:36 +00:00
|
|
|
ErrInvalidOpCode constErr = "Received invalid OpCode"
|
2021-05-14 14:47:02 +00:00
|
|
|
// ErrReservedBits error
|
2021-06-15 20:13:36 +00:00
|
|
|
ErrReservedBits constErr = "Received reserved bits"
|
2021-05-14 14:47:02 +00:00
|
|
|
// ErrCloseFrame error
|
2021-06-15 20:13:36 +00:00
|
|
|
ErrCloseFrame constErr = "Received close Frame"
|
2021-05-14 14:47:02 +00:00
|
|
|
)
|
2018-05-08 09:15:01 +00:00
|
|
|
|
2018-05-03 17:31:09 +00:00
|
|
|
// Maximum Header Size = Final/OpCode + isMask/Length + Length + Mask
|
|
|
|
const maximumHeaderSize = 1 + 1 + 8 + 4
|
2018-05-05 23:05:38 +00:00
|
|
|
const maxWriteChunk = 0x7fff
|
2018-05-03 17:31:09 +00:00
|
|
|
|
2021-05-14 14:47:02 +00:00
|
|
|
// MessageError lists websocket close statuses
|
2018-05-03 17:31:09 +00:00
|
|
|
type MessageError uint16
|
|
|
|
|
|
|
|
const (
|
2021-05-14 14:47:02 +00:00
|
|
|
// None used when there is no error
|
|
|
|
None MessageError = 0
|
|
|
|
// Normal error
|
|
|
|
Normal MessageError = 1000
|
|
|
|
// GoingAway error
|
|
|
|
GoingAway MessageError = 1001
|
|
|
|
// ProtocolError error
|
|
|
|
ProtocolError MessageError = 1002
|
|
|
|
// UnacceptableOpCode error
|
|
|
|
UnacceptableOpCode MessageError = 1003
|
|
|
|
// InvalidPayload error
|
|
|
|
InvalidPayload MessageError = 1007 // utf8
|
|
|
|
// MessageTooLarge error
|
|
|
|
MessageTooLarge MessageError = 1009
|
2018-05-03 17:31:09 +00:00
|
|
|
)
|
|
|
|
|
2021-05-14 14:47:02 +00:00
|
|
|
// MessageType lists websocket message types
|
2018-05-03 17:31:09 +00:00
|
|
|
type MessageType byte
|
|
|
|
|
|
|
|
const (
|
2021-05-14 14:47:02 +00:00
|
|
|
// Continuation message type
|
|
|
|
Continuation MessageType = 0x00
|
|
|
|
// Text message type
|
|
|
|
Text MessageType = 0x01
|
|
|
|
// Binary message type
|
|
|
|
Binary MessageType = 0x02
|
|
|
|
// Close message type
|
|
|
|
Close MessageType = 0x08
|
|
|
|
// Ping message type
|
|
|
|
Ping MessageType = 0x09
|
|
|
|
// Pong message type
|
|
|
|
Pong MessageType = 0x0a
|
2018-09-29 12:36:47 +00:00
|
|
|
)
|
2018-05-03 17:31:09 +00:00
|
|
|
|
2021-05-14 14:47:02 +00:00
|
|
|
// Message is a websocket message
|
2018-05-03 17:31:09 +00:00
|
|
|
type Message struct {
|
2018-05-05 22:18:01 +00:00
|
|
|
Final bool
|
2018-05-03 17:31:09 +00:00
|
|
|
Type MessageType
|
|
|
|
Size uint
|
2018-05-05 22:18:01 +00:00
|
|
|
Data []byte
|
2018-05-03 17:31:09 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// ReadFrom reads a message from a reader
|
|
|
|
//
|
|
|
|
// implements io.ReaderFrom
|
|
|
|
func (m *Message) ReadFrom(reader io.Reader) (int64, error) {
|
|
|
|
var (
|
|
|
|
read int64
|
|
|
|
err error
|
|
|
|
tmpBuf []byte
|
|
|
|
mask []byte
|
|
|
|
cursor int
|
|
|
|
)
|
|
|
|
|
|
|
|
// byte 1: FIN and OpCode
|
2018-05-03 17:31:09 +00:00
|
|
|
tmpBuf = make([]byte, 1)
|
2021-06-15 22:04:09 +00:00
|
|
|
read += int64(len(tmpBuf))
|
2018-05-08 09:52:34 +00:00
|
|
|
err = readBytes(reader, tmpBuf)
|
2018-09-29 12:36:47 +00:00
|
|
|
if err != nil {
|
2021-06-15 22:04:09 +00:00
|
|
|
return read, err
|
2018-09-29 12:36:47 +00:00
|
|
|
}
|
2018-05-03 17:31:09 +00:00
|
|
|
|
2018-05-08 09:52:34 +00:00
|
|
|
// check reserved bits
|
2018-09-29 12:36:47 +00:00
|
|
|
if tmpBuf[0]&0x70 != 0 {
|
2021-06-15 22:04:09 +00:00
|
|
|
return read, ErrReservedBits
|
2018-05-08 09:52:34 +00:00
|
|
|
}
|
|
|
|
|
2018-09-29 12:36:47 +00:00
|
|
|
m.Final = bool(tmpBuf[0]&0x80 == 0x80)
|
|
|
|
m.Type = MessageType(tmpBuf[0] & 0x0f)
|
2018-05-03 17:31:09 +00:00
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// byte 2: mask and length[0]
|
2018-05-03 17:31:09 +00:00
|
|
|
tmpBuf = make([]byte, 1)
|
2021-06-15 22:04:09 +00:00
|
|
|
read += int64(len(tmpBuf))
|
2018-05-08 09:52:34 +00:00
|
|
|
err = readBytes(reader, tmpBuf)
|
2018-09-29 12:36:47 +00:00
|
|
|
if err != nil {
|
2021-06-15 22:04:09 +00:00
|
|
|
return read, err
|
2018-09-29 12:36:47 +00:00
|
|
|
}
|
2018-05-03 17:31:09 +00:00
|
|
|
|
|
|
|
// if mask, byte array not nil
|
2018-09-29 12:36:47 +00:00
|
|
|
if tmpBuf[0]&0x80 == 0x80 {
|
2018-05-03 17:31:09 +00:00
|
|
|
mask = make([]byte, 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
// payload length
|
2018-09-29 12:36:47 +00:00
|
|
|
m.Size = uint(tmpBuf[0] & 0x7f)
|
2018-05-03 17:31:09 +00:00
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// extended payload
|
2018-05-03 17:31:09 +00:00
|
|
|
if m.Size == 127 {
|
|
|
|
tmpBuf = make([]byte, 8)
|
2021-06-15 22:04:09 +00:00
|
|
|
read += int64(len(tmpBuf))
|
2018-05-08 09:52:34 +00:00
|
|
|
err := readBytes(reader, tmpBuf)
|
2018-09-29 12:36:47 +00:00
|
|
|
if err != nil {
|
2021-06-15 22:04:09 +00:00
|
|
|
return read, err
|
2018-09-29 12:36:47 +00:00
|
|
|
}
|
|
|
|
m.Size = uint(binary.BigEndian.Uint64(tmpBuf))
|
2018-05-03 17:31:09 +00:00
|
|
|
|
|
|
|
} else if m.Size == 126 {
|
|
|
|
tmpBuf = make([]byte, 2)
|
2021-06-15 22:04:09 +00:00
|
|
|
read += int64(len(tmpBuf))
|
2018-05-08 09:52:34 +00:00
|
|
|
err := readBytes(reader, tmpBuf)
|
2018-09-29 12:36:47 +00:00
|
|
|
if err != nil {
|
2021-06-15 22:04:09 +00:00
|
|
|
return read, err
|
2018-09-29 12:36:47 +00:00
|
|
|
}
|
|
|
|
m.Size = uint(binary.BigEndian.Uint16(tmpBuf))
|
2018-05-03 17:31:09 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// masking key
|
2018-05-03 17:31:09 +00:00
|
|
|
if mask != nil {
|
|
|
|
tmpBuf = make([]byte, 4)
|
2021-06-15 22:04:09 +00:00
|
|
|
read += int64(len(tmpBuf))
|
2018-05-08 09:52:34 +00:00
|
|
|
err := readBytes(reader, tmpBuf)
|
2018-09-29 12:36:47 +00:00
|
|
|
if err != nil {
|
2021-06-15 22:04:09 +00:00
|
|
|
return read, err
|
2018-09-29 12:36:47 +00:00
|
|
|
}
|
2018-05-03 17:31:09 +00:00
|
|
|
mask = make([]byte, 4)
|
|
|
|
copy(mask, tmpBuf)
|
|
|
|
}
|
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// read payload by chunks
|
2018-05-03 17:31:09 +00:00
|
|
|
m.Data = make([]byte, int(m.Size))
|
|
|
|
|
|
|
|
cursor = 0
|
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// while data to read
|
2018-05-03 17:31:09 +00:00
|
|
|
for uint(cursor) < m.Size {
|
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// try to read (at least 1 byte)
|
2018-05-03 17:31:09 +00:00
|
|
|
nbread, err := io.ReadAtLeast(reader, m.Data[cursor:m.Size], 1)
|
|
|
|
if err != nil {
|
2021-06-15 22:04:09 +00:00
|
|
|
return read + int64(cursor) + int64(nbread), err
|
2018-05-03 17:31:09 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// unmask data //
|
2018-05-03 17:31:09 +00:00
|
|
|
if mask != nil {
|
2018-09-29 12:36:47 +00:00
|
|
|
for i, l := cursor, cursor+nbread; i < l; i++ {
|
2018-05-03 17:31:09 +00:00
|
|
|
mi := i % 4 // mask index
|
|
|
|
m.Data[i] = m.Data[i] ^ mask[mi]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cursor += nbread
|
|
|
|
}
|
2021-06-15 22:04:09 +00:00
|
|
|
read += int64(cursor)
|
2018-05-03 17:31:09 +00:00
|
|
|
|
2018-05-05 22:18:01 +00:00
|
|
|
// return error if unmasked frame
|
2018-05-06 12:45:38 +00:00
|
|
|
// we have to fully read it for read buffer to be clean
|
|
|
|
err = nil
|
2018-05-05 22:18:01 +00:00
|
|
|
if mask == nil {
|
2018-05-08 09:15:01 +00:00
|
|
|
err = ErrUnmaskedFrame
|
2018-05-05 22:18:01 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
return read, err
|
2018-05-03 17:31:09 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// WriteTo writes a message frame over a socket
|
|
|
|
//
|
|
|
|
// implements io.WriterTo
|
|
|
|
func (m Message) WriteTo(writer io.Writer) (int64, error) {
|
2018-05-03 17:31:09 +00:00
|
|
|
header := make([]byte, 0, maximumHeaderSize)
|
|
|
|
|
2018-05-05 21:53:00 +00:00
|
|
|
// fix size
|
|
|
|
if uint(len(m.Data)) <= m.Size {
|
2018-09-29 12:36:47 +00:00
|
|
|
m.Size = uint(len(m.Data))
|
2018-05-05 21:53:00 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// byte 0 : FIN + opcode
|
2018-05-05 22:27:45 +00:00
|
|
|
var final byte = 0x80
|
|
|
|
if !m.Final {
|
|
|
|
final = 0
|
|
|
|
}
|
2018-09-29 12:36:47 +00:00
|
|
|
header = append(header, final|byte(m.Type))
|
2018-05-03 17:31:09 +00:00
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// get payload length
|
2018-05-03 17:31:09 +00:00
|
|
|
if m.Size < 126 { // simple
|
2018-09-29 12:36:47 +00:00
|
|
|
header = append(header, byte(m.Size))
|
2018-05-03 17:31:09 +00:00
|
|
|
|
2018-05-07 17:25:54 +00:00
|
|
|
} else if m.Size <= 0xffff { // extended: 16 bits
|
2018-05-03 17:31:09 +00:00
|
|
|
header = append(header, 126)
|
|
|
|
|
|
|
|
buf := make([]byte, 2)
|
|
|
|
binary.BigEndian.PutUint16(buf, uint16(m.Size))
|
|
|
|
header = append(header, buf...)
|
|
|
|
|
2018-05-07 17:25:54 +00:00
|
|
|
} else if m.Size <= 0xffffffffffffffff { // extended: 64 bits
|
2018-05-03 17:31:09 +00:00
|
|
|
header = append(header, 127)
|
|
|
|
|
|
|
|
buf := make([]byte, 8)
|
|
|
|
binary.BigEndian.PutUint64(buf, uint64(m.Size))
|
|
|
|
header = append(header, buf...)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// build write buffer
|
2018-09-29 12:36:47 +00:00
|
|
|
writeBuf := make([]byte, 0, len(header)+int(m.Size))
|
2018-05-05 15:39:39 +00:00
|
|
|
writeBuf = append(writeBuf, header...)
|
2018-05-05 21:53:00 +00:00
|
|
|
writeBuf = append(writeBuf, m.Data[0:m.Size]...)
|
2018-05-03 17:31:09 +00:00
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// write by chunks
|
2018-05-05 23:05:38 +00:00
|
|
|
toWrite := len(header) + int(m.Size)
|
|
|
|
cursor := 0
|
|
|
|
for cursor < toWrite {
|
2018-09-29 12:36:47 +00:00
|
|
|
maxBoundary := cursor + maxWriteChunk
|
2018-05-05 23:05:38 +00:00
|
|
|
if maxBoundary > toWrite {
|
|
|
|
maxBoundary = toWrite
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try to wrote (at max 1024 bytes) //
|
|
|
|
nbwritten, err := writer.Write(writeBuf[cursor:maxBoundary])
|
2018-09-29 12:36:47 +00:00
|
|
|
if err != nil {
|
2021-06-15 22:04:09 +00:00
|
|
|
return int64(nbwritten), err
|
2018-09-29 12:36:47 +00:00
|
|
|
}
|
2018-05-05 23:05:38 +00:00
|
|
|
|
|
|
|
// Update cursor //
|
|
|
|
cursor += nbwritten
|
|
|
|
}
|
2021-06-15 22:04:09 +00:00
|
|
|
return int64(cursor), nil
|
2018-05-08 09:15:01 +00:00
|
|
|
}
|
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// check for message errors with:
|
2018-05-08 09:15:01 +00:00
|
|
|
// (m) the current message
|
|
|
|
// (fragment) whether there is a fragment in construction
|
|
|
|
// returns the message error
|
2018-09-29 12:36:47 +00:00
|
|
|
func (m *Message) check(fragment bool) error {
|
2018-05-08 09:15:01 +00:00
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// invalid first fragment (not TEXT nor BINARY)
|
2021-05-14 14:47:02 +00:00
|
|
|
if !m.Final && !fragment && m.Type != Text && m.Type != Binary {
|
2018-05-08 09:15:01 +00:00
|
|
|
return ErrInvalidFragment
|
|
|
|
}
|
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// waiting fragment but received standalone frame
|
2021-05-14 14:47:02 +00:00
|
|
|
if fragment && m.Type != Continuation && m.Type != Close && m.Type != Ping && m.Type != Pong {
|
2018-05-08 09:15:01 +00:00
|
|
|
return ErrInvalidFragment
|
|
|
|
}
|
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// control frame too long
|
2021-05-14 14:47:02 +00:00
|
|
|
if (m.Type == Close || m.Type == Ping || m.Type == Pong) && (m.Size > 125 || !m.Final) {
|
2018-05-08 09:15:01 +00:00
|
|
|
return ErrTooLongControlFrame
|
|
|
|
}
|
|
|
|
|
|
|
|
switch m.Type {
|
2021-05-14 14:47:02 +00:00
|
|
|
case Continuation:
|
2018-09-29 12:36:47 +00:00
|
|
|
// unexpected continuation
|
|
|
|
if !fragment {
|
|
|
|
return ErrUnexpectedContinuation
|
|
|
|
}
|
|
|
|
return nil
|
2018-05-08 09:15:01 +00:00
|
|
|
|
2021-05-14 14:47:02 +00:00
|
|
|
case Text:
|
2018-09-29 12:36:47 +00:00
|
|
|
if m.Final && !utf8.Valid(m.Data) {
|
|
|
|
return ErrInvalidPayload
|
|
|
|
}
|
|
|
|
return nil
|
2018-05-08 09:15:01 +00:00
|
|
|
|
2021-05-14 14:47:02 +00:00
|
|
|
case Binary:
|
2018-09-29 12:36:47 +00:00
|
|
|
return nil
|
2018-05-08 09:15:01 +00:00
|
|
|
|
2021-05-14 14:47:02 +00:00
|
|
|
case Close:
|
2018-09-29 12:36:47 +00:00
|
|
|
// incomplete code
|
|
|
|
if m.Size == 1 {
|
|
|
|
return ErrInvalidCloseStatus
|
|
|
|
}
|
2018-05-08 09:15:01 +00:00
|
|
|
|
2018-09-29 12:36:47 +00:00
|
|
|
// invalid utf-8 reason
|
|
|
|
if m.Size > 2 && !utf8.Valid(m.Data[2:]) {
|
|
|
|
return ErrInvalidPayload
|
|
|
|
}
|
2018-05-08 09:15:01 +00:00
|
|
|
|
2018-09-29 12:36:47 +00:00
|
|
|
// invalid code
|
|
|
|
if m.Size >= 2 {
|
|
|
|
c := binary.BigEndian.Uint16(m.Data[0:2])
|
2018-05-08 09:15:01 +00:00
|
|
|
|
2018-09-29 12:36:47 +00:00
|
|
|
if c < 1000 || c >= 1004 && c <= 1006 || c >= 1012 && c <= 1016 || c == 1100 || c == 2000 || c == 2999 {
|
|
|
|
return ErrInvalidCloseStatus
|
2018-05-08 09:15:01 +00:00
|
|
|
}
|
2018-09-29 12:36:47 +00:00
|
|
|
}
|
2021-05-14 14:47:02 +00:00
|
|
|
return ErrCloseFrame
|
2018-05-08 09:15:01 +00:00
|
|
|
|
2021-05-14 14:47:02 +00:00
|
|
|
case Ping:
|
2018-09-29 12:36:47 +00:00
|
|
|
return nil
|
2018-05-08 09:15:01 +00:00
|
|
|
|
2021-05-14 14:47:02 +00:00
|
|
|
case Pong:
|
2018-09-29 12:36:47 +00:00
|
|
|
return nil
|
2018-05-08 09:15:01 +00:00
|
|
|
|
2018-09-29 12:36:47 +00:00
|
|
|
default:
|
|
|
|
return ErrInvalidOpCode
|
2018-05-08 09:15:01 +00:00
|
|
|
|
|
|
|
}
|
2018-05-08 09:52:34 +00:00
|
|
|
}
|
|
|
|
|
2018-05-08 10:10:24 +00:00
|
|
|
// readBytes reads from a reader into a byte array
|
|
|
|
// until the byte length is fully filled with data
|
|
|
|
// loops while there is no error
|
|
|
|
//
|
|
|
|
// It manages connections which chunks data
|
2018-05-08 09:52:34 +00:00
|
|
|
func readBytes(reader io.Reader, buffer []byte) error {
|
2021-06-15 22:04:09 +00:00
|
|
|
var (
|
|
|
|
cur = 0
|
|
|
|
len = len(buffer)
|
|
|
|
)
|
2018-05-08 09:52:34 +00:00
|
|
|
|
2021-06-15 22:04:09 +00:00
|
|
|
// read until the full size is read
|
2018-05-08 09:52:34 +00:00
|
|
|
for cur < len {
|
|
|
|
nbread, err := reader.Read(buffer[cur:])
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
cur += nbread
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
|
2018-09-29 12:36:47 +00:00
|
|
|
}
|