1
// 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.
7
// This file implements a protocol of hybi draft.
8
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17
26
websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
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
40
maxControlFramePayloadLength = 125
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"}
51
// A hybiFrameHeader is a frame header as defined in hybi draft.
52
type hybiFrameHeader struct {
62
// A hybiFrameReader is a reader for hybi frame.
63
type hybiFrameReader struct {
66
header hybiFrameHeader
71
func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) {
72
n, err = frame.reader.Read(msg)
76
if frame.header.MaskingKey != nil {
77
for i := 0; i < n; i++ {
78
msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4]
85
func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode }
87
func (frame *hybiFrameReader) HeaderReader() io.Reader {
88
if frame.header.data == nil {
91
if frame.header.data.Len() == 0 {
94
return frame.header.data
97
func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil }
99
func (frame *hybiFrameReader) Len() (n int) { return frame.length }
101
// A hybiFrameReaderFactory creates new frame reader based on its frame type.
102
type hybiFrameReaderFactory struct {
106
// NewFrameReader reads a frame header from the connection, and creates new reader for the frame.
107
// See Section 5.2 Base Framing protocol for detail.
108
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2
109
func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) {
110
hybiFrame := new(hybiFrameReader)
114
// First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits)
115
b, err = buf.ReadByte()
119
header = append(header, b)
120
hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0
121
for i := 0; i < 3; i++ {
123
hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0
125
hybiFrame.header.OpCode = header[0] & 0x0f
127
// Second byte. Mask/Payload len(7bits)
128
b, err = buf.ReadByte()
132
header = append(header, b)
133
mask := (b & 0x80) != 0
137
case b <= 125: // Payload length 7bits.
138
hybiFrame.header.Length = int64(b)
139
case b == 126: // Payload length 7+16bits
141
case b == 127: // Payload length 7+64bits
144
for i := 0; i < lengthFields; i++ {
145
b, err = buf.ReadByte()
149
header = append(header, b)
150
hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b)
153
// Masking key. 4 bytes.
154
for i := 0; i < 4; i++ {
155
b, err = buf.ReadByte()
159
header = append(header, b)
160
hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b)
163
hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length)
164
hybiFrame.header.data = bytes.NewBuffer(header)
165
hybiFrame.length = len(header) + int(hybiFrame.header.Length)
169
// A HybiFrameWriter is a writer for hybi frame.
170
type hybiFrameWriter struct {
173
header *hybiFrameHeader
176
func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) {
179
if frame.header.Fin {
182
for i := 0; i < 3; i++ {
183
if frame.header.Rsv[i] {
188
b |= frame.header.OpCode
189
header = append(header, b)
190
if frame.header.MaskingKey != nil {
207
header = append(header, b)
208
for i := 0; i < lengthFields; i++ {
209
j := uint((lengthFields - i - 1) * 8)
210
b = byte((length >> j) & 0xff)
211
header = append(header, b)
213
if frame.header.MaskingKey != nil {
214
if len(frame.header.MaskingKey) != 4 {
215
return 0, ErrBadMaskingKey
217
header = append(header, frame.header.MaskingKey...)
218
frame.writer.Write(header)
221
for i := 0; i < length; i++ {
222
data = append(data, msg[i]^frame.header.MaskingKey[i%4])
224
frame.writer.Write(data)
225
err = frame.writer.Flush()
228
frame.writer.Write(header)
229
frame.writer.Write(msg)
230
err = frame.writer.Flush()
234
func (frame *hybiFrameWriter) Close() error { return nil }
236
type hybiFrameWriterFactory struct {
241
func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) {
242
frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType}
243
if buf.needMaskingKey {
244
frameHeader.MaskingKey, err = generateMaskingKey()
249
return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil
252
type hybiFrameHandler struct {
257
func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err error) {
258
if handler.conn.IsServerConn() {
259
// The client MUST mask all frames sent to the server.
260
if frame.(*hybiFrameReader).header.MaskingKey == nil {
261
handler.WriteClose(closeStatusProtocolError)
265
// The server MUST NOT mask all frames.
266
if frame.(*hybiFrameReader).header.MaskingKey != nil {
267
handler.WriteClose(closeStatusProtocolError)
271
if header := frame.HeaderReader(); header != nil {
272
io.Copy(ioutil.Discard, header)
274
switch frame.PayloadType() {
275
case ContinuationFrame:
276
frame.(*hybiFrameReader).header.OpCode = handler.payloadType
277
case TextFrame, BinaryFrame:
278
handler.payloadType = frame.PayloadType()
282
pingMsg := make([]byte, maxControlFramePayloadLength)
283
n, err := io.ReadFull(frame, pingMsg)
284
if err != nil && err != io.ErrUnexpectedEOF {
287
io.Copy(ioutil.Discard, frame)
288
n, err = handler.WritePong(pingMsg[:n])
294
return nil, ErrNotImplemented
299
func (handler *hybiFrameHandler) WriteClose(status int) (err error) {
300
handler.conn.wio.Lock()
301
defer handler.conn.wio.Unlock()
302
w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame)
306
msg := make([]byte, 2)
307
binary.BigEndian.PutUint16(msg, uint16(status))
308
_, err = w.Write(msg)
313
func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) {
314
handler.conn.wio.Lock()
315
defer handler.conn.wio.Unlock()
316
w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame)
320
n, err = w.Write(msg)
325
// newHybiConn creates a new WebSocket connection speaking hybi draft protocol.
326
func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
328
br := bufio.NewReader(rwc)
329
bw := bufio.NewWriter(rwc)
330
buf = bufio.NewReadWriter(br, bw)
332
ws := &Conn{config: config, request: request, buf: buf, rwc: rwc,
333
frameReaderFactory: hybiFrameReaderFactory{buf.Reader},
334
frameWriterFactory: hybiFrameWriterFactory{
335
buf.Writer, request == nil},
336
PayloadType: TextFrame,
337
defaultCloseStatus: closeStatusNormal}
338
ws.frameHandler = &hybiFrameHandler{conn: ws}
342
// generateMaskingKey generates a masking key for a frame.
343
func generateMaskingKey() (maskingKey []byte, err error) {
344
maskingKey = make([]byte, 4)
345
if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil {
351
// generateNonce generates a nonce consisting of a randomly selected 16-byte
352
// value that has been base64-encoded.
353
func generateNonce() (nonce []byte) {
354
key := make([]byte, 16)
355
if _, err := io.ReadFull(rand.Reader, key); err != nil {
358
nonce = make([]byte, 24)
359
base64.StdEncoding.Encode(nonce, key)
363
// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of
364
// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string.
365
func getNonceAccept(nonce []byte) (expected []byte, err error) {
367
if _, err = h.Write(nonce); err != nil {
370
if _, err = h.Write([]byte(websocketGUID)); err != nil {
373
expected = make([]byte, 28)
374
base64.StdEncoding.Encode(expected, h.Sum(nil))
378
func isHybiVersion(version int) bool {
380
case ProtocolVersionHybi08, ProtocolVersionHybi13:
387
// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17
388
func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) {
389
if !isHybiVersion(config.Version) {
390
panic("wrong protocol version.")
393
bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
395
bw.WriteString("Host: " + config.Location.Host + "\r\n")
396
bw.WriteString("Upgrade: websocket\r\n")
397
bw.WriteString("Connection: Upgrade\r\n")
398
nonce := generateNonce()
399
if config.handshakeData != nil {
400
nonce = []byte(config.handshakeData["key"])
402
bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n")
403
if config.Version == ProtocolVersionHybi13 {
404
bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n")
405
} else if config.Version == ProtocolVersionHybi08 {
406
bw.WriteString("Sec-WebSocket-Origin: " + strings.ToLower(config.Origin.String()) + "\r\n")
408
bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n")
409
if len(config.Protocol) > 0 {
410
bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n")
412
// TODO(ukai): send extensions.
413
// TODO(ukai): send cookie if any.
415
bw.WriteString("\r\n")
416
if err = bw.Flush(); err != nil {
420
resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
424
if resp.StatusCode != 101 {
427
if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" ||
428
strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
431
expectedAccept, err := getNonceAccept(nonce)
435
if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) {
436
return ErrChallengeResponse
438
if resp.Header.Get("Sec-WebSocket-Extensions") != "" {
439
return ErrUnsupportedExtensions
441
offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol")
442
if offeredProtocol != "" {
443
protocolMatched := false
444
for i := 0; i < len(config.Protocol); i++ {
445
if config.Protocol[i] == offeredProtocol {
446
protocolMatched = true
450
if !protocolMatched {
451
return ErrBadWebSocketProtocol
453
config.Protocol = []string{offeredProtocol}
459
// newHybiClientConn creates a client WebSocket connection after handshake.
460
func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
461
return newHybiConn(config, buf, rwc, nil)
464
// A HybiServerHandshaker performs a server handshake using hybi draft protocol.
465
type hybiServerHandshaker struct {
470
func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) {
471
c.Version = ProtocolVersionHybi13
472
if req.Method != "GET" {
473
return http.StatusMethodNotAllowed, ErrBadRequestMethod
475
// HTTP version can be safely ignored.
477
if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
478
!strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") {
479
return http.StatusBadRequest, ErrNotWebSocket
482
key := req.Header.Get("Sec-Websocket-Key")
484
return http.StatusBadRequest, ErrChallengeResponse
486
version := req.Header.Get("Sec-Websocket-Version")
490
c.Version = ProtocolVersionHybi13
491
origin = req.Header.Get("Origin")
493
c.Version = ProtocolVersionHybi08
494
origin = req.Header.Get("Sec-Websocket-Origin")
496
return http.StatusBadRequest, ErrBadWebSocketVersion
498
c.Origin, err = url.ParseRequestURI(origin)
500
return http.StatusForbidden, err
508
c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI())
510
return http.StatusBadRequest, err
512
protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
513
protocols := strings.Split(protocol, ",")
514
for i := 0; i < len(protocols); i++ {
515
c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i]))
517
c.accept, err = getNonceAccept([]byte(key))
519
return http.StatusInternalServerError, err
521
return http.StatusSwitchingProtocols, nil
524
func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) {
525
if len(c.Protocol) > 0 {
526
if len(c.Protocol) != 1 {
527
return ErrBadWebSocketProtocol
530
buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
531
buf.WriteString("Upgrade: websocket\r\n")
532
buf.WriteString("Connection: Upgrade\r\n")
533
buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n")
534
if len(c.Protocol) > 0 {
535
buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
537
// TODO(ukai): support extensions
538
buf.WriteString("\r\n")
542
func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
543
return newHybiServerConn(c.Config, buf, rwc, request)
546
// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol.
547
func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
548
return newHybiConn(config, buf, rwc, request)