blob: 1f4f7be40086d4eded6f3f26b88c8a50a1843cb7 [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001// 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 an alternative
9// and more actively maintained WebSocket package:
10//
11// https://godoc.org/github.com/gorilla/websocket
12//
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}