blob: 8cffdd16c91da4627b5e3116fc292495b113543e [file] [log] [blame]
Matthias Andreas Benkard832a54e2019-01-29 09:27:38 +01001// Copyright 2011 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
5package websocket
6
7// This file implements a protocol of hybi draft.
8// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
9
10import (
11 "bufio"
12 "bytes"
13 "crypto/rand"
14 "crypto/sha1"
15 "encoding/base64"
16 "encoding/binary"
17 "fmt"
18 "io"
19 "io/ioutil"
20 "net/http"
21 "net/url"
22 "strings"
23)
24
25const (
26 websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
27
28 closeStatusNormal = 1000
29 closeStatusGoingAway = 1001
30 closeStatusProtocolError = 1002
31 closeStatusUnsupportedData = 1003
32 closeStatusFrameTooLarge = 1004
33 closeStatusNoStatusRcvd = 1005
34 closeStatusAbnormalClosure = 1006
35 closeStatusBadMessageData = 1007
36 closeStatusPolicyViolation = 1008
37 closeStatusTooBigData = 1009
38 closeStatusExtensionMismatch = 1010
39
40 maxControlFramePayloadLength = 125
41)
42
43var (
44 ErrBadMaskingKey = &ProtocolError{"bad masking key"}
45 ErrBadPongMessage = &ProtocolError{"bad pong message"}
46 ErrBadClosingStatus = &ProtocolError{"bad closing status"}
47 ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"}
48 ErrNotImplemented = &ProtocolError{"not implemented"}
49
50 handshakeHeader = map[string]bool{
51 "Host": true,
52 "Upgrade": true,
53 "Connection": true,
54 "Sec-Websocket-Key": true,
55 "Sec-Websocket-Origin": true,
56 "Sec-Websocket-Version": true,
57 "Sec-Websocket-Protocol": true,
58 "Sec-Websocket-Accept": true,
59 }
60)
61
62// A hybiFrameHeader is a frame header as defined in hybi draft.
63type hybiFrameHeader struct {
64 Fin bool
65 Rsv [3]bool
66 OpCode byte
67 Length int64
68 MaskingKey []byte
69
70 data *bytes.Buffer
71}
72
73// A hybiFrameReader is a reader for hybi frame.
74type hybiFrameReader struct {
75 reader io.Reader
76
77 header hybiFrameHeader
78 pos int64
79 length int
80}
81
82func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) {
83 n, err = frame.reader.Read(msg)
84 if frame.header.MaskingKey != nil {
85 for i := 0; i < n; i++ {
86 msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4]
87 frame.pos++
88 }
89 }
90 return n, err
91}
92
93func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode }
94
95func (frame *hybiFrameReader) HeaderReader() io.Reader {
96 if frame.header.data == nil {
97 return nil
98 }
99 if frame.header.data.Len() == 0 {
100 return nil
101 }
102 return frame.header.data
103}
104
105func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil }
106
107func (frame *hybiFrameReader) Len() (n int) { return frame.length }
108
109// A hybiFrameReaderFactory creates new frame reader based on its frame type.
110type hybiFrameReaderFactory struct {
111 *bufio.Reader
112}
113
114// NewFrameReader reads a frame header from the connection, and creates new reader for the frame.
115// See Section 5.2 Base Framing protocol for detail.
116// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2
117func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) {
118 hybiFrame := new(hybiFrameReader)
119 frame = hybiFrame
120 var header []byte
121 var b byte
122 // First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits)
123 b, err = buf.ReadByte()
124 if err != nil {
125 return
126 }
127 header = append(header, b)
128 hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0
129 for i := 0; i < 3; i++ {
130 j := uint(6 - i)
131 hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0
132 }
133 hybiFrame.header.OpCode = header[0] & 0x0f
134
135 // Second byte. Mask/Payload len(7bits)
136 b, err = buf.ReadByte()
137 if err != nil {
138 return
139 }
140 header = append(header, b)
141 mask := (b & 0x80) != 0
142 b &= 0x7f
143 lengthFields := 0
144 switch {
145 case b <= 125: // Payload length 7bits.
146 hybiFrame.header.Length = int64(b)
147 case b == 126: // Payload length 7+16bits
148 lengthFields = 2
149 case b == 127: // Payload length 7+64bits
150 lengthFields = 8
151 }
152 for i := 0; i < lengthFields; i++ {
153 b, err = buf.ReadByte()
154 if err != nil {
155 return
156 }
157 if lengthFields == 8 && i == 0 { // MSB must be zero when 7+64 bits
158 b &= 0x7f
159 }
160 header = append(header, b)
161 hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b)
162 }
163 if mask {
164 // Masking key. 4 bytes.
165 for i := 0; i < 4; i++ {
166 b, err = buf.ReadByte()
167 if err != nil {
168 return
169 }
170 header = append(header, b)
171 hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b)
172 }
173 }
174 hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length)
175 hybiFrame.header.data = bytes.NewBuffer(header)
176 hybiFrame.length = len(header) + int(hybiFrame.header.Length)
177 return
178}
179
180// A HybiFrameWriter is a writer for hybi frame.
181type hybiFrameWriter struct {
182 writer *bufio.Writer
183
184 header *hybiFrameHeader
185}
186
187func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) {
188 var header []byte
189 var b byte
190 if frame.header.Fin {
191 b |= 0x80
192 }
193 for i := 0; i < 3; i++ {
194 if frame.header.Rsv[i] {
195 j := uint(6 - i)
196 b |= 1 << j
197 }
198 }
199 b |= frame.header.OpCode
200 header = append(header, b)
201 if frame.header.MaskingKey != nil {
202 b = 0x80
203 } else {
204 b = 0
205 }
206 lengthFields := 0
207 length := len(msg)
208 switch {
209 case length <= 125:
210 b |= byte(length)
211 case length < 65536:
212 b |= 126
213 lengthFields = 2
214 default:
215 b |= 127
216 lengthFields = 8
217 }
218 header = append(header, b)
219 for i := 0; i < lengthFields; i++ {
220 j := uint((lengthFields - i - 1) * 8)
221 b = byte((length >> j) & 0xff)
222 header = append(header, b)
223 }
224 if frame.header.MaskingKey != nil {
225 if len(frame.header.MaskingKey) != 4 {
226 return 0, ErrBadMaskingKey
227 }
228 header = append(header, frame.header.MaskingKey...)
229 frame.writer.Write(header)
230 data := make([]byte, length)
231 for i := range data {
232 data[i] = msg[i] ^ frame.header.MaskingKey[i%4]
233 }
234 frame.writer.Write(data)
235 err = frame.writer.Flush()
236 return length, err
237 }
238 frame.writer.Write(header)
239 frame.writer.Write(msg)
240 err = frame.writer.Flush()
241 return length, err
242}
243
244func (frame *hybiFrameWriter) Close() error { return nil }
245
246type hybiFrameWriterFactory struct {
247 *bufio.Writer
248 needMaskingKey bool
249}
250
251func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) {
252 frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType}
253 if buf.needMaskingKey {
254 frameHeader.MaskingKey, err = generateMaskingKey()
255 if err != nil {
256 return nil, err
257 }
258 }
259 return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil
260}
261
262type hybiFrameHandler struct {
263 conn *Conn
264 payloadType byte
265}
266
267func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (frameReader, error) {
268 if handler.conn.IsServerConn() {
269 // The client MUST mask all frames sent to the server.
270 if frame.(*hybiFrameReader).header.MaskingKey == nil {
271 handler.WriteClose(closeStatusProtocolError)
272 return nil, io.EOF
273 }
274 } else {
275 // The server MUST NOT mask all frames.
276 if frame.(*hybiFrameReader).header.MaskingKey != nil {
277 handler.WriteClose(closeStatusProtocolError)
278 return nil, io.EOF
279 }
280 }
281 if header := frame.HeaderReader(); header != nil {
282 io.Copy(ioutil.Discard, header)
283 }
284 switch frame.PayloadType() {
285 case ContinuationFrame:
286 frame.(*hybiFrameReader).header.OpCode = handler.payloadType
287 case TextFrame, BinaryFrame:
288 handler.payloadType = frame.PayloadType()
289 case CloseFrame:
290 return nil, io.EOF
291 case PingFrame, PongFrame:
292 b := make([]byte, maxControlFramePayloadLength)
293 n, err := io.ReadFull(frame, b)
294 if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
295 return nil, err
296 }
297 io.Copy(ioutil.Discard, frame)
298 if frame.PayloadType() == PingFrame {
299 if _, err := handler.WritePong(b[:n]); err != nil {
300 return nil, err
301 }
302 }
303 return nil, nil
304 }
305 return frame, nil
306}
307
308func (handler *hybiFrameHandler) WriteClose(status int) (err error) {
309 handler.conn.wio.Lock()
310 defer handler.conn.wio.Unlock()
311 w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame)
312 if err != nil {
313 return err
314 }
315 msg := make([]byte, 2)
316 binary.BigEndian.PutUint16(msg, uint16(status))
317 _, err = w.Write(msg)
318 w.Close()
319 return err
320}
321
322func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) {
323 handler.conn.wio.Lock()
324 defer handler.conn.wio.Unlock()
325 w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame)
326 if err != nil {
327 return 0, err
328 }
329 n, err = w.Write(msg)
330 w.Close()
331 return n, err
332}
333
334// newHybiConn creates a new WebSocket connection speaking hybi draft protocol.
335func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
336 if buf == nil {
337 br := bufio.NewReader(rwc)
338 bw := bufio.NewWriter(rwc)
339 buf = bufio.NewReadWriter(br, bw)
340 }
341 ws := &Conn{config: config, request: request, buf: buf, rwc: rwc,
342 frameReaderFactory: hybiFrameReaderFactory{buf.Reader},
343 frameWriterFactory: hybiFrameWriterFactory{
344 buf.Writer, request == nil},
345 PayloadType: TextFrame,
346 defaultCloseStatus: closeStatusNormal}
347 ws.frameHandler = &hybiFrameHandler{conn: ws}
348 return ws
349}
350
351// generateMaskingKey generates a masking key for a frame.
352func generateMaskingKey() (maskingKey []byte, err error) {
353 maskingKey = make([]byte, 4)
354 if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil {
355 return
356 }
357 return
358}
359
360// generateNonce generates a nonce consisting of a randomly selected 16-byte
361// value that has been base64-encoded.
362func generateNonce() (nonce []byte) {
363 key := make([]byte, 16)
364 if _, err := io.ReadFull(rand.Reader, key); err != nil {
365 panic(err)
366 }
367 nonce = make([]byte, 24)
368 base64.StdEncoding.Encode(nonce, key)
369 return
370}
371
372// removeZone removes IPv6 zone identifer from host.
373// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
374func removeZone(host string) string {
375 if !strings.HasPrefix(host, "[") {
376 return host
377 }
378 i := strings.LastIndex(host, "]")
379 if i < 0 {
380 return host
381 }
382 j := strings.LastIndex(host[:i], "%")
383 if j < 0 {
384 return host
385 }
386 return host[:j] + host[i:]
387}
388
389// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of
390// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string.
391func getNonceAccept(nonce []byte) (expected []byte, err error) {
392 h := sha1.New()
393 if _, err = h.Write(nonce); err != nil {
394 return
395 }
396 if _, err = h.Write([]byte(websocketGUID)); err != nil {
397 return
398 }
399 expected = make([]byte, 28)
400 base64.StdEncoding.Encode(expected, h.Sum(nil))
401 return
402}
403
404// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17
405func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) {
406 bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
407
408 // According to RFC 6874, an HTTP client, proxy, or other
409 // intermediary must remove any IPv6 zone identifier attached
410 // to an outgoing URI.
411 bw.WriteString("Host: " + removeZone(config.Location.Host) + "\r\n")
412 bw.WriteString("Upgrade: websocket\r\n")
413 bw.WriteString("Connection: Upgrade\r\n")
414 nonce := generateNonce()
415 if config.handshakeData != nil {
416 nonce = []byte(config.handshakeData["key"])
417 }
418 bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n")
419 bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n")
420
421 if config.Version != ProtocolVersionHybi13 {
422 return ErrBadProtocolVersion
423 }
424
425 bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n")
426 if len(config.Protocol) > 0 {
427 bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n")
428 }
429 // TODO(ukai): send Sec-WebSocket-Extensions.
430 err = config.Header.WriteSubset(bw, handshakeHeader)
431 if err != nil {
432 return err
433 }
434
435 bw.WriteString("\r\n")
436 if err = bw.Flush(); err != nil {
437 return err
438 }
439
440 resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
441 if err != nil {
442 return err
443 }
444 if resp.StatusCode != 101 {
445 return ErrBadStatus
446 }
447 if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" ||
448 strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
449 return ErrBadUpgrade
450 }
451 expectedAccept, err := getNonceAccept(nonce)
452 if err != nil {
453 return err
454 }
455 if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) {
456 return ErrChallengeResponse
457 }
458 if resp.Header.Get("Sec-WebSocket-Extensions") != "" {
459 return ErrUnsupportedExtensions
460 }
461 offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol")
462 if offeredProtocol != "" {
463 protocolMatched := false
464 for i := 0; i < len(config.Protocol); i++ {
465 if config.Protocol[i] == offeredProtocol {
466 protocolMatched = true
467 break
468 }
469 }
470 if !protocolMatched {
471 return ErrBadWebSocketProtocol
472 }
473 config.Protocol = []string{offeredProtocol}
474 }
475
476 return nil
477}
478
479// newHybiClientConn creates a client WebSocket connection after handshake.
480func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
481 return newHybiConn(config, buf, rwc, nil)
482}
483
484// A HybiServerHandshaker performs a server handshake using hybi draft protocol.
485type hybiServerHandshaker struct {
486 *Config
487 accept []byte
488}
489
490func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) {
491 c.Version = ProtocolVersionHybi13
492 if req.Method != "GET" {
493 return http.StatusMethodNotAllowed, ErrBadRequestMethod
494 }
495 // HTTP version can be safely ignored.
496
497 if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
498 !strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") {
499 return http.StatusBadRequest, ErrNotWebSocket
500 }
501
502 key := req.Header.Get("Sec-Websocket-Key")
503 if key == "" {
504 return http.StatusBadRequest, ErrChallengeResponse
505 }
506 version := req.Header.Get("Sec-Websocket-Version")
507 switch version {
508 case "13":
509 c.Version = ProtocolVersionHybi13
510 default:
511 return http.StatusBadRequest, ErrBadWebSocketVersion
512 }
513 var scheme string
514 if req.TLS != nil {
515 scheme = "wss"
516 } else {
517 scheme = "ws"
518 }
519 c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI())
520 if err != nil {
521 return http.StatusBadRequest, err
522 }
523 protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
524 if protocol != "" {
525 protocols := strings.Split(protocol, ",")
526 for i := 0; i < len(protocols); i++ {
527 c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i]))
528 }
529 }
530 c.accept, err = getNonceAccept([]byte(key))
531 if err != nil {
532 return http.StatusInternalServerError, err
533 }
534 return http.StatusSwitchingProtocols, nil
535}
536
537// Origin parses the Origin header in req.
538// If the Origin header is not set, it returns nil and nil.
539func Origin(config *Config, req *http.Request) (*url.URL, error) {
540 var origin string
541 switch config.Version {
542 case ProtocolVersionHybi13:
543 origin = req.Header.Get("Origin")
544 }
545 if origin == "" {
546 return nil, nil
547 }
548 return url.ParseRequestURI(origin)
549}
550
551func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) {
552 if len(c.Protocol) > 0 {
553 if len(c.Protocol) != 1 {
554 // You need choose a Protocol in Handshake func in Server.
555 return ErrBadWebSocketProtocol
556 }
557 }
558 buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
559 buf.WriteString("Upgrade: websocket\r\n")
560 buf.WriteString("Connection: Upgrade\r\n")
561 buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n")
562 if len(c.Protocol) > 0 {
563 buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
564 }
565 // TODO(ukai): send Sec-WebSocket-Extensions.
566 if c.Header != nil {
567 err := c.Header.WriteSubset(buf, handshakeHeader)
568 if err != nil {
569 return err
570 }
571 }
572 buf.WriteString("\r\n")
573 return buf.Flush()
574}
575
576func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
577 return newHybiServerConn(c.Config, buf, rwc, request)
578}
579
580// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol.
581func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
582 return newHybiConn(config, buf, rwc, request)
583}