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"}
50
handshakeHeader = map[string]bool{
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,
62
// A hybiFrameHeader is a frame header as defined in hybi draft.
63
type hybiFrameHeader struct {
73
// A hybiFrameReader is a reader for hybi frame.
74
type hybiFrameReader struct {
77
header hybiFrameHeader
82
func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) {
83
n, err = frame.reader.Read(msg)
87
if frame.header.MaskingKey != nil {
88
for i := 0; i < n; i++ {
89
msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4]
96
func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode }
98
func (frame *hybiFrameReader) HeaderReader() io.Reader {
99
if frame.header.data == nil {
102
if frame.header.data.Len() == 0 {
105
return frame.header.data
108
func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil }
110
func (frame *hybiFrameReader) Len() (n int) { return frame.length }
112
// A hybiFrameReaderFactory creates new frame reader based on its frame type.
113
type hybiFrameReaderFactory struct {
117
// NewFrameReader reads a frame header from the connection, and creates new reader for the frame.
118
// See Section 5.2 Base Framing protocol for detail.
119
// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2
120
func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) {
121
hybiFrame := new(hybiFrameReader)
125
// First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits)
126
b, err = buf.ReadByte()
130
header = append(header, b)
131
hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0
132
for i := 0; i < 3; i++ {
134
hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0
136
hybiFrame.header.OpCode = header[0] & 0x0f
138
// Second byte. Mask/Payload len(7bits)
139
b, err = buf.ReadByte()
143
header = append(header, b)
144
mask := (b & 0x80) != 0
148
case b <= 125: // Payload length 7bits.
149
hybiFrame.header.Length = int64(b)
150
case b == 126: // Payload length 7+16bits
152
case b == 127: // Payload length 7+64bits
155
for i := 0; i < lengthFields; i++ {
156
b, err = buf.ReadByte()
160
if lengthFields == 8 && i == 0 { // MSB must be zero when 7+64 bits
163
header = append(header, b)
164
hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b)
167
// Masking key. 4 bytes.
168
for i := 0; i < 4; i++ {
169
b, err = buf.ReadByte()
173
header = append(header, b)
174
hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b)
177
hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length)
178
hybiFrame.header.data = bytes.NewBuffer(header)
179
hybiFrame.length = len(header) + int(hybiFrame.header.Length)
183
// A HybiFrameWriter is a writer for hybi frame.
184
type hybiFrameWriter struct {
187
header *hybiFrameHeader
190
func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) {
193
if frame.header.Fin {
196
for i := 0; i < 3; i++ {
197
if frame.header.Rsv[i] {
202
b |= frame.header.OpCode
203
header = append(header, b)
204
if frame.header.MaskingKey != nil {
221
header = append(header, b)
222
for i := 0; i < lengthFields; i++ {
223
j := uint((lengthFields - i - 1) * 8)
224
b = byte((length >> j) & 0xff)
225
header = append(header, b)
227
if frame.header.MaskingKey != nil {
228
if len(frame.header.MaskingKey) != 4 {
229
return 0, ErrBadMaskingKey
231
header = append(header, frame.header.MaskingKey...)
232
frame.writer.Write(header)
233
data := make([]byte, length)
234
for i := range data {
235
data[i] = msg[i] ^ frame.header.MaskingKey[i%4]
237
frame.writer.Write(data)
238
err = frame.writer.Flush()
241
frame.writer.Write(header)
242
frame.writer.Write(msg)
243
err = frame.writer.Flush()
247
func (frame *hybiFrameWriter) Close() error { return nil }
249
type hybiFrameWriterFactory struct {
254
func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) {
255
frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType}
256
if buf.needMaskingKey {
257
frameHeader.MaskingKey, err = generateMaskingKey()
262
return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil
265
type hybiFrameHandler struct {
270
func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (frameReader, error) {
271
if handler.conn.IsServerConn() {
272
// The client MUST mask all frames sent to the server.
273
if frame.(*hybiFrameReader).header.MaskingKey == nil {
274
handler.WriteClose(closeStatusProtocolError)
278
// The server MUST NOT mask all frames.
279
if frame.(*hybiFrameReader).header.MaskingKey != nil {
280
handler.WriteClose(closeStatusProtocolError)
284
if header := frame.HeaderReader(); header != nil {
285
io.Copy(ioutil.Discard, header)
287
switch frame.PayloadType() {
288
case ContinuationFrame:
289
frame.(*hybiFrameReader).header.OpCode = handler.payloadType
290
case TextFrame, BinaryFrame:
291
handler.payloadType = frame.PayloadType()
294
case PingFrame, PongFrame:
295
b := make([]byte, maxControlFramePayloadLength)
296
n, err := io.ReadFull(frame, b)
297
if err != nil && err != io.EOF && err != io.ErrUnexpectedEOF {
300
io.Copy(ioutil.Discard, frame)
301
if frame.PayloadType() == PingFrame {
302
if _, err := handler.WritePong(b[:n]); err != nil {
311
func (handler *hybiFrameHandler) WriteClose(status int) (err error) {
312
handler.conn.wio.Lock()
313
defer handler.conn.wio.Unlock()
314
w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame)
318
msg := make([]byte, 2)
319
binary.BigEndian.PutUint16(msg, uint16(status))
320
_, err = w.Write(msg)
325
func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) {
326
handler.conn.wio.Lock()
327
defer handler.conn.wio.Unlock()
328
w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame)
332
n, err = w.Write(msg)
337
// newHybiConn creates a new WebSocket connection speaking hybi draft protocol.
338
func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
340
br := bufio.NewReader(rwc)
341
bw := bufio.NewWriter(rwc)
342
buf = bufio.NewReadWriter(br, bw)
344
ws := &Conn{config: config, request: request, buf: buf, rwc: rwc,
345
frameReaderFactory: hybiFrameReaderFactory{buf.Reader},
346
frameWriterFactory: hybiFrameWriterFactory{
347
buf.Writer, request == nil},
348
PayloadType: TextFrame,
349
defaultCloseStatus: closeStatusNormal}
350
ws.frameHandler = &hybiFrameHandler{conn: ws}
354
// generateMaskingKey generates a masking key for a frame.
355
func generateMaskingKey() (maskingKey []byte, err error) {
356
maskingKey = make([]byte, 4)
357
if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil {
363
// generateNonce generates a nonce consisting of a randomly selected 16-byte
364
// value that has been base64-encoded.
365
func generateNonce() (nonce []byte) {
366
key := make([]byte, 16)
367
if _, err := io.ReadFull(rand.Reader, key); err != nil {
370
nonce = make([]byte, 24)
371
base64.StdEncoding.Encode(nonce, key)
375
// removeZone removes IPv6 zone identifer from host.
376
// E.g., "[fe80::1%en0]:8080" to "[fe80::1]:8080"
377
func removeZone(host string) string {
378
if !strings.HasPrefix(host, "[") {
381
i := strings.LastIndex(host, "]")
385
j := strings.LastIndex(host[:i], "%")
389
return host[:j] + host[i:]
392
// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of
393
// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string.
394
func getNonceAccept(nonce []byte) (expected []byte, err error) {
396
if _, err = h.Write(nonce); err != nil {
399
if _, err = h.Write([]byte(websocketGUID)); err != nil {
402
expected = make([]byte, 28)
403
base64.StdEncoding.Encode(expected, h.Sum(nil))
407
// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17
408
func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) {
409
bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
411
// According to RFC 6874, an HTTP client, proxy, or other
412
// intermediary must remove any IPv6 zone identifier attached
413
// to an outgoing URI.
414
bw.WriteString("Host: " + removeZone(config.Location.Host) + "\r\n")
415
bw.WriteString("Upgrade: websocket\r\n")
416
bw.WriteString("Connection: Upgrade\r\n")
417
nonce := generateNonce()
418
if config.handshakeData != nil {
419
nonce = []byte(config.handshakeData["key"])
421
bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n")
422
bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n")
424
if config.Version != ProtocolVersionHybi13 {
425
return ErrBadProtocolVersion
428
bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n")
429
if len(config.Protocol) > 0 {
430
bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n")
432
// TODO(ukai): send Sec-WebSocket-Extensions.
433
err = config.Header.WriteSubset(bw, handshakeHeader)
438
bw.WriteString("\r\n")
439
if err = bw.Flush(); err != nil {
443
resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
447
if resp.StatusCode != 101 {
450
if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" ||
451
strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
454
expectedAccept, err := getNonceAccept(nonce)
458
if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) {
459
return ErrChallengeResponse
461
if resp.Header.Get("Sec-WebSocket-Extensions") != "" {
462
return ErrUnsupportedExtensions
464
offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol")
465
if offeredProtocol != "" {
466
protocolMatched := false
467
for i := 0; i < len(config.Protocol); i++ {
468
if config.Protocol[i] == offeredProtocol {
469
protocolMatched = true
473
if !protocolMatched {
474
return ErrBadWebSocketProtocol
476
config.Protocol = []string{offeredProtocol}
482
// newHybiClientConn creates a client WebSocket connection after handshake.
483
func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
484
return newHybiConn(config, buf, rwc, nil)
487
// A HybiServerHandshaker performs a server handshake using hybi draft protocol.
488
type hybiServerHandshaker struct {
493
func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) {
494
c.Version = ProtocolVersionHybi13
495
if req.Method != "GET" {
496
return http.StatusMethodNotAllowed, ErrBadRequestMethod
498
// HTTP version can be safely ignored.
500
if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
501
!strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") {
502
return http.StatusBadRequest, ErrNotWebSocket
505
key := req.Header.Get("Sec-Websocket-Key")
507
return http.StatusBadRequest, ErrChallengeResponse
509
version := req.Header.Get("Sec-Websocket-Version")
512
c.Version = ProtocolVersionHybi13
514
return http.StatusBadRequest, ErrBadWebSocketVersion
522
c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI())
524
return http.StatusBadRequest, err
526
protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
528
protocols := strings.Split(protocol, ",")
529
for i := 0; i < len(protocols); i++ {
530
c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i]))
533
c.accept, err = getNonceAccept([]byte(key))
535
return http.StatusInternalServerError, err
537
return http.StatusSwitchingProtocols, nil
540
// Origin parses the Origin header in req.
541
// If the Origin header is not set, it returns nil and nil.
542
func Origin(config *Config, req *http.Request) (*url.URL, error) {
544
switch config.Version {
545
case ProtocolVersionHybi13:
546
origin = req.Header.Get("Origin")
551
return url.ParseRequestURI(origin)
554
func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) {
555
if len(c.Protocol) > 0 {
556
if len(c.Protocol) != 1 {
557
// You need choose a Protocol in Handshake func in Server.
558
return ErrBadWebSocketProtocol
561
buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
562
buf.WriteString("Upgrade: websocket\r\n")
563
buf.WriteString("Connection: Upgrade\r\n")
564
buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n")
565
if len(c.Protocol) > 0 {
566
buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
568
// TODO(ukai): send Sec-WebSocket-Extensions.
570
err := c.Header.WriteSubset(buf, handshakeHeader)
575
buf.WriteString("\r\n")
579
func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
580
return newHybiServerConn(c.Config, buf, rwc, request)
583
// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol.
584
func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
585
return newHybiConn(config, buf, rwc, request)