|
|
// Copyright 2009 The Go 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 implements a client and server for the WebSocket protocol
// as specified in RFC 6455.
//
// This package currently lacks some features found in an alternative
// and more actively maintained WebSocket package:
//
// https://godoc.org/github.com/gorilla/websocket
//
package websocket // import "golang.org/x/net/websocket"
import ( "bufio" "crypto/tls" "encoding/json" "errors" "io" "io/ioutil" "net" "net/http" "net/url" "sync" "time" )
const ( ProtocolVersionHybi13 = 13 ProtocolVersionHybi = ProtocolVersionHybi13 SupportedProtocolVersion = "13"
ContinuationFrame = 0 TextFrame = 1 BinaryFrame = 2 CloseFrame = 8 PingFrame = 9 PongFrame = 10 UnknownFrame = 255
DefaultMaxPayloadBytes = 32 << 20 // 32MB
)
// ProtocolError represents WebSocket protocol errors.
type ProtocolError struct { ErrorString string }
func (err *ProtocolError) Error() string { return err.ErrorString }
var ( ErrBadProtocolVersion = &ProtocolError{"bad protocol version"} ErrBadScheme = &ProtocolError{"bad scheme"} ErrBadStatus = &ProtocolError{"bad status"} ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"} ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"} ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"} ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"} ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"} ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"} ErrBadFrame = &ProtocolError{"bad frame"} ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"} ErrNotWebSocket = &ProtocolError{"not websocket protocol"} ErrBadRequestMethod = &ProtocolError{"bad method"} ErrNotSupported = &ProtocolError{"not supported"} )
// ErrFrameTooLarge is returned by Codec's Receive method if payload size
// exceeds limit set by Conn.MaxPayloadBytes
var ErrFrameTooLarge = errors.New("websocket: frame payload size exceeds limit")
// Addr is an implementation of net.Addr for WebSocket.
type Addr struct { *url.URL }
// Network returns the network type for a WebSocket, "websocket".
func (addr *Addr) Network() string { return "websocket" }
// Config is a WebSocket configuration
type Config struct { // A WebSocket server address.
Location *url.URL
// A Websocket client origin.
Origin *url.URL
// WebSocket subprotocols.
Protocol []string
// WebSocket protocol version.
Version int
// TLS config for secure WebSocket (wss).
TlsConfig *tls.Config
// Additional header fields to be sent in WebSocket opening handshake.
Header http.Header
// Dialer used when opening websocket connections.
Dialer *net.Dialer
handshakeData map[string]string }
// serverHandshaker is an interface to handle WebSocket server side handshake.
type serverHandshaker interface { // ReadHandshake reads handshake request message from client.
// Returns http response code and error if any.
ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error)
// AcceptHandshake accepts the client handshake request and sends
// handshake response back to client.
AcceptHandshake(buf *bufio.Writer) (err error)
// NewServerConn creates a new WebSocket connection.
NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) }
// frameReader is an interface to read a WebSocket frame.
type frameReader interface { // Reader is to read payload of the frame.
io.Reader
// PayloadType returns payload type.
PayloadType() byte
// HeaderReader returns a reader to read header of the frame.
HeaderReader() io.Reader
// TrailerReader returns a reader to read trailer of the frame.
// If it returns nil, there is no trailer in the frame.
TrailerReader() io.Reader
// Len returns total length of the frame, including header and trailer.
Len() int }
// frameReaderFactory is an interface to creates new frame reader.
type frameReaderFactory interface { NewFrameReader() (r frameReader, err error) }
// frameWriter is an interface to write a WebSocket frame.
type frameWriter interface { // Writer is to write payload of the frame.
io.WriteCloser }
// frameWriterFactory is an interface to create new frame writer.
type frameWriterFactory interface { NewFrameWriter(payloadType byte) (w frameWriter, err error) }
type frameHandler interface { HandleFrame(frame frameReader) (r frameReader, err error) WriteClose(status int) (err error) }
// Conn represents a WebSocket connection.
//
// Multiple goroutines may invoke methods on a Conn simultaneously.
type Conn struct { config *Config request *http.Request
buf *bufio.ReadWriter rwc io.ReadWriteCloser
rio sync.Mutex frameReaderFactory frameReader
wio sync.Mutex frameWriterFactory
frameHandler PayloadType byte defaultCloseStatus int
// MaxPayloadBytes limits the size of frame payload received over Conn
// by Codec's Receive method. If zero, DefaultMaxPayloadBytes is used.
MaxPayloadBytes int }
// Read implements the io.Reader interface:
// it reads data of a frame from the WebSocket connection.
// if msg is not large enough for the frame data, it fills the msg and next Read
// will read the rest of the frame data.
// it reads Text frame or Binary frame.
func (ws *Conn) Read(msg []byte) (n int, err error) { ws.rio.Lock() defer ws.rio.Unlock() again: if ws.frameReader == nil { frame, err := ws.frameReaderFactory.NewFrameReader() if err != nil { return 0, err } ws.frameReader, err = ws.frameHandler.HandleFrame(frame) if err != nil { return 0, err } if ws.frameReader == nil { goto again } } n, err = ws.frameReader.Read(msg) if err == io.EOF { if trailer := ws.frameReader.TrailerReader(); trailer != nil { io.Copy(ioutil.Discard, trailer) } ws.frameReader = nil goto again } return n, err }
// Write implements the io.Writer interface:
// it writes data as a frame to the WebSocket connection.
func (ws *Conn) Write(msg []byte) (n int, err error) { ws.wio.Lock() defer ws.wio.Unlock() w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType) if err != nil { return 0, err } n, err = w.Write(msg) w.Close() return n, err }
// Close implements the io.Closer interface.
func (ws *Conn) Close() error { err := ws.frameHandler.WriteClose(ws.defaultCloseStatus) err1 := ws.rwc.Close() if err != nil { return err } return err1 }
func (ws *Conn) IsClientConn() bool { return ws.request == nil } func (ws *Conn) IsServerConn() bool { return ws.request != nil }
// LocalAddr returns the WebSocket Origin for the connection for client, or
// the WebSocket location for server.
func (ws *Conn) LocalAddr() net.Addr { if ws.IsClientConn() { return &Addr{ws.config.Origin} } return &Addr{ws.config.Location} }
// RemoteAddr returns the WebSocket location for the connection for client, or
// the Websocket Origin for server.
func (ws *Conn) RemoteAddr() net.Addr { if ws.IsClientConn() { return &Addr{ws.config.Location} } return &Addr{ws.config.Origin} }
var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn")
// SetDeadline sets the connection's network read & write deadlines.
func (ws *Conn) SetDeadline(t time.Time) error { if conn, ok := ws.rwc.(net.Conn); ok { return conn.SetDeadline(t) } return errSetDeadline }
// SetReadDeadline sets the connection's network read deadline.
func (ws *Conn) SetReadDeadline(t time.Time) error { if conn, ok := ws.rwc.(net.Conn); ok { return conn.SetReadDeadline(t) } return errSetDeadline }
// SetWriteDeadline sets the connection's network write deadline.
func (ws *Conn) SetWriteDeadline(t time.Time) error { if conn, ok := ws.rwc.(net.Conn); ok { return conn.SetWriteDeadline(t) } return errSetDeadline }
// Config returns the WebSocket config.
func (ws *Conn) Config() *Config { return ws.config }
// Request returns the http request upgraded to the WebSocket.
// It is nil for client side.
func (ws *Conn) Request() *http.Request { return ws.request }
// Codec represents a symmetric pair of functions that implement a codec.
type Codec struct { Marshal func(v interface{}) (data []byte, payloadType byte, err error) Unmarshal func(data []byte, payloadType byte, v interface{}) (err error) }
// Send sends v marshaled by cd.Marshal as single frame to ws.
func (cd Codec) Send(ws *Conn, v interface{}) (err error) { data, payloadType, err := cd.Marshal(v) if err != nil { return err } ws.wio.Lock() defer ws.wio.Unlock() w, err := ws.frameWriterFactory.NewFrameWriter(payloadType) if err != nil { return err } _, err = w.Write(data) w.Close() return err }
// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores
// in v. The whole frame payload is read to an in-memory buffer; max size of
// payload is defined by ws.MaxPayloadBytes. If frame payload size exceeds
// limit, ErrFrameTooLarge is returned; in this case frame is not read off wire
// completely. The next call to Receive would read and discard leftover data of
// previous oversized frame before processing next frame.
func (cd Codec) Receive(ws *Conn, v interface{}) (err error) { ws.rio.Lock() defer ws.rio.Unlock() if ws.frameReader != nil { _, err = io.Copy(ioutil.Discard, ws.frameReader) if err != nil { return err } ws.frameReader = nil } again: frame, err := ws.frameReaderFactory.NewFrameReader() if err != nil { return err } frame, err = ws.frameHandler.HandleFrame(frame) if err != nil { return err } if frame == nil { goto again } maxPayloadBytes := ws.MaxPayloadBytes if maxPayloadBytes == 0 { maxPayloadBytes = DefaultMaxPayloadBytes } if hf, ok := frame.(*hybiFrameReader); ok && hf.header.Length > int64(maxPayloadBytes) { // payload size exceeds limit, no need to call Unmarshal
//
// set frameReader to current oversized frame so that
// the next call to this function can drain leftover
// data before processing the next frame
ws.frameReader = frame return ErrFrameTooLarge } payloadType := frame.PayloadType() data, err := ioutil.ReadAll(frame) if err != nil { return err } return cd.Unmarshal(data, payloadType, v) }
func marshal(v interface{}) (msg []byte, payloadType byte, err error) { switch data := v.(type) { case string: return []byte(data), TextFrame, nil case []byte: return data, BinaryFrame, nil } return nil, UnknownFrame, ErrNotSupported }
func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) { switch data := v.(type) { case *string: *data = string(msg) return nil case *[]byte: *data = msg return nil } return ErrNotSupported }
/* Message is a codec to send/receive text/binary data in a frame on WebSocket connection. To send/receive text frame, use string type. To send/receive binary frame, use []byte type.
Trivial usage:
import "websocket"
// receive text frame
var message string websocket.Message.Receive(ws, &message)
// send text frame
message = "hello" websocket.Message.Send(ws, message)
// receive binary frame
var data []byte websocket.Message.Receive(ws, &data)
// send binary frame
data = []byte{0, 1, 2} websocket.Message.Send(ws, data)
*/ var Message = Codec{marshal, unmarshal}
func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) { msg, err = json.Marshal(v) return msg, TextFrame, err }
func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) { return json.Unmarshal(msg, v) }
/* JSON is a codec to send/receive JSON data in a frame from a WebSocket connection.
Trivial usage:
import "websocket"
type T struct { Msg string Count int }
// receive JSON type T
var data T websocket.JSON.Receive(ws, &data)
// send JSON type T
websocket.JSON.Send(ws, data) */ var JSON = Codec{jsonMarshal, jsonUnmarshal}
|