1// Copyright 2009 The Go Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style 3// license that can be found in the LICENSE file. 4 5// Package websocket implements a client and server for the WebSocket protocol 6// as specified in RFC 6455. 7// 8// This package currently lacks some features found in alternative 9// and more actively maintained WebSocket packages: 10// 11// https://godoc.org/github.com/gorilla/websocket 12// https://godoc.org/nhooyr.io/websocket 13package websocket // import "golang.org/x/net/websocket" 14 15import ( 16 "bufio" 17 "crypto/tls" 18 "encoding/json" 19 "errors" 20 "io" 21 "io/ioutil" 22 "net" 23 "net/http" 24 "net/url" 25 "sync" 26 "time" 27) 28 29const ( 30 ProtocolVersionHybi13 = 13 31 ProtocolVersionHybi = ProtocolVersionHybi13 32 SupportedProtocolVersion = "13" 33 34 ContinuationFrame = 0 35 TextFrame = 1 36 BinaryFrame = 2 37 CloseFrame = 8 38 PingFrame = 9 39 PongFrame = 10 40 UnknownFrame = 255 41 42 DefaultMaxPayloadBytes = 32 << 20 // 32MB 43) 44 45// ProtocolError represents WebSocket protocol errors. 46type ProtocolError struct { 47 ErrorString string 48} 49 50func (err *ProtocolError) Error() string { return err.ErrorString } 51 52var ( 53 ErrBadProtocolVersion = &ProtocolError{"bad protocol version"} 54 ErrBadScheme = &ProtocolError{"bad scheme"} 55 ErrBadStatus = &ProtocolError{"bad status"} 56 ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"} 57 ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"} 58 ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"} 59 ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"} 60 ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"} 61 ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"} 62 ErrBadFrame = &ProtocolError{"bad frame"} 63 ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"} 64 ErrNotWebSocket = &ProtocolError{"not websocket protocol"} 65 ErrBadRequestMethod = &ProtocolError{"bad method"} 66 ErrNotSupported = &ProtocolError{"not supported"} 67) 68 69// ErrFrameTooLarge is returned by Codec's Receive method if payload size 70// exceeds limit set by Conn.MaxPayloadBytes 71var ErrFrameTooLarge = errors.New("websocket: frame payload size exceeds limit") 72 73// Addr is an implementation of net.Addr for WebSocket. 74type Addr struct { 75 *url.URL 76} 77 78// Network returns the network type for a WebSocket, "websocket". 79func (addr *Addr) Network() string { return "websocket" } 80 81// Config is a WebSocket configuration 82type Config struct { 83 // A WebSocket server address. 84 Location *url.URL 85 86 // A Websocket client origin. 87 Origin *url.URL 88 89 // WebSocket subprotocols. 90 Protocol []string 91 92 // WebSocket protocol version. 93 Version int 94 95 // TLS config for secure WebSocket (wss). 96 TlsConfig *tls.Config 97 98 // Additional header fields to be sent in WebSocket opening handshake. 99 Header http.Header 100 101 // Dialer used when opening websocket connections. 102 Dialer *net.Dialer 103 104 handshakeData map[string]string 105} 106 107// serverHandshaker is an interface to handle WebSocket server side handshake. 108type serverHandshaker interface { 109 // ReadHandshake reads handshake request message from client. 110 // Returns http response code and error if any. 111 ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) 112 113 // AcceptHandshake accepts the client handshake request and sends 114 // handshake response back to client. 115 AcceptHandshake(buf *bufio.Writer) (err error) 116 117 // NewServerConn creates a new WebSocket connection. 118 NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) 119} 120 121// frameReader is an interface to read a WebSocket frame. 122type frameReader interface { 123 // Reader is to read payload of the frame. 124 io.Reader 125 126 // PayloadType returns payload type. 127 PayloadType() byte 128 129 // HeaderReader returns a reader to read header of the frame. 130 HeaderReader() io.Reader 131 132 // TrailerReader returns a reader to read trailer of the frame. 133 // If it returns nil, there is no trailer in the frame. 134 TrailerReader() io.Reader 135 136 // Len returns total length of the frame, including header and trailer. 137 Len() int 138} 139 140// frameReaderFactory is an interface to creates new frame reader. 141type frameReaderFactory interface { 142 NewFrameReader() (r frameReader, err error) 143} 144 145// frameWriter is an interface to write a WebSocket frame. 146type frameWriter interface { 147 // Writer is to write payload of the frame. 148 io.WriteCloser 149} 150 151// frameWriterFactory is an interface to create new frame writer. 152type frameWriterFactory interface { 153 NewFrameWriter(payloadType byte) (w frameWriter, err error) 154} 155 156type frameHandler interface { 157 HandleFrame(frame frameReader) (r frameReader, err error) 158 WriteClose(status int) (err error) 159} 160 161// Conn represents a WebSocket connection. 162// 163// Multiple goroutines may invoke methods on a Conn simultaneously. 164type Conn struct { 165 config *Config 166 request *http.Request 167 168 buf *bufio.ReadWriter 169 rwc io.ReadWriteCloser 170 171 rio sync.Mutex 172 frameReaderFactory 173 frameReader 174 175 wio sync.Mutex 176 frameWriterFactory 177 178 frameHandler 179 PayloadType byte 180 defaultCloseStatus int 181 182 // MaxPayloadBytes limits the size of frame payload received over Conn 183 // by Codec's Receive method. If zero, DefaultMaxPayloadBytes is used. 184 MaxPayloadBytes int 185} 186 187// Read implements the io.Reader interface: 188// it reads data of a frame from the WebSocket connection. 189// if msg is not large enough for the frame data, it fills the msg and next Read 190// will read the rest of the frame data. 191// it reads Text frame or Binary frame. 192func (ws *Conn) Read(msg []byte) (n int, err error) { 193 ws.rio.Lock() 194 defer ws.rio.Unlock() 195again: 196 if ws.frameReader == nil { 197 frame, err := ws.frameReaderFactory.NewFrameReader() 198 if err != nil { 199 return 0, err 200 } 201 ws.frameReader, err = ws.frameHandler.HandleFrame(frame) 202 if err != nil { 203 return 0, err 204 } 205 if ws.frameReader == nil { 206 goto again 207 } 208 } 209 n, err = ws.frameReader.Read(msg) 210 if err == io.EOF { 211 if trailer := ws.frameReader.TrailerReader(); trailer != nil { 212 io.Copy(ioutil.Discard, trailer) 213 } 214 ws.frameReader = nil 215 goto again 216 } 217 return n, err 218} 219 220// Write implements the io.Writer interface: 221// it writes data as a frame to the WebSocket connection. 222func (ws *Conn) Write(msg []byte) (n int, err error) { 223 ws.wio.Lock() 224 defer ws.wio.Unlock() 225 w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType) 226 if err != nil { 227 return 0, err 228 } 229 n, err = w.Write(msg) 230 w.Close() 231 return n, err 232} 233 234// Close implements the io.Closer interface. 235func (ws *Conn) Close() error { 236 err := ws.frameHandler.WriteClose(ws.defaultCloseStatus) 237 err1 := ws.rwc.Close() 238 if err != nil { 239 return err 240 } 241 return err1 242} 243 244// IsClientConn reports whether ws is a client-side connection. 245func (ws *Conn) IsClientConn() bool { return ws.request == nil } 246 247// IsServerConn reports whether ws is a server-side connection. 248func (ws *Conn) IsServerConn() bool { return ws.request != nil } 249 250// LocalAddr returns the WebSocket Origin for the connection for client, or 251// the WebSocket location for server. 252func (ws *Conn) LocalAddr() net.Addr { 253 if ws.IsClientConn() { 254 return &Addr{ws.config.Origin} 255 } 256 return &Addr{ws.config.Location} 257} 258 259// RemoteAddr returns the WebSocket location for the connection for client, or 260// the Websocket Origin for server. 261func (ws *Conn) RemoteAddr() net.Addr { 262 if ws.IsClientConn() { 263 return &Addr{ws.config.Location} 264 } 265 return &Addr{ws.config.Origin} 266} 267 268var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn") 269 270// SetDeadline sets the connection's network read & write deadlines. 271func (ws *Conn) SetDeadline(t time.Time) error { 272 if conn, ok := ws.rwc.(net.Conn); ok { 273 return conn.SetDeadline(t) 274 } 275 return errSetDeadline 276} 277 278// SetReadDeadline sets the connection's network read deadline. 279func (ws *Conn) SetReadDeadline(t time.Time) error { 280 if conn, ok := ws.rwc.(net.Conn); ok { 281 return conn.SetReadDeadline(t) 282 } 283 return errSetDeadline 284} 285 286// SetWriteDeadline sets the connection's network write deadline. 287func (ws *Conn) SetWriteDeadline(t time.Time) error { 288 if conn, ok := ws.rwc.(net.Conn); ok { 289 return conn.SetWriteDeadline(t) 290 } 291 return errSetDeadline 292} 293 294// Config returns the WebSocket config. 295func (ws *Conn) Config() *Config { return ws.config } 296 297// Request returns the http request upgraded to the WebSocket. 298// It is nil for client side. 299func (ws *Conn) Request() *http.Request { return ws.request } 300 301// Codec represents a symmetric pair of functions that implement a codec. 302type Codec struct { 303 Marshal func(v interface{}) (data []byte, payloadType byte, err error) 304 Unmarshal func(data []byte, payloadType byte, v interface{}) (err error) 305} 306 307// Send sends v marshaled by cd.Marshal as single frame to ws. 308func (cd Codec) Send(ws *Conn, v interface{}) (err error) { 309 data, payloadType, err := cd.Marshal(v) 310 if err != nil { 311 return err 312 } 313 ws.wio.Lock() 314 defer ws.wio.Unlock() 315 w, err := ws.frameWriterFactory.NewFrameWriter(payloadType) 316 if err != nil { 317 return err 318 } 319 _, err = w.Write(data) 320 w.Close() 321 return err 322} 323 324// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores 325// in v. The whole frame payload is read to an in-memory buffer; max size of 326// payload is defined by ws.MaxPayloadBytes. If frame payload size exceeds 327// limit, ErrFrameTooLarge is returned; in this case frame is not read off wire 328// completely. The next call to Receive would read and discard leftover data of 329// previous oversized frame before processing next frame. 330func (cd Codec) Receive(ws *Conn, v interface{}) (err error) { 331 ws.rio.Lock() 332 defer ws.rio.Unlock() 333 if ws.frameReader != nil { 334 _, err = io.Copy(ioutil.Discard, ws.frameReader) 335 if err != nil { 336 return err 337 } 338 ws.frameReader = nil 339 } 340again: 341 frame, err := ws.frameReaderFactory.NewFrameReader() 342 if err != nil { 343 return err 344 } 345 frame, err = ws.frameHandler.HandleFrame(frame) 346 if err != nil { 347 return err 348 } 349 if frame == nil { 350 goto again 351 } 352 maxPayloadBytes := ws.MaxPayloadBytes 353 if maxPayloadBytes == 0 { 354 maxPayloadBytes = DefaultMaxPayloadBytes 355 } 356 if hf, ok := frame.(*hybiFrameReader); ok && hf.header.Length > int64(maxPayloadBytes) { 357 // payload size exceeds limit, no need to call Unmarshal 358 // 359 // set frameReader to current oversized frame so that 360 // the next call to this function can drain leftover 361 // data before processing the next frame 362 ws.frameReader = frame 363 return ErrFrameTooLarge 364 } 365 payloadType := frame.PayloadType() 366 data, err := ioutil.ReadAll(frame) 367 if err != nil { 368 return err 369 } 370 return cd.Unmarshal(data, payloadType, v) 371} 372 373func marshal(v interface{}) (msg []byte, payloadType byte, err error) { 374 switch data := v.(type) { 375 case string: 376 return []byte(data), TextFrame, nil 377 case []byte: 378 return data, BinaryFrame, nil 379 } 380 return nil, UnknownFrame, ErrNotSupported 381} 382 383func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) { 384 switch data := v.(type) { 385 case *string: 386 *data = string(msg) 387 return nil 388 case *[]byte: 389 *data = msg 390 return nil 391 } 392 return ErrNotSupported 393} 394 395/* 396Message is a codec to send/receive text/binary data in a frame on WebSocket connection. 397To send/receive text frame, use string type. 398To send/receive binary frame, use []byte type. 399 400Trivial usage: 401 402 import "websocket" 403 404 // receive text frame 405 var message string 406 websocket.Message.Receive(ws, &message) 407 408 // send text frame 409 message = "hello" 410 websocket.Message.Send(ws, message) 411 412 // receive binary frame 413 var data []byte 414 websocket.Message.Receive(ws, &data) 415 416 // send binary frame 417 data = []byte{0, 1, 2} 418 websocket.Message.Send(ws, data) 419 420*/ 421var Message = Codec{marshal, unmarshal} 422 423func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) { 424 msg, err = json.Marshal(v) 425 return msg, TextFrame, err 426} 427 428func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) { 429 return json.Unmarshal(msg, v) 430} 431 432/* 433JSON is a codec to send/receive JSON data in a frame from a WebSocket connection. 434 435Trivial usage: 436 437 import "websocket" 438 439 type T struct { 440 Msg string 441 Count int 442 } 443 444 // receive JSON type T 445 var data T 446 websocket.JSON.Receive(ws, &data) 447 448 // send JSON type T 449 websocket.JSON.Send(ws, data) 450*/ 451var JSON = Codec{jsonMarshal, jsonUnmarshal} 452