123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451 |
- // 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 alternative
- // and more actively maintained WebSocket packages:
- //
- // https://godoc.org/github.com/gorilla/websocket
- // https://godoc.org/nhooyr.io/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
- }
- // IsClientConn reports whether ws is a client-side connection.
- func (ws *Conn) IsClientConn() bool { return ws.request == nil }
- // IsServerConn reports whether ws is a server-side connection.
- 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}
|