Signed-off-by: Tibor Vass <tibor@docker.com>
| ... | ... |
@@ -2,6 +2,7 @@ |
| 2 | 2 |
set -e |
| 3 | 3 |
|
| 4 | 4 |
cd "$(dirname "$BASH_SOURCE")/.." |
| 5 |
+rm -rf vendor/ |
|
| 5 | 6 |
source 'hack/.vendor-helpers.sh' |
| 6 | 7 |
|
| 7 | 8 |
# the following lines are in sorted order, FYI |
| ... | ... |
@@ -13,7 +14,7 @@ clone git github.com/gorilla/mux e444e69cbd |
| 13 | 13 |
clone git github.com/kr/pty 5cf931ef8f |
| 14 | 14 |
clone git github.com/mistifyio/go-zfs v2.1.1 |
| 15 | 15 |
clone git github.com/tchap/go-patricia v2.1.0 |
| 16 |
-clone hg code.google.com/p/go.net 84a4013f96e0 |
|
| 16 |
+clone git golang.org/x/net 3cffabab72adf04f8e3b01c5baf775361837b5fe https://github.com/golang/net.git |
|
| 17 | 17 |
clone hg code.google.com/p/gosqlite 74691fb6f837 |
| 18 | 18 |
|
| 19 | 19 |
#get libnetwork packages |
| 15 | 14 |
deleted file mode 100644 |
| ... | ... |
@@ -1,112 +0,0 @@ |
| 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 |
|
| 6 |
- |
|
| 7 |
-import ( |
|
| 8 |
- "bufio" |
|
| 9 |
- "crypto/tls" |
|
| 10 |
- "io" |
|
| 11 |
- "net" |
|
| 12 |
- "net/http" |
|
| 13 |
- "net/url" |
|
| 14 |
-) |
|
| 15 |
- |
|
| 16 |
-// DialError is an error that occurs while dialling a websocket server. |
|
| 17 |
-type DialError struct {
|
|
| 18 |
- *Config |
|
| 19 |
- Err error |
|
| 20 |
-} |
|
| 21 |
- |
|
| 22 |
-func (e *DialError) Error() string {
|
|
| 23 |
- return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error() |
|
| 24 |
-} |
|
| 25 |
- |
|
| 26 |
-// NewConfig creates a new WebSocket config for client connection. |
|
| 27 |
-func NewConfig(server, origin string) (config *Config, err error) {
|
|
| 28 |
- config = new(Config) |
|
| 29 |
- config.Version = ProtocolVersionHybi13 |
|
| 30 |
- config.Location, err = url.ParseRequestURI(server) |
|
| 31 |
- if err != nil {
|
|
| 32 |
- return |
|
| 33 |
- } |
|
| 34 |
- config.Origin, err = url.ParseRequestURI(origin) |
|
| 35 |
- if err != nil {
|
|
| 36 |
- return |
|
| 37 |
- } |
|
| 38 |
- config.Header = http.Header(make(map[string][]string)) |
|
| 39 |
- return |
|
| 40 |
-} |
|
| 41 |
- |
|
| 42 |
-// NewClient creates a new WebSocket client connection over rwc. |
|
| 43 |
-func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) {
|
|
| 44 |
- br := bufio.NewReader(rwc) |
|
| 45 |
- bw := bufio.NewWriter(rwc) |
|
| 46 |
- switch config.Version {
|
|
| 47 |
- case ProtocolVersionHixie75: |
|
| 48 |
- err = hixie75ClientHandshake(config, br, bw) |
|
| 49 |
- case ProtocolVersionHixie76, ProtocolVersionHybi00: |
|
| 50 |
- err = hixie76ClientHandshake(config, br, bw) |
|
| 51 |
- case ProtocolVersionHybi08, ProtocolVersionHybi13: |
|
| 52 |
- err = hybiClientHandshake(config, br, bw) |
|
| 53 |
- default: |
|
| 54 |
- err = ErrBadProtocolVersion |
|
| 55 |
- } |
|
| 56 |
- if err != nil {
|
|
| 57 |
- return |
|
| 58 |
- } |
|
| 59 |
- buf := bufio.NewReadWriter(br, bw) |
|
| 60 |
- switch config.Version {
|
|
| 61 |
- case ProtocolVersionHixie75, ProtocolVersionHixie76, ProtocolVersionHybi00: |
|
| 62 |
- ws = newHixieClientConn(config, buf, rwc) |
|
| 63 |
- case ProtocolVersionHybi08, ProtocolVersionHybi13: |
|
| 64 |
- ws = newHybiClientConn(config, buf, rwc) |
|
| 65 |
- } |
|
| 66 |
- return |
|
| 67 |
-} |
|
| 68 |
- |
|
| 69 |
-// Dial opens a new client connection to a WebSocket. |
|
| 70 |
-func Dial(url_, protocol, origin string) (ws *Conn, err error) {
|
|
| 71 |
- config, err := NewConfig(url_, origin) |
|
| 72 |
- if err != nil {
|
|
| 73 |
- return nil, err |
|
| 74 |
- } |
|
| 75 |
- if protocol != "" {
|
|
| 76 |
- config.Protocol = []string{protocol}
|
|
| 77 |
- } |
|
| 78 |
- return DialConfig(config) |
|
| 79 |
-} |
|
| 80 |
- |
|
| 81 |
-// DialConfig opens a new client connection to a WebSocket with a config. |
|
| 82 |
-func DialConfig(config *Config) (ws *Conn, err error) {
|
|
| 83 |
- var client net.Conn |
|
| 84 |
- if config.Location == nil {
|
|
| 85 |
- return nil, &DialError{config, ErrBadWebSocketLocation}
|
|
| 86 |
- } |
|
| 87 |
- if config.Origin == nil {
|
|
| 88 |
- return nil, &DialError{config, ErrBadWebSocketOrigin}
|
|
| 89 |
- } |
|
| 90 |
- switch config.Location.Scheme {
|
|
| 91 |
- case "ws": |
|
| 92 |
- client, err = net.Dial("tcp", config.Location.Host)
|
|
| 93 |
- |
|
| 94 |
- case "wss": |
|
| 95 |
- client, err = tls.Dial("tcp", config.Location.Host, config.TlsConfig)
|
|
| 96 |
- |
|
| 97 |
- default: |
|
| 98 |
- err = ErrBadScheme |
|
| 99 |
- } |
|
| 100 |
- if err != nil {
|
|
| 101 |
- goto Error |
|
| 102 |
- } |
|
| 103 |
- |
|
| 104 |
- ws, err = NewClient(config, client) |
|
| 105 |
- if err != nil {
|
|
| 106 |
- goto Error |
|
| 107 |
- } |
|
| 108 |
- return |
|
| 109 |
- |
|
| 110 |
-Error: |
|
| 111 |
- return nil, &DialError{config, err}
|
|
| 112 |
-} |
| 113 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,695 +0,0 @@ |
| 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 |
|
| 6 |
- |
|
| 7 |
-// This file implements a protocol of Hixie draft version 75 and 76 |
|
| 8 |
-// (draft 76 equals to hybi 00) |
|
| 9 |
- |
|
| 10 |
-import ( |
|
| 11 |
- "bufio" |
|
| 12 |
- "bytes" |
|
| 13 |
- "crypto/md5" |
|
| 14 |
- "encoding/binary" |
|
| 15 |
- "fmt" |
|
| 16 |
- "io" |
|
| 17 |
- "io/ioutil" |
|
| 18 |
- "math/rand" |
|
| 19 |
- "net/http" |
|
| 20 |
- "net/url" |
|
| 21 |
- "strconv" |
|
| 22 |
- "strings" |
|
| 23 |
-) |
|
| 24 |
- |
|
| 25 |
-// An array of characters to be randomly inserted to construct Sec-WebSocket-Key |
|
| 26 |
-// value. It holds characters from ranges U+0021 to U+002F and U+003A to U+007E. |
|
| 27 |
-// See Step 21 in Section 4.1 Opening handshake. |
|
| 28 |
-// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#page-22 |
|
| 29 |
-var secKeyRandomChars [0x30 - 0x21 + 0x7F - 0x3A]byte |
|
| 30 |
- |
|
| 31 |
-func init() {
|
|
| 32 |
- i := 0 |
|
| 33 |
- for ch := byte(0x21); ch < 0x30; ch++ {
|
|
| 34 |
- secKeyRandomChars[i] = ch |
|
| 35 |
- i++ |
|
| 36 |
- } |
|
| 37 |
- for ch := byte(0x3a); ch < 0x7F; ch++ {
|
|
| 38 |
- secKeyRandomChars[i] = ch |
|
| 39 |
- i++ |
|
| 40 |
- } |
|
| 41 |
-} |
|
| 42 |
- |
|
| 43 |
-type byteReader interface {
|
|
| 44 |
- ReadByte() (byte, error) |
|
| 45 |
-} |
|
| 46 |
- |
|
| 47 |
-// readHixieLength reads frame length for frame type 0x80-0xFF |
|
| 48 |
-// as defined in Hixie draft. |
|
| 49 |
-// See section 4.2 Data framing. |
|
| 50 |
-// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00#section-4.2 |
|
| 51 |
-func readHixieLength(r byteReader) (length int64, lengthFields []byte, err error) {
|
|
| 52 |
- for {
|
|
| 53 |
- c, err := r.ReadByte() |
|
| 54 |
- if err != nil {
|
|
| 55 |
- return 0, nil, err |
|
| 56 |
- } |
|
| 57 |
- lengthFields = append(lengthFields, c) |
|
| 58 |
- length = length*128 + int64(c&0x7f) |
|
| 59 |
- if c&0x80 == 0 {
|
|
| 60 |
- break |
|
| 61 |
- } |
|
| 62 |
- } |
|
| 63 |
- return |
|
| 64 |
-} |
|
| 65 |
- |
|
| 66 |
-// A hixieLengthFrameReader is a reader for frame type 0x80-0xFF |
|
| 67 |
-// as defined in hixie draft. |
|
| 68 |
-type hixieLengthFrameReader struct {
|
|
| 69 |
- reader io.Reader |
|
| 70 |
- FrameType byte |
|
| 71 |
- Length int64 |
|
| 72 |
- header *bytes.Buffer |
|
| 73 |
- length int |
|
| 74 |
-} |
|
| 75 |
- |
|
| 76 |
-func (frame *hixieLengthFrameReader) Read(msg []byte) (n int, err error) {
|
|
| 77 |
- return frame.reader.Read(msg) |
|
| 78 |
-} |
|
| 79 |
- |
|
| 80 |
-func (frame *hixieLengthFrameReader) PayloadType() byte {
|
|
| 81 |
- if frame.FrameType == '\xff' && frame.Length == 0 {
|
|
| 82 |
- return CloseFrame |
|
| 83 |
- } |
|
| 84 |
- return UnknownFrame |
|
| 85 |
-} |
|
| 86 |
- |
|
| 87 |
-func (frame *hixieLengthFrameReader) HeaderReader() io.Reader {
|
|
| 88 |
- if frame.header == nil {
|
|
| 89 |
- return nil |
|
| 90 |
- } |
|
| 91 |
- if frame.header.Len() == 0 {
|
|
| 92 |
- frame.header = nil |
|
| 93 |
- return nil |
|
| 94 |
- } |
|
| 95 |
- return frame.header |
|
| 96 |
-} |
|
| 97 |
- |
|
| 98 |
-func (frame *hixieLengthFrameReader) TrailerReader() io.Reader { return nil }
|
|
| 99 |
- |
|
| 100 |
-func (frame *hixieLengthFrameReader) Len() (n int) { return frame.length }
|
|
| 101 |
- |
|
| 102 |
-// A HixieSentinelFrameReader is a reader for frame type 0x00-0x7F |
|
| 103 |
-// as defined in hixie draft. |
|
| 104 |
-type hixieSentinelFrameReader struct {
|
|
| 105 |
- reader *bufio.Reader |
|
| 106 |
- FrameType byte |
|
| 107 |
- header *bytes.Buffer |
|
| 108 |
- data []byte |
|
| 109 |
- seenTrailer bool |
|
| 110 |
- trailer *bytes.Buffer |
|
| 111 |
-} |
|
| 112 |
- |
|
| 113 |
-func (frame *hixieSentinelFrameReader) Read(msg []byte) (n int, err error) {
|
|
| 114 |
- if len(frame.data) == 0 {
|
|
| 115 |
- if frame.seenTrailer {
|
|
| 116 |
- return 0, io.EOF |
|
| 117 |
- } |
|
| 118 |
- frame.data, err = frame.reader.ReadSlice('\xff')
|
|
| 119 |
- if err == nil {
|
|
| 120 |
- frame.seenTrailer = true |
|
| 121 |
- frame.data = frame.data[:len(frame.data)-1] // trim \xff |
|
| 122 |
- frame.trailer = bytes.NewBuffer([]byte{0xff})
|
|
| 123 |
- } |
|
| 124 |
- } |
|
| 125 |
- n = copy(msg, frame.data) |
|
| 126 |
- frame.data = frame.data[n:] |
|
| 127 |
- return n, err |
|
| 128 |
-} |
|
| 129 |
- |
|
| 130 |
-func (frame *hixieSentinelFrameReader) PayloadType() byte {
|
|
| 131 |
- if frame.FrameType == 0 {
|
|
| 132 |
- return TextFrame |
|
| 133 |
- } |
|
| 134 |
- return UnknownFrame |
|
| 135 |
-} |
|
| 136 |
- |
|
| 137 |
-func (frame *hixieSentinelFrameReader) HeaderReader() io.Reader {
|
|
| 138 |
- if frame.header == nil {
|
|
| 139 |
- return nil |
|
| 140 |
- } |
|
| 141 |
- if frame.header.Len() == 0 {
|
|
| 142 |
- frame.header = nil |
|
| 143 |
- return nil |
|
| 144 |
- } |
|
| 145 |
- return frame.header |
|
| 146 |
-} |
|
| 147 |
- |
|
| 148 |
-func (frame *hixieSentinelFrameReader) TrailerReader() io.Reader {
|
|
| 149 |
- if frame.trailer == nil {
|
|
| 150 |
- return nil |
|
| 151 |
- } |
|
| 152 |
- if frame.trailer.Len() == 0 {
|
|
| 153 |
- frame.trailer = nil |
|
| 154 |
- return nil |
|
| 155 |
- } |
|
| 156 |
- return frame.trailer |
|
| 157 |
-} |
|
| 158 |
- |
|
| 159 |
-func (frame *hixieSentinelFrameReader) Len() int { return -1 }
|
|
| 160 |
- |
|
| 161 |
-// A HixieFrameReaderFactory creates new frame reader based on its frame type. |
|
| 162 |
-type hixieFrameReaderFactory struct {
|
|
| 163 |
- *bufio.Reader |
|
| 164 |
-} |
|
| 165 |
- |
|
| 166 |
-func (buf hixieFrameReaderFactory) NewFrameReader() (r frameReader, err error) {
|
|
| 167 |
- var header []byte |
|
| 168 |
- var b byte |
|
| 169 |
- b, err = buf.ReadByte() |
|
| 170 |
- if err != nil {
|
|
| 171 |
- return |
|
| 172 |
- } |
|
| 173 |
- header = append(header, b) |
|
| 174 |
- if b&0x80 == 0x80 {
|
|
| 175 |
- length, lengthFields, err := readHixieLength(buf.Reader) |
|
| 176 |
- if err != nil {
|
|
| 177 |
- return nil, err |
|
| 178 |
- } |
|
| 179 |
- if length == 0 {
|
|
| 180 |
- return nil, io.EOF |
|
| 181 |
- } |
|
| 182 |
- header = append(header, lengthFields...) |
|
| 183 |
- return &hixieLengthFrameReader{
|
|
| 184 |
- reader: io.LimitReader(buf.Reader, length), |
|
| 185 |
- FrameType: b, |
|
| 186 |
- Length: length, |
|
| 187 |
- header: bytes.NewBuffer(header)}, err |
|
| 188 |
- } |
|
| 189 |
- return &hixieSentinelFrameReader{
|
|
| 190 |
- reader: buf.Reader, |
|
| 191 |
- FrameType: b, |
|
| 192 |
- header: bytes.NewBuffer(header)}, err |
|
| 193 |
-} |
|
| 194 |
- |
|
| 195 |
-type hixiFrameWriter struct {
|
|
| 196 |
- writer *bufio.Writer |
|
| 197 |
-} |
|
| 198 |
- |
|
| 199 |
-func (frame *hixiFrameWriter) Write(msg []byte) (n int, err error) {
|
|
| 200 |
- frame.writer.WriteByte(0) |
|
| 201 |
- frame.writer.Write(msg) |
|
| 202 |
- frame.writer.WriteByte(0xff) |
|
| 203 |
- err = frame.writer.Flush() |
|
| 204 |
- return len(msg), err |
|
| 205 |
-} |
|
| 206 |
- |
|
| 207 |
-func (frame *hixiFrameWriter) Close() error { return nil }
|
|
| 208 |
- |
|
| 209 |
-type hixiFrameWriterFactory struct {
|
|
| 210 |
- *bufio.Writer |
|
| 211 |
-} |
|
| 212 |
- |
|
| 213 |
-func (buf hixiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) {
|
|
| 214 |
- if payloadType != TextFrame {
|
|
| 215 |
- return nil, ErrNotSupported |
|
| 216 |
- } |
|
| 217 |
- return &hixiFrameWriter{writer: buf.Writer}, nil
|
|
| 218 |
-} |
|
| 219 |
- |
|
| 220 |
-type hixiFrameHandler struct {
|
|
| 221 |
- conn *Conn |
|
| 222 |
-} |
|
| 223 |
- |
|
| 224 |
-func (handler *hixiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err error) {
|
|
| 225 |
- if header := frame.HeaderReader(); header != nil {
|
|
| 226 |
- io.Copy(ioutil.Discard, header) |
|
| 227 |
- } |
|
| 228 |
- if frame.PayloadType() != TextFrame {
|
|
| 229 |
- io.Copy(ioutil.Discard, frame) |
|
| 230 |
- return nil, nil |
|
| 231 |
- } |
|
| 232 |
- return frame, nil |
|
| 233 |
-} |
|
| 234 |
- |
|
| 235 |
-func (handler *hixiFrameHandler) WriteClose(_ int) (err error) {
|
|
| 236 |
- handler.conn.wio.Lock() |
|
| 237 |
- defer handler.conn.wio.Unlock() |
|
| 238 |
- closingFrame := []byte{'\xff', '\x00'}
|
|
| 239 |
- handler.conn.buf.Write(closingFrame) |
|
| 240 |
- return handler.conn.buf.Flush() |
|
| 241 |
-} |
|
| 242 |
- |
|
| 243 |
-// newHixiConn creates a new WebSocket connection speaking hixie draft protocol. |
|
| 244 |
-func newHixieConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
|
| 245 |
- if buf == nil {
|
|
| 246 |
- br := bufio.NewReader(rwc) |
|
| 247 |
- bw := bufio.NewWriter(rwc) |
|
| 248 |
- buf = bufio.NewReadWriter(br, bw) |
|
| 249 |
- } |
|
| 250 |
- ws := &Conn{config: config, request: request, buf: buf, rwc: rwc,
|
|
| 251 |
- frameReaderFactory: hixieFrameReaderFactory{buf.Reader},
|
|
| 252 |
- frameWriterFactory: hixiFrameWriterFactory{buf.Writer},
|
|
| 253 |
- PayloadType: TextFrame} |
|
| 254 |
- ws.frameHandler = &hixiFrameHandler{ws}
|
|
| 255 |
- return ws |
|
| 256 |
-} |
|
| 257 |
- |
|
| 258 |
-// getChallengeResponse computes the expected response from the |
|
| 259 |
-// challenge as described in section 5.1 Opening Handshake steps 42 to |
|
| 260 |
-// 43 of http://www.whatwg.org/specs/web-socket-protocol/ |
|
| 261 |
-func getChallengeResponse(number1, number2 uint32, key3 []byte) (expected []byte, err error) {
|
|
| 262 |
- // 41. Let /challenge/ be the concatenation of /number_1/, expressed |
|
| 263 |
- // a big-endian 32 bit integer, /number_2/, expressed in a big- |
|
| 264 |
- // endian 32 bit integer, and the eight bytes of /key_3/ in the |
|
| 265 |
- // order they were sent to the wire. |
|
| 266 |
- challenge := make([]byte, 16) |
|
| 267 |
- binary.BigEndian.PutUint32(challenge[0:], number1) |
|
| 268 |
- binary.BigEndian.PutUint32(challenge[4:], number2) |
|
| 269 |
- copy(challenge[8:], key3) |
|
| 270 |
- |
|
| 271 |
- // 42. Let /expected/ be the MD5 fingerprint of /challenge/ as a big- |
|
| 272 |
- // endian 128 bit string. |
|
| 273 |
- h := md5.New() |
|
| 274 |
- if _, err = h.Write(challenge); err != nil {
|
|
| 275 |
- return |
|
| 276 |
- } |
|
| 277 |
- expected = h.Sum(nil) |
|
| 278 |
- return |
|
| 279 |
-} |
|
| 280 |
- |
|
| 281 |
-// Generates handshake key as described in 4.1 Opening handshake step 16 to 22. |
|
| 282 |
-// cf. http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 |
|
| 283 |
-func generateKeyNumber() (key string, number uint32) {
|
|
| 284 |
- // 16. Let /spaces_n/ be a random integer from 1 to 12 inclusive. |
|
| 285 |
- spaces := rand.Intn(12) + 1 |
|
| 286 |
- |
|
| 287 |
- // 17. Let /max_n/ be the largest integer not greater than |
|
| 288 |
- // 4,294,967,295 divided by /spaces_n/ |
|
| 289 |
- max := int(4294967295 / uint32(spaces)) |
|
| 290 |
- |
|
| 291 |
- // 18. Let /number_n/ be a random integer from 0 to /max_n/ inclusive. |
|
| 292 |
- number = uint32(rand.Intn(max + 1)) |
|
| 293 |
- |
|
| 294 |
- // 19. Let /product_n/ be the result of multiplying /number_n/ and |
|
| 295 |
- // /spaces_n/ together. |
|
| 296 |
- product := number * uint32(spaces) |
|
| 297 |
- |
|
| 298 |
- // 20. Let /key_n/ be a string consisting of /product_n/, expressed |
|
| 299 |
- // in base ten using the numerals in the range U+0030 DIGIT ZERO (0) |
|
| 300 |
- // to U+0039 DIGIT NINE (9). |
|
| 301 |
- key = fmt.Sprintf("%d", product)
|
|
| 302 |
- |
|
| 303 |
- // 21. Insert between one and twelve random characters from the ranges |
|
| 304 |
- // U+0021 to U+002F and U+003A to U+007E into /key_n/ at random |
|
| 305 |
- // positions. |
|
| 306 |
- n := rand.Intn(12) + 1 |
|
| 307 |
- for i := 0; i < n; i++ {
|
|
| 308 |
- pos := rand.Intn(len(key)) + 1 |
|
| 309 |
- ch := secKeyRandomChars[rand.Intn(len(secKeyRandomChars))] |
|
| 310 |
- key = key[0:pos] + string(ch) + key[pos:] |
|
| 311 |
- } |
|
| 312 |
- |
|
| 313 |
- // 22. Insert /spaces_n/ U+0020 SPACE characters into /key_n/ at random |
|
| 314 |
- // positions other than the start or end of the string. |
|
| 315 |
- for i := 0; i < spaces; i++ {
|
|
| 316 |
- pos := rand.Intn(len(key)-1) + 1 |
|
| 317 |
- key = key[0:pos] + " " + key[pos:] |
|
| 318 |
- } |
|
| 319 |
- |
|
| 320 |
- return |
|
| 321 |
-} |
|
| 322 |
- |
|
| 323 |
-// Generates handshake key_3 as described in 4.1 Opening handshake step 26. |
|
| 324 |
-// cf. http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-00 |
|
| 325 |
-func generateKey3() (key []byte) {
|
|
| 326 |
- // 26. Let /key3/ be a string consisting of eight random bytes (or |
|
| 327 |
- // equivalently, a random 64 bit integer encoded in big-endian order). |
|
| 328 |
- key = make([]byte, 8) |
|
| 329 |
- for i := 0; i < 8; i++ {
|
|
| 330 |
- key[i] = byte(rand.Intn(256)) |
|
| 331 |
- } |
|
| 332 |
- return |
|
| 333 |
-} |
|
| 334 |
- |
|
| 335 |
-// Client handshake described in (soon obsolete) |
|
| 336 |
-// draft-ietf-hybi-thewebsocket-protocol-00 |
|
| 337 |
-// (draft-hixie-thewebsocket-protocol-76) |
|
| 338 |
-func hixie76ClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) {
|
|
| 339 |
- switch config.Version {
|
|
| 340 |
- case ProtocolVersionHixie76, ProtocolVersionHybi00: |
|
| 341 |
- default: |
|
| 342 |
- panic("wrong protocol version.")
|
|
| 343 |
- } |
|
| 344 |
- // 4.1. Opening handshake. |
|
| 345 |
- // Step 5. send a request line. |
|
| 346 |
- bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
|
|
| 347 |
- |
|
| 348 |
- // Step 6-14. push request headers in fields. |
|
| 349 |
- fields := []string{
|
|
| 350 |
- "Upgrade: WebSocket\r\n", |
|
| 351 |
- "Connection: Upgrade\r\n", |
|
| 352 |
- "Host: " + config.Location.Host + "\r\n", |
|
| 353 |
- "Origin: " + config.Origin.String() + "\r\n", |
|
| 354 |
- } |
|
| 355 |
- if len(config.Protocol) > 0 {
|
|
| 356 |
- if len(config.Protocol) != 1 {
|
|
| 357 |
- return ErrBadWebSocketProtocol |
|
| 358 |
- } |
|
| 359 |
- fields = append(fields, "Sec-WebSocket-Protocol: "+config.Protocol[0]+"\r\n") |
|
| 360 |
- } |
|
| 361 |
- // TODO(ukai): Step 15. send cookie if any. |
|
| 362 |
- |
|
| 363 |
- // Step 16-23. generate keys and push Sec-WebSocket-Key<n> in fields. |
|
| 364 |
- key1, number1 := generateKeyNumber() |
|
| 365 |
- key2, number2 := generateKeyNumber() |
|
| 366 |
- if config.handshakeData != nil {
|
|
| 367 |
- key1 = config.handshakeData["key1"] |
|
| 368 |
- n, err := strconv.ParseUint(config.handshakeData["number1"], 10, 32) |
|
| 369 |
- if err != nil {
|
|
| 370 |
- panic(err) |
|
| 371 |
- } |
|
| 372 |
- number1 = uint32(n) |
|
| 373 |
- key2 = config.handshakeData["key2"] |
|
| 374 |
- n, err = strconv.ParseUint(config.handshakeData["number2"], 10, 32) |
|
| 375 |
- if err != nil {
|
|
| 376 |
- panic(err) |
|
| 377 |
- } |
|
| 378 |
- number2 = uint32(n) |
|
| 379 |
- } |
|
| 380 |
- fields = append(fields, "Sec-WebSocket-Key1: "+key1+"\r\n") |
|
| 381 |
- fields = append(fields, "Sec-WebSocket-Key2: "+key2+"\r\n") |
|
| 382 |
- |
|
| 383 |
- // Step 24. shuffle fields and send them out. |
|
| 384 |
- for i := 1; i < len(fields); i++ {
|
|
| 385 |
- j := rand.Intn(i) |
|
| 386 |
- fields[i], fields[j] = fields[j], fields[i] |
|
| 387 |
- } |
|
| 388 |
- for i := 0; i < len(fields); i++ {
|
|
| 389 |
- bw.WriteString(fields[i]) |
|
| 390 |
- } |
|
| 391 |
- // Step 25. send CRLF. |
|
| 392 |
- bw.WriteString("\r\n")
|
|
| 393 |
- |
|
| 394 |
- // Step 26. generate 8 bytes random key. |
|
| 395 |
- key3 := generateKey3() |
|
| 396 |
- if config.handshakeData != nil {
|
|
| 397 |
- key3 = []byte(config.handshakeData["key3"]) |
|
| 398 |
- } |
|
| 399 |
- // Step 27. send it out. |
|
| 400 |
- bw.Write(key3) |
|
| 401 |
- if err = bw.Flush(); err != nil {
|
|
| 402 |
- return |
|
| 403 |
- } |
|
| 404 |
- |
|
| 405 |
- // Step 28-29, 32-40. read response from server. |
|
| 406 |
- resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
|
|
| 407 |
- if err != nil {
|
|
| 408 |
- return err |
|
| 409 |
- } |
|
| 410 |
- // Step 30. check response code is 101. |
|
| 411 |
- if resp.StatusCode != 101 {
|
|
| 412 |
- return ErrBadStatus |
|
| 413 |
- } |
|
| 414 |
- |
|
| 415 |
- // Step 41. check websocket headers. |
|
| 416 |
- if resp.Header.Get("Upgrade") != "WebSocket" ||
|
|
| 417 |
- strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
|
|
| 418 |
- return ErrBadUpgrade |
|
| 419 |
- } |
|
| 420 |
- |
|
| 421 |
- if resp.Header.Get("Sec-Websocket-Origin") != config.Origin.String() {
|
|
| 422 |
- return ErrBadWebSocketOrigin |
|
| 423 |
- } |
|
| 424 |
- |
|
| 425 |
- if resp.Header.Get("Sec-Websocket-Location") != config.Location.String() {
|
|
| 426 |
- return ErrBadWebSocketLocation |
|
| 427 |
- } |
|
| 428 |
- |
|
| 429 |
- if len(config.Protocol) > 0 && resp.Header.Get("Sec-Websocket-Protocol") != config.Protocol[0] {
|
|
| 430 |
- return ErrBadWebSocketProtocol |
|
| 431 |
- } |
|
| 432 |
- |
|
| 433 |
- // Step 42-43. get expected data from challenge data. |
|
| 434 |
- expected, err := getChallengeResponse(number1, number2, key3) |
|
| 435 |
- if err != nil {
|
|
| 436 |
- return err |
|
| 437 |
- } |
|
| 438 |
- |
|
| 439 |
- // Step 44. read 16 bytes from server. |
|
| 440 |
- reply := make([]byte, 16) |
|
| 441 |
- if _, err = io.ReadFull(br, reply); err != nil {
|
|
| 442 |
- return err |
|
| 443 |
- } |
|
| 444 |
- |
|
| 445 |
- // Step 45. check the reply equals to expected data. |
|
| 446 |
- if !bytes.Equal(expected, reply) {
|
|
| 447 |
- return ErrChallengeResponse |
|
| 448 |
- } |
|
| 449 |
- // WebSocket connection is established. |
|
| 450 |
- return |
|
| 451 |
-} |
|
| 452 |
- |
|
| 453 |
-// Client Handshake described in (soon obsolete) |
|
| 454 |
-// draft-hixie-thewebsocket-protocol-75. |
|
| 455 |
-func hixie75ClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) {
|
|
| 456 |
- if config.Version != ProtocolVersionHixie75 {
|
|
| 457 |
- panic("wrong protocol version.")
|
|
| 458 |
- } |
|
| 459 |
- bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
|
|
| 460 |
- bw.WriteString("Upgrade: WebSocket\r\n")
|
|
| 461 |
- bw.WriteString("Connection: Upgrade\r\n")
|
|
| 462 |
- bw.WriteString("Host: " + config.Location.Host + "\r\n")
|
|
| 463 |
- bw.WriteString("Origin: " + config.Origin.String() + "\r\n")
|
|
| 464 |
- if len(config.Protocol) > 0 {
|
|
| 465 |
- if len(config.Protocol) != 1 {
|
|
| 466 |
- return ErrBadWebSocketProtocol |
|
| 467 |
- } |
|
| 468 |
- bw.WriteString("WebSocket-Protocol: " + config.Protocol[0] + "\r\n")
|
|
| 469 |
- } |
|
| 470 |
- bw.WriteString("\r\n")
|
|
| 471 |
- bw.Flush() |
|
| 472 |
- resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
|
|
| 473 |
- if err != nil {
|
|
| 474 |
- return |
|
| 475 |
- } |
|
| 476 |
- if resp.Status != "101 Web Socket Protocol Handshake" {
|
|
| 477 |
- return ErrBadStatus |
|
| 478 |
- } |
|
| 479 |
- if resp.Header.Get("Upgrade") != "WebSocket" ||
|
|
| 480 |
- resp.Header.Get("Connection") != "Upgrade" {
|
|
| 481 |
- return ErrBadUpgrade |
|
| 482 |
- } |
|
| 483 |
- if resp.Header.Get("Websocket-Origin") != config.Origin.String() {
|
|
| 484 |
- return ErrBadWebSocketOrigin |
|
| 485 |
- } |
|
| 486 |
- if resp.Header.Get("Websocket-Location") != config.Location.String() {
|
|
| 487 |
- return ErrBadWebSocketLocation |
|
| 488 |
- } |
|
| 489 |
- if len(config.Protocol) > 0 && resp.Header.Get("Websocket-Protocol") != config.Protocol[0] {
|
|
| 490 |
- return ErrBadWebSocketProtocol |
|
| 491 |
- } |
|
| 492 |
- return |
|
| 493 |
-} |
|
| 494 |
- |
|
| 495 |
-// newHixieClientConn returns new WebSocket connection speaking hixie draft protocol. |
|
| 496 |
-func newHixieClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
|
|
| 497 |
- return newHixieConn(config, buf, rwc, nil) |
|
| 498 |
-} |
|
| 499 |
- |
|
| 500 |
-// Gets key number from Sec-WebSocket-Key<n>: field as described |
|
| 501 |
-// in 5.2 Sending the server's opening handshake, 4. |
|
| 502 |
-func getKeyNumber(s string) (r uint32) {
|
|
| 503 |
- // 4. Let /key-number_n/ be the digits (characters in the range |
|
| 504 |
- // U+0030 DIGIT ZERO (0) to U+0039 DIGIT NINE (9)) in /key_1/, |
|
| 505 |
- // interpreted as a base ten integer, ignoring all other characters |
|
| 506 |
- // in /key_n/. |
|
| 507 |
- r = 0 |
|
| 508 |
- for i := 0; i < len(s); i++ {
|
|
| 509 |
- if s[i] >= '0' && s[i] <= '9' {
|
|
| 510 |
- r = r*10 + uint32(s[i]) - '0' |
|
| 511 |
- } |
|
| 512 |
- } |
|
| 513 |
- return |
|
| 514 |
-} |
|
| 515 |
- |
|
| 516 |
-// A Hixie76ServerHandshaker performs a server handshake using |
|
| 517 |
-// hixie draft 76 protocol. |
|
| 518 |
-type hixie76ServerHandshaker struct {
|
|
| 519 |
- *Config |
|
| 520 |
- challengeResponse []byte |
|
| 521 |
-} |
|
| 522 |
- |
|
| 523 |
-func (c *hixie76ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) {
|
|
| 524 |
- c.Version = ProtocolVersionHybi00 |
|
| 525 |
- if req.Method != "GET" {
|
|
| 526 |
- return http.StatusMethodNotAllowed, ErrBadRequestMethod |
|
| 527 |
- } |
|
| 528 |
- // HTTP version can be safely ignored. |
|
| 529 |
- |
|
| 530 |
- if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
|
|
| 531 |
- strings.ToLower(req.Header.Get("Connection")) != "upgrade" {
|
|
| 532 |
- return http.StatusBadRequest, ErrNotWebSocket |
|
| 533 |
- } |
|
| 534 |
- |
|
| 535 |
- // TODO(ukai): check Host |
|
| 536 |
- c.Origin, err = url.ParseRequestURI(req.Header.Get("Origin"))
|
|
| 537 |
- if err != nil {
|
|
| 538 |
- return http.StatusBadRequest, err |
|
| 539 |
- } |
|
| 540 |
- |
|
| 541 |
- key1 := req.Header.Get("Sec-Websocket-Key1")
|
|
| 542 |
- if key1 == "" {
|
|
| 543 |
- return http.StatusBadRequest, ErrChallengeResponse |
|
| 544 |
- } |
|
| 545 |
- key2 := req.Header.Get("Sec-Websocket-Key2")
|
|
| 546 |
- if key2 == "" {
|
|
| 547 |
- return http.StatusBadRequest, ErrChallengeResponse |
|
| 548 |
- } |
|
| 549 |
- key3 := make([]byte, 8) |
|
| 550 |
- if _, err := io.ReadFull(buf, key3); err != nil {
|
|
| 551 |
- return http.StatusBadRequest, ErrChallengeResponse |
|
| 552 |
- } |
|
| 553 |
- |
|
| 554 |
- var scheme string |
|
| 555 |
- if req.TLS != nil {
|
|
| 556 |
- scheme = "wss" |
|
| 557 |
- } else {
|
|
| 558 |
- scheme = "ws" |
|
| 559 |
- } |
|
| 560 |
- c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) |
|
| 561 |
- if err != nil {
|
|
| 562 |
- return http.StatusBadRequest, err |
|
| 563 |
- } |
|
| 564 |
- |
|
| 565 |
- // Step 4. get key number in Sec-WebSocket-Key<n> fields. |
|
| 566 |
- keyNumber1 := getKeyNumber(key1) |
|
| 567 |
- keyNumber2 := getKeyNumber(key2) |
|
| 568 |
- |
|
| 569 |
- // Step 5. get number of spaces in Sec-WebSocket-Key<n> fields. |
|
| 570 |
- space1 := uint32(strings.Count(key1, " ")) |
|
| 571 |
- space2 := uint32(strings.Count(key2, " ")) |
|
| 572 |
- if space1 == 0 || space2 == 0 {
|
|
| 573 |
- return http.StatusBadRequest, ErrChallengeResponse |
|
| 574 |
- } |
|
| 575 |
- |
|
| 576 |
- // Step 6. key number must be an integral multiple of spaces. |
|
| 577 |
- if keyNumber1%space1 != 0 || keyNumber2%space2 != 0 {
|
|
| 578 |
- return http.StatusBadRequest, ErrChallengeResponse |
|
| 579 |
- } |
|
| 580 |
- |
|
| 581 |
- // Step 7. let part be key number divided by spaces. |
|
| 582 |
- part1 := keyNumber1 / space1 |
|
| 583 |
- part2 := keyNumber2 / space2 |
|
| 584 |
- |
|
| 585 |
- // Step 8. let challenge be concatenation of part1, part2 and key3. |
|
| 586 |
- // Step 9. get MD5 fingerprint of challenge. |
|
| 587 |
- c.challengeResponse, err = getChallengeResponse(part1, part2, key3) |
|
| 588 |
- if err != nil {
|
|
| 589 |
- return http.StatusInternalServerError, err |
|
| 590 |
- } |
|
| 591 |
- protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
|
|
| 592 |
- protocols := strings.Split(protocol, ",") |
|
| 593 |
- for i := 0; i < len(protocols); i++ {
|
|
| 594 |
- c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) |
|
| 595 |
- } |
|
| 596 |
- |
|
| 597 |
- return http.StatusSwitchingProtocols, nil |
|
| 598 |
-} |
|
| 599 |
- |
|
| 600 |
-func (c *hixie76ServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) {
|
|
| 601 |
- if len(c.Protocol) > 0 {
|
|
| 602 |
- if len(c.Protocol) != 1 {
|
|
| 603 |
- return ErrBadWebSocketProtocol |
|
| 604 |
- } |
|
| 605 |
- } |
|
| 606 |
- |
|
| 607 |
- // Step 10. send response status line. |
|
| 608 |
- buf.WriteString("HTTP/1.1 101 WebSocket Protocol Handshake\r\n")
|
|
| 609 |
- // Step 11. send response headers. |
|
| 610 |
- buf.WriteString("Upgrade: WebSocket\r\n")
|
|
| 611 |
- buf.WriteString("Connection: Upgrade\r\n")
|
|
| 612 |
- buf.WriteString("Sec-WebSocket-Origin: " + c.Origin.String() + "\r\n")
|
|
| 613 |
- buf.WriteString("Sec-WebSocket-Location: " + c.Location.String() + "\r\n")
|
|
| 614 |
- if len(c.Protocol) > 0 {
|
|
| 615 |
- buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
|
|
| 616 |
- } |
|
| 617 |
- // Step 12. send CRLF. |
|
| 618 |
- buf.WriteString("\r\n")
|
|
| 619 |
- // Step 13. send response data. |
|
| 620 |
- buf.Write(c.challengeResponse) |
|
| 621 |
- return buf.Flush() |
|
| 622 |
-} |
|
| 623 |
- |
|
| 624 |
-func (c *hixie76ServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) {
|
|
| 625 |
- return newHixieServerConn(c.Config, buf, rwc, request) |
|
| 626 |
-} |
|
| 627 |
- |
|
| 628 |
-// A hixie75ServerHandshaker performs a server handshake using |
|
| 629 |
-// hixie draft 75 protocol. |
|
| 630 |
-type hixie75ServerHandshaker struct {
|
|
| 631 |
- *Config |
|
| 632 |
-} |
|
| 633 |
- |
|
| 634 |
-func (c *hixie75ServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) {
|
|
| 635 |
- c.Version = ProtocolVersionHixie75 |
|
| 636 |
- if req.Method != "GET" || req.Proto != "HTTP/1.1" {
|
|
| 637 |
- return http.StatusMethodNotAllowed, ErrBadRequestMethod |
|
| 638 |
- } |
|
| 639 |
- if req.Header.Get("Upgrade") != "WebSocket" {
|
|
| 640 |
- return http.StatusBadRequest, ErrNotWebSocket |
|
| 641 |
- } |
|
| 642 |
- if req.Header.Get("Connection") != "Upgrade" {
|
|
| 643 |
- return http.StatusBadRequest, ErrNotWebSocket |
|
| 644 |
- } |
|
| 645 |
- c.Origin, err = url.ParseRequestURI(strings.TrimSpace(req.Header.Get("Origin")))
|
|
| 646 |
- if err != nil {
|
|
| 647 |
- return http.StatusBadRequest, err |
|
| 648 |
- } |
|
| 649 |
- |
|
| 650 |
- var scheme string |
|
| 651 |
- if req.TLS != nil {
|
|
| 652 |
- scheme = "wss" |
|
| 653 |
- } else {
|
|
| 654 |
- scheme = "ws" |
|
| 655 |
- } |
|
| 656 |
- c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) |
|
| 657 |
- if err != nil {
|
|
| 658 |
- return http.StatusBadRequest, err |
|
| 659 |
- } |
|
| 660 |
- protocol := strings.TrimSpace(req.Header.Get("Websocket-Protocol"))
|
|
| 661 |
- protocols := strings.Split(protocol, ",") |
|
| 662 |
- for i := 0; i < len(protocols); i++ {
|
|
| 663 |
- c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) |
|
| 664 |
- } |
|
| 665 |
- |
|
| 666 |
- return http.StatusSwitchingProtocols, nil |
|
| 667 |
-} |
|
| 668 |
- |
|
| 669 |
-func (c *hixie75ServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) {
|
|
| 670 |
- if len(c.Protocol) > 0 {
|
|
| 671 |
- if len(c.Protocol) != 1 {
|
|
| 672 |
- return ErrBadWebSocketProtocol |
|
| 673 |
- } |
|
| 674 |
- } |
|
| 675 |
- |
|
| 676 |
- buf.WriteString("HTTP/1.1 101 Web Socket Protocol Handshake\r\n")
|
|
| 677 |
- buf.WriteString("Upgrade: WebSocket\r\n")
|
|
| 678 |
- buf.WriteString("Connection: Upgrade\r\n")
|
|
| 679 |
- buf.WriteString("WebSocket-Origin: " + c.Origin.String() + "\r\n")
|
|
| 680 |
- buf.WriteString("WebSocket-Location: " + c.Location.String() + "\r\n")
|
|
| 681 |
- if len(c.Protocol) > 0 {
|
|
| 682 |
- buf.WriteString("WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
|
|
| 683 |
- } |
|
| 684 |
- buf.WriteString("\r\n")
|
|
| 685 |
- return buf.Flush() |
|
| 686 |
-} |
|
| 687 |
- |
|
| 688 |
-func (c *hixie75ServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) {
|
|
| 689 |
- return newHixieServerConn(c.Config, buf, rwc, request) |
|
| 690 |
-} |
|
| 691 |
- |
|
| 692 |
-// newHixieServerConn returns a new WebSocket connection speaking hixie draft protocol. |
|
| 693 |
-func newHixieServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
|
| 694 |
- return newHixieConn(config, buf, rwc, request) |
|
| 695 |
-} |
| 696 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,580 +0,0 @@ |
| 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. |
|
| 4 |
- |
|
| 5 |
-package websocket |
|
| 6 |
- |
|
| 7 |
-// This file implements a protocol of hybi draft. |
|
| 8 |
-// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 |
|
| 9 |
- |
|
| 10 |
-import ( |
|
| 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 |
- |
|
| 25 |
-const ( |
|
| 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 |
- |
|
| 43 |
-var ( |
|
| 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. |
|
| 63 |
-type 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. |
|
| 74 |
-type hybiFrameReader struct {
|
|
| 75 |
- reader io.Reader |
|
| 76 |
- |
|
| 77 |
- header hybiFrameHeader |
|
| 78 |
- pos int64 |
|
| 79 |
- length int |
|
| 80 |
-} |
|
| 81 |
- |
|
| 82 |
-func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) {
|
|
| 83 |
- n, err = frame.reader.Read(msg) |
|
| 84 |
- if err != nil {
|
|
| 85 |
- return 0, err |
|
| 86 |
- } |
|
| 87 |
- if frame.header.MaskingKey != nil {
|
|
| 88 |
- for i := 0; i < n; i++ {
|
|
| 89 |
- msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4] |
|
| 90 |
- frame.pos++ |
|
| 91 |
- } |
|
| 92 |
- } |
|
| 93 |
- return n, err |
|
| 94 |
-} |
|
| 95 |
- |
|
| 96 |
-func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode }
|
|
| 97 |
- |
|
| 98 |
-func (frame *hybiFrameReader) HeaderReader() io.Reader {
|
|
| 99 |
- if frame.header.data == nil {
|
|
| 100 |
- return nil |
|
| 101 |
- } |
|
| 102 |
- if frame.header.data.Len() == 0 {
|
|
| 103 |
- return nil |
|
| 104 |
- } |
|
| 105 |
- return frame.header.data |
|
| 106 |
-} |
|
| 107 |
- |
|
| 108 |
-func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil }
|
|
| 109 |
- |
|
| 110 |
-func (frame *hybiFrameReader) Len() (n int) { return frame.length }
|
|
| 111 |
- |
|
| 112 |
-// A hybiFrameReaderFactory creates new frame reader based on its frame type. |
|
| 113 |
-type hybiFrameReaderFactory struct {
|
|
| 114 |
- *bufio.Reader |
|
| 115 |
-} |
|
| 116 |
- |
|
| 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) |
|
| 122 |
- frame = hybiFrame |
|
| 123 |
- var header []byte |
|
| 124 |
- var b byte |
|
| 125 |
- // First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits) |
|
| 126 |
- b, err = buf.ReadByte() |
|
| 127 |
- if err != nil {
|
|
| 128 |
- return |
|
| 129 |
- } |
|
| 130 |
- header = append(header, b) |
|
| 131 |
- hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0 |
|
| 132 |
- for i := 0; i < 3; i++ {
|
|
| 133 |
- j := uint(6 - i) |
|
| 134 |
- hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0 |
|
| 135 |
- } |
|
| 136 |
- hybiFrame.header.OpCode = header[0] & 0x0f |
|
| 137 |
- |
|
| 138 |
- // Second byte. Mask/Payload len(7bits) |
|
| 139 |
- b, err = buf.ReadByte() |
|
| 140 |
- if err != nil {
|
|
| 141 |
- return |
|
| 142 |
- } |
|
| 143 |
- header = append(header, b) |
|
| 144 |
- mask := (b & 0x80) != 0 |
|
| 145 |
- b &= 0x7f |
|
| 146 |
- lengthFields := 0 |
|
| 147 |
- switch {
|
|
| 148 |
- case b <= 125: // Payload length 7bits. |
|
| 149 |
- hybiFrame.header.Length = int64(b) |
|
| 150 |
- case b == 126: // Payload length 7+16bits |
|
| 151 |
- lengthFields = 2 |
|
| 152 |
- case b == 127: // Payload length 7+64bits |
|
| 153 |
- lengthFields = 8 |
|
| 154 |
- } |
|
| 155 |
- for i := 0; i < lengthFields; i++ {
|
|
| 156 |
- b, err = buf.ReadByte() |
|
| 157 |
- if err != nil {
|
|
| 158 |
- return |
|
| 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. |
|
| 181 |
-type hybiFrameWriter struct {
|
|
| 182 |
- writer *bufio.Writer |
|
| 183 |
- |
|
| 184 |
- header *hybiFrameHeader |
|
| 185 |
-} |
|
| 186 |
- |
|
| 187 |
-func (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 |
- |
|
| 244 |
-func (frame *hybiFrameWriter) Close() error { return nil }
|
|
| 245 |
- |
|
| 246 |
-type hybiFrameWriterFactory struct {
|
|
| 247 |
- *bufio.Writer |
|
| 248 |
- needMaskingKey bool |
|
| 249 |
-} |
|
| 250 |
- |
|
| 251 |
-func (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 |
- |
|
| 262 |
-type hybiFrameHandler struct {
|
|
| 263 |
- conn *Conn |
|
| 264 |
- payloadType byte |
|
| 265 |
-} |
|
| 266 |
- |
|
| 267 |
-func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err 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: |
|
| 292 |
- pingMsg := make([]byte, maxControlFramePayloadLength) |
|
| 293 |
- n, err := io.ReadFull(frame, pingMsg) |
|
| 294 |
- if err != nil && err != io.ErrUnexpectedEOF {
|
|
| 295 |
- return nil, err |
|
| 296 |
- } |
|
| 297 |
- io.Copy(ioutil.Discard, frame) |
|
| 298 |
- n, err = handler.WritePong(pingMsg[:n]) |
|
| 299 |
- if err != nil {
|
|
| 300 |
- return nil, err |
|
| 301 |
- } |
|
| 302 |
- return nil, nil |
|
| 303 |
- case PongFrame: |
|
| 304 |
- return nil, ErrNotImplemented |
|
| 305 |
- } |
|
| 306 |
- return frame, nil |
|
| 307 |
-} |
|
| 308 |
- |
|
| 309 |
-func (handler *hybiFrameHandler) WriteClose(status int) (err error) {
|
|
| 310 |
- handler.conn.wio.Lock() |
|
| 311 |
- defer handler.conn.wio.Unlock() |
|
| 312 |
- w, err := handler.conn.frameWriterFactory.NewFrameWriter(CloseFrame) |
|
| 313 |
- if err != nil {
|
|
| 314 |
- return err |
|
| 315 |
- } |
|
| 316 |
- msg := make([]byte, 2) |
|
| 317 |
- binary.BigEndian.PutUint16(msg, uint16(status)) |
|
| 318 |
- _, err = w.Write(msg) |
|
| 319 |
- w.Close() |
|
| 320 |
- return err |
|
| 321 |
-} |
|
| 322 |
- |
|
| 323 |
-func (handler *hybiFrameHandler) WritePong(msg []byte) (n int, err error) {
|
|
| 324 |
- handler.conn.wio.Lock() |
|
| 325 |
- defer handler.conn.wio.Unlock() |
|
| 326 |
- w, err := handler.conn.frameWriterFactory.NewFrameWriter(PongFrame) |
|
| 327 |
- if err != nil {
|
|
| 328 |
- return 0, err |
|
| 329 |
- } |
|
| 330 |
- n, err = w.Write(msg) |
|
| 331 |
- w.Close() |
|
| 332 |
- return n, err |
|
| 333 |
-} |
|
| 334 |
- |
|
| 335 |
-// newHybiConn creates a new WebSocket connection speaking hybi draft protocol. |
|
| 336 |
-func newHybiConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
|
| 337 |
- if buf == nil {
|
|
| 338 |
- br := bufio.NewReader(rwc) |
|
| 339 |
- bw := bufio.NewWriter(rwc) |
|
| 340 |
- buf = bufio.NewReadWriter(br, bw) |
|
| 341 |
- } |
|
| 342 |
- ws := &Conn{config: config, request: request, buf: buf, rwc: rwc,
|
|
| 343 |
- frameReaderFactory: hybiFrameReaderFactory{buf.Reader},
|
|
| 344 |
- frameWriterFactory: hybiFrameWriterFactory{
|
|
| 345 |
- buf.Writer, request == nil}, |
|
| 346 |
- PayloadType: TextFrame, |
|
| 347 |
- defaultCloseStatus: closeStatusNormal} |
|
| 348 |
- ws.frameHandler = &hybiFrameHandler{conn: ws}
|
|
| 349 |
- return ws |
|
| 350 |
-} |
|
| 351 |
- |
|
| 352 |
-// generateMaskingKey generates a masking key for a frame. |
|
| 353 |
-func generateMaskingKey() (maskingKey []byte, err error) {
|
|
| 354 |
- maskingKey = make([]byte, 4) |
|
| 355 |
- if _, err = io.ReadFull(rand.Reader, maskingKey); err != nil {
|
|
| 356 |
- return |
|
| 357 |
- } |
|
| 358 |
- return |
|
| 359 |
-} |
|
| 360 |
- |
|
| 361 |
-// generateNonce generates a nonce consisting of a randomly selected 16-byte |
|
| 362 |
-// value that has been base64-encoded. |
|
| 363 |
-func generateNonce() (nonce []byte) {
|
|
| 364 |
- key := make([]byte, 16) |
|
| 365 |
- if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
|
| 366 |
- panic(err) |
|
| 367 |
- } |
|
| 368 |
- nonce = make([]byte, 24) |
|
| 369 |
- base64.StdEncoding.Encode(nonce, key) |
|
| 370 |
- return |
|
| 371 |
-} |
|
| 372 |
- |
|
| 373 |
-// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of |
|
| 374 |
-// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string.
|
|
| 375 |
-func getNonceAccept(nonce []byte) (expected []byte, err error) {
|
|
| 376 |
- h := sha1.New() |
|
| 377 |
- if _, err = h.Write(nonce); err != nil {
|
|
| 378 |
- return |
|
| 379 |
- } |
|
| 380 |
- if _, err = h.Write([]byte(websocketGUID)); err != nil {
|
|
| 381 |
- return |
|
| 382 |
- } |
|
| 383 |
- expected = make([]byte, 28) |
|
| 384 |
- base64.StdEncoding.Encode(expected, h.Sum(nil)) |
|
| 385 |
- return |
|
| 386 |
-} |
|
| 387 |
- |
|
| 388 |
-func isHybiVersion(version int) bool {
|
|
| 389 |
- switch version {
|
|
| 390 |
- case ProtocolVersionHybi08, ProtocolVersionHybi13: |
|
| 391 |
- return true |
|
| 392 |
- default: |
|
| 393 |
- } |
|
| 394 |
- return false |
|
| 395 |
-} |
|
| 396 |
- |
|
| 397 |
-// Client handshake described in draft-ietf-hybi-thewebsocket-protocol-17 |
|
| 398 |
-func hybiClientHandshake(config *Config, br *bufio.Reader, bw *bufio.Writer) (err error) {
|
|
| 399 |
- if !isHybiVersion(config.Version) {
|
|
| 400 |
- panic("wrong protocol version.")
|
|
| 401 |
- } |
|
| 402 |
- |
|
| 403 |
- bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
|
|
| 404 |
- |
|
| 405 |
- bw.WriteString("Host: " + config.Location.Host + "\r\n")
|
|
| 406 |
- bw.WriteString("Upgrade: websocket\r\n")
|
|
| 407 |
- bw.WriteString("Connection: Upgrade\r\n")
|
|
| 408 |
- nonce := generateNonce() |
|
| 409 |
- if config.handshakeData != nil {
|
|
| 410 |
- nonce = []byte(config.handshakeData["key"]) |
|
| 411 |
- } |
|
| 412 |
- bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n")
|
|
| 413 |
- if config.Version == ProtocolVersionHybi13 {
|
|
| 414 |
- bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n")
|
|
| 415 |
- } else if config.Version == ProtocolVersionHybi08 {
|
|
| 416 |
- bw.WriteString("Sec-WebSocket-Origin: " + strings.ToLower(config.Origin.String()) + "\r\n")
|
|
| 417 |
- } |
|
| 418 |
- bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n")
|
|
| 419 |
- if len(config.Protocol) > 0 {
|
|
| 420 |
- bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n")
|
|
| 421 |
- } |
|
| 422 |
- // TODO(ukai): send Sec-WebSocket-Extensions. |
|
| 423 |
- err = config.Header.WriteSubset(bw, handshakeHeader) |
|
| 424 |
- if err != nil {
|
|
| 425 |
- return err |
|
| 426 |
- } |
|
| 427 |
- |
|
| 428 |
- bw.WriteString("\r\n")
|
|
| 429 |
- if err = bw.Flush(); err != nil {
|
|
| 430 |
- return err |
|
| 431 |
- } |
|
| 432 |
- |
|
| 433 |
- resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
|
|
| 434 |
- if err != nil {
|
|
| 435 |
- return err |
|
| 436 |
- } |
|
| 437 |
- if resp.StatusCode != 101 {
|
|
| 438 |
- return ErrBadStatus |
|
| 439 |
- } |
|
| 440 |
- if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" ||
|
|
| 441 |
- strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
|
|
| 442 |
- return ErrBadUpgrade |
|
| 443 |
- } |
|
| 444 |
- expectedAccept, err := getNonceAccept(nonce) |
|
| 445 |
- if err != nil {
|
|
| 446 |
- return err |
|
| 447 |
- } |
|
| 448 |
- if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) {
|
|
| 449 |
- return ErrChallengeResponse |
|
| 450 |
- } |
|
| 451 |
- if resp.Header.Get("Sec-WebSocket-Extensions") != "" {
|
|
| 452 |
- return ErrUnsupportedExtensions |
|
| 453 |
- } |
|
| 454 |
- offeredProtocol := resp.Header.Get("Sec-WebSocket-Protocol")
|
|
| 455 |
- if offeredProtocol != "" {
|
|
| 456 |
- protocolMatched := false |
|
| 457 |
- for i := 0; i < len(config.Protocol); i++ {
|
|
| 458 |
- if config.Protocol[i] == offeredProtocol {
|
|
| 459 |
- protocolMatched = true |
|
| 460 |
- break |
|
| 461 |
- } |
|
| 462 |
- } |
|
| 463 |
- if !protocolMatched {
|
|
| 464 |
- return ErrBadWebSocketProtocol |
|
| 465 |
- } |
|
| 466 |
- config.Protocol = []string{offeredProtocol}
|
|
| 467 |
- } |
|
| 468 |
- |
|
| 469 |
- return nil |
|
| 470 |
-} |
|
| 471 |
- |
|
| 472 |
-// newHybiClientConn creates a client WebSocket connection after handshake. |
|
| 473 |
-func newHybiClientConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser) *Conn {
|
|
| 474 |
- return newHybiConn(config, buf, rwc, nil) |
|
| 475 |
-} |
|
| 476 |
- |
|
| 477 |
-// A HybiServerHandshaker performs a server handshake using hybi draft protocol. |
|
| 478 |
-type hybiServerHandshaker struct {
|
|
| 479 |
- *Config |
|
| 480 |
- accept []byte |
|
| 481 |
-} |
|
| 482 |
- |
|
| 483 |
-func (c *hybiServerHandshaker) ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) {
|
|
| 484 |
- c.Version = ProtocolVersionHybi13 |
|
| 485 |
- if req.Method != "GET" {
|
|
| 486 |
- return http.StatusMethodNotAllowed, ErrBadRequestMethod |
|
| 487 |
- } |
|
| 488 |
- // HTTP version can be safely ignored. |
|
| 489 |
- |
|
| 490 |
- if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
|
|
| 491 |
- !strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") {
|
|
| 492 |
- return http.StatusBadRequest, ErrNotWebSocket |
|
| 493 |
- } |
|
| 494 |
- |
|
| 495 |
- key := req.Header.Get("Sec-Websocket-Key")
|
|
| 496 |
- if key == "" {
|
|
| 497 |
- return http.StatusBadRequest, ErrChallengeResponse |
|
| 498 |
- } |
|
| 499 |
- version := req.Header.Get("Sec-Websocket-Version")
|
|
| 500 |
- switch version {
|
|
| 501 |
- case "13": |
|
| 502 |
- c.Version = ProtocolVersionHybi13 |
|
| 503 |
- case "8": |
|
| 504 |
- c.Version = ProtocolVersionHybi08 |
|
| 505 |
- default: |
|
| 506 |
- return http.StatusBadRequest, ErrBadWebSocketVersion |
|
| 507 |
- } |
|
| 508 |
- var scheme string |
|
| 509 |
- if req.TLS != nil {
|
|
| 510 |
- scheme = "wss" |
|
| 511 |
- } else {
|
|
| 512 |
- scheme = "ws" |
|
| 513 |
- } |
|
| 514 |
- c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) |
|
| 515 |
- if err != nil {
|
|
| 516 |
- return http.StatusBadRequest, err |
|
| 517 |
- } |
|
| 518 |
- protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
|
|
| 519 |
- if protocol != "" {
|
|
| 520 |
- protocols := strings.Split(protocol, ",") |
|
| 521 |
- for i := 0; i < len(protocols); i++ {
|
|
| 522 |
- c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) |
|
| 523 |
- } |
|
| 524 |
- } |
|
| 525 |
- c.accept, err = getNonceAccept([]byte(key)) |
|
| 526 |
- if err != nil {
|
|
| 527 |
- return http.StatusInternalServerError, err |
|
| 528 |
- } |
|
| 529 |
- return http.StatusSwitchingProtocols, nil |
|
| 530 |
-} |
|
| 531 |
- |
|
| 532 |
-// Origin parses Origin header in "req". |
|
| 533 |
-// If origin is "null", returns (nil, nil). |
|
| 534 |
-func Origin(config *Config, req *http.Request) (*url.URL, error) {
|
|
| 535 |
- var origin string |
|
| 536 |
- switch config.Version {
|
|
| 537 |
- case ProtocolVersionHybi13: |
|
| 538 |
- origin = req.Header.Get("Origin")
|
|
| 539 |
- case ProtocolVersionHybi08: |
|
| 540 |
- origin = req.Header.Get("Sec-Websocket-Origin")
|
|
| 541 |
- } |
|
| 542 |
- if origin == "null" {
|
|
| 543 |
- return nil, nil |
|
| 544 |
- } |
|
| 545 |
- return url.ParseRequestURI(origin) |
|
| 546 |
-} |
|
| 547 |
- |
|
| 548 |
-func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) {
|
|
| 549 |
- if len(c.Protocol) > 0 {
|
|
| 550 |
- if len(c.Protocol) != 1 {
|
|
| 551 |
- // You need choose a Protocol in Handshake func in Server. |
|
| 552 |
- return ErrBadWebSocketProtocol |
|
| 553 |
- } |
|
| 554 |
- } |
|
| 555 |
- buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
|
|
| 556 |
- buf.WriteString("Upgrade: websocket\r\n")
|
|
| 557 |
- buf.WriteString("Connection: Upgrade\r\n")
|
|
| 558 |
- buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n")
|
|
| 559 |
- if len(c.Protocol) > 0 {
|
|
| 560 |
- buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
|
|
| 561 |
- } |
|
| 562 |
- // TODO(ukai): send Sec-WebSocket-Extensions. |
|
| 563 |
- if c.Header != nil {
|
|
| 564 |
- err := c.Header.WriteSubset(buf, handshakeHeader) |
|
| 565 |
- if err != nil {
|
|
| 566 |
- return err |
|
| 567 |
- } |
|
| 568 |
- } |
|
| 569 |
- buf.WriteString("\r\n")
|
|
| 570 |
- return buf.Flush() |
|
| 571 |
-} |
|
| 572 |
- |
|
| 573 |
-func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
|
| 574 |
- return newHybiServerConn(c.Config, buf, rwc, request) |
|
| 575 |
-} |
|
| 576 |
- |
|
| 577 |
-// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol. |
|
| 578 |
-func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
|
| 579 |
- return newHybiConn(config, buf, rwc, request) |
|
| 580 |
-} |
| 581 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,122 +0,0 @@ |
| 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 |
|
| 6 |
- |
|
| 7 |
-import ( |
|
| 8 |
- "bufio" |
|
| 9 |
- "fmt" |
|
| 10 |
- "io" |
|
| 11 |
- "net/http" |
|
| 12 |
-) |
|
| 13 |
- |
|
| 14 |
-func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) {
|
|
| 15 |
- var hs serverHandshaker = &hybiServerHandshaker{Config: config}
|
|
| 16 |
- code, err := hs.ReadHandshake(buf.Reader, req) |
|
| 17 |
- if err == ErrBadWebSocketVersion {
|
|
| 18 |
- fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) |
|
| 19 |
- fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion) |
|
| 20 |
- buf.WriteString("\r\n")
|
|
| 21 |
- buf.WriteString(err.Error()) |
|
| 22 |
- buf.Flush() |
|
| 23 |
- return |
|
| 24 |
- } |
|
| 25 |
- if err != nil {
|
|
| 26 |
- hs = &hixie76ServerHandshaker{Config: config}
|
|
| 27 |
- code, err = hs.ReadHandshake(buf.Reader, req) |
|
| 28 |
- } |
|
| 29 |
- if err != nil {
|
|
| 30 |
- hs = &hixie75ServerHandshaker{Config: config}
|
|
| 31 |
- code, err = hs.ReadHandshake(buf.Reader, req) |
|
| 32 |
- } |
|
| 33 |
- if err != nil {
|
|
| 34 |
- fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) |
|
| 35 |
- buf.WriteString("\r\n")
|
|
| 36 |
- buf.WriteString(err.Error()) |
|
| 37 |
- buf.Flush() |
|
| 38 |
- return |
|
| 39 |
- } |
|
| 40 |
- if handshake != nil {
|
|
| 41 |
- err = handshake(config, req) |
|
| 42 |
- if err != nil {
|
|
| 43 |
- code = http.StatusForbidden |
|
| 44 |
- fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) |
|
| 45 |
- buf.WriteString("\r\n")
|
|
| 46 |
- buf.Flush() |
|
| 47 |
- return |
|
| 48 |
- } |
|
| 49 |
- } |
|
| 50 |
- err = hs.AcceptHandshake(buf.Writer) |
|
| 51 |
- if err != nil {
|
|
| 52 |
- code = http.StatusBadRequest |
|
| 53 |
- fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) |
|
| 54 |
- buf.WriteString("\r\n")
|
|
| 55 |
- buf.Flush() |
|
| 56 |
- return |
|
| 57 |
- } |
|
| 58 |
- conn = hs.NewServerConn(buf, rwc, req) |
|
| 59 |
- return |
|
| 60 |
-} |
|
| 61 |
- |
|
| 62 |
-// Server represents a server of a WebSocket. |
|
| 63 |
-type Server struct {
|
|
| 64 |
- // Config is a WebSocket configuration for new WebSocket connection. |
|
| 65 |
- Config |
|
| 66 |
- |
|
| 67 |
- // Handshake is an optional function in WebSocket handshake. |
|
| 68 |
- // For example, you can check, or don't check Origin header. |
|
| 69 |
- // Another example, you can select config.Protocol. |
|
| 70 |
- Handshake func(*Config, *http.Request) error |
|
| 71 |
- |
|
| 72 |
- // Handler handles a WebSocket connection. |
|
| 73 |
- Handler |
|
| 74 |
-} |
|
| 75 |
- |
|
| 76 |
-// ServeHTTP implements the http.Handler interface for a WebSocket |
|
| 77 |
-func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
| 78 |
- s.serveWebSocket(w, req) |
|
| 79 |
-} |
|
| 80 |
- |
|
| 81 |
-func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) {
|
|
| 82 |
- rwc, buf, err := w.(http.Hijacker).Hijack() |
|
| 83 |
- if err != nil {
|
|
| 84 |
- panic("Hijack failed: " + err.Error())
|
|
| 85 |
- return |
|
| 86 |
- } |
|
| 87 |
- // The server should abort the WebSocket connection if it finds |
|
| 88 |
- // the client did not send a handshake that matches with protocol |
|
| 89 |
- // specification. |
|
| 90 |
- defer rwc.Close() |
|
| 91 |
- conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake) |
|
| 92 |
- if err != nil {
|
|
| 93 |
- return |
|
| 94 |
- } |
|
| 95 |
- if conn == nil {
|
|
| 96 |
- panic("unexpected nil conn")
|
|
| 97 |
- } |
|
| 98 |
- s.Handler(conn) |
|
| 99 |
-} |
|
| 100 |
- |
|
| 101 |
-// Handler is a simple interface to a WebSocket browser client. |
|
| 102 |
-// It checks if Origin header is valid URL by default. |
|
| 103 |
-// You might want to verify websocket.Conn.Config().Origin in the func. |
|
| 104 |
-// If you use Server instead of Handler, you could call websocket.Origin and |
|
| 105 |
-// check the origin in your Handshake func. So, if you want to accept |
|
| 106 |
-// non-browser client, which doesn't send Origin header, you could use Server |
|
| 107 |
-//. that doesn't check origin in its Handshake. |
|
| 108 |
-type Handler func(*Conn) |
|
| 109 |
- |
|
| 110 |
-func checkOrigin(config *Config, req *http.Request) (err error) {
|
|
| 111 |
- config.Origin, err = Origin(config, req) |
|
| 112 |
- if err == nil && config.Origin == nil {
|
|
| 113 |
- return fmt.Errorf("null origin")
|
|
| 114 |
- } |
|
| 115 |
- return err |
|
| 116 |
-} |
|
| 117 |
- |
|
| 118 |
-// ServeHTTP implements the http.Handler interface for a WebSocket |
|
| 119 |
-func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
| 120 |
- s := Server{Handler: h, Handshake: checkOrigin}
|
|
| 121 |
- s.serveWebSocket(w, req) |
|
| 122 |
-} |
| 123 | 1 |
deleted file mode 100644 |
| ... | ... |
@@ -1,415 +0,0 @@ |
| 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 |
-package websocket |
|
| 8 |
- |
|
| 9 |
-import ( |
|
| 10 |
- "bufio" |
|
| 11 |
- "crypto/tls" |
|
| 12 |
- "encoding/json" |
|
| 13 |
- "errors" |
|
| 14 |
- "io" |
|
| 15 |
- "io/ioutil" |
|
| 16 |
- "net" |
|
| 17 |
- "net/http" |
|
| 18 |
- "net/url" |
|
| 19 |
- "sync" |
|
| 20 |
- "time" |
|
| 21 |
-) |
|
| 22 |
- |
|
| 23 |
-const ( |
|
| 24 |
- ProtocolVersionHixie75 = -75 |
|
| 25 |
- ProtocolVersionHixie76 = -76 |
|
| 26 |
- ProtocolVersionHybi00 = 0 |
|
| 27 |
- ProtocolVersionHybi08 = 8 |
|
| 28 |
- ProtocolVersionHybi13 = 13 |
|
| 29 |
- ProtocolVersionHybi = ProtocolVersionHybi13 |
|
| 30 |
- SupportedProtocolVersion = "13, 8" |
|
| 31 |
- |
|
| 32 |
- ContinuationFrame = 0 |
|
| 33 |
- TextFrame = 1 |
|
| 34 |
- BinaryFrame = 2 |
|
| 35 |
- CloseFrame = 8 |
|
| 36 |
- PingFrame = 9 |
|
| 37 |
- PongFrame = 10 |
|
| 38 |
- UnknownFrame = 255 |
|
| 39 |
-) |
|
| 40 |
- |
|
| 41 |
-// ProtocolError represents WebSocket protocol errors. |
|
| 42 |
-type ProtocolError struct {
|
|
| 43 |
- ErrorString string |
|
| 44 |
-} |
|
| 45 |
- |
|
| 46 |
-func (err *ProtocolError) Error() string { return err.ErrorString }
|
|
| 47 |
- |
|
| 48 |
-var ( |
|
| 49 |
- ErrBadProtocolVersion = &ProtocolError{"bad protocol version"}
|
|
| 50 |
- ErrBadScheme = &ProtocolError{"bad scheme"}
|
|
| 51 |
- ErrBadStatus = &ProtocolError{"bad status"}
|
|
| 52 |
- ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"}
|
|
| 53 |
- ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"}
|
|
| 54 |
- ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"}
|
|
| 55 |
- ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"}
|
|
| 56 |
- ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"}
|
|
| 57 |
- ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"}
|
|
| 58 |
- ErrBadFrame = &ProtocolError{"bad frame"}
|
|
| 59 |
- ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"}
|
|
| 60 |
- ErrNotWebSocket = &ProtocolError{"not websocket protocol"}
|
|
| 61 |
- ErrBadRequestMethod = &ProtocolError{"bad method"}
|
|
| 62 |
- ErrNotSupported = &ProtocolError{"not supported"}
|
|
| 63 |
-) |
|
| 64 |
- |
|
| 65 |
-// Addr is an implementation of net.Addr for WebSocket. |
|
| 66 |
-type Addr struct {
|
|
| 67 |
- *url.URL |
|
| 68 |
-} |
|
| 69 |
- |
|
| 70 |
-// Network returns the network type for a WebSocket, "websocket". |
|
| 71 |
-func (addr *Addr) Network() string { return "websocket" }
|
|
| 72 |
- |
|
| 73 |
-// Config is a WebSocket configuration |
|
| 74 |
-type Config struct {
|
|
| 75 |
- // A WebSocket server address. |
|
| 76 |
- Location *url.URL |
|
| 77 |
- |
|
| 78 |
- // A Websocket client origin. |
|
| 79 |
- Origin *url.URL |
|
| 80 |
- |
|
| 81 |
- // WebSocket subprotocols. |
|
| 82 |
- Protocol []string |
|
| 83 |
- |
|
| 84 |
- // WebSocket protocol version. |
|
| 85 |
- Version int |
|
| 86 |
- |
|
| 87 |
- // TLS config for secure WebSocket (wss). |
|
| 88 |
- TlsConfig *tls.Config |
|
| 89 |
- |
|
| 90 |
- // Additional header fields to be sent in WebSocket opening handshake. |
|
| 91 |
- Header http.Header |
|
| 92 |
- |
|
| 93 |
- handshakeData map[string]string |
|
| 94 |
-} |
|
| 95 |
- |
|
| 96 |
-// serverHandshaker is an interface to handle WebSocket server side handshake. |
|
| 97 |
-type serverHandshaker interface {
|
|
| 98 |
- // ReadHandshake reads handshake request message from client. |
|
| 99 |
- // Returns http response code and error if any. |
|
| 100 |
- ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) |
|
| 101 |
- |
|
| 102 |
- // AcceptHandshake accepts the client handshake request and sends |
|
| 103 |
- // handshake response back to client. |
|
| 104 |
- AcceptHandshake(buf *bufio.Writer) (err error) |
|
| 105 |
- |
|
| 106 |
- // NewServerConn creates a new WebSocket connection. |
|
| 107 |
- NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) |
|
| 108 |
-} |
|
| 109 |
- |
|
| 110 |
-// frameReader is an interface to read a WebSocket frame. |
|
| 111 |
-type frameReader interface {
|
|
| 112 |
- // Reader is to read payload of the frame. |
|
| 113 |
- io.Reader |
|
| 114 |
- |
|
| 115 |
- // PayloadType returns payload type. |
|
| 116 |
- PayloadType() byte |
|
| 117 |
- |
|
| 118 |
- // HeaderReader returns a reader to read header of the frame. |
|
| 119 |
- HeaderReader() io.Reader |
|
| 120 |
- |
|
| 121 |
- // TrailerReader returns a reader to read trailer of the frame. |
|
| 122 |
- // If it returns nil, there is no trailer in the frame. |
|
| 123 |
- TrailerReader() io.Reader |
|
| 124 |
- |
|
| 125 |
- // Len returns total length of the frame, including header and trailer. |
|
| 126 |
- Len() int |
|
| 127 |
-} |
|
| 128 |
- |
|
| 129 |
-// frameReaderFactory is an interface to creates new frame reader. |
|
| 130 |
-type frameReaderFactory interface {
|
|
| 131 |
- NewFrameReader() (r frameReader, err error) |
|
| 132 |
-} |
|
| 133 |
- |
|
| 134 |
-// frameWriter is an interface to write a WebSocket frame. |
|
| 135 |
-type frameWriter interface {
|
|
| 136 |
- // Writer is to write playload of the frame. |
|
| 137 |
- io.WriteCloser |
|
| 138 |
-} |
|
| 139 |
- |
|
| 140 |
-// frameWriterFactory is an interface to create new frame writer. |
|
| 141 |
-type frameWriterFactory interface {
|
|
| 142 |
- NewFrameWriter(payloadType byte) (w frameWriter, err error) |
|
| 143 |
-} |
|
| 144 |
- |
|
| 145 |
-type frameHandler interface {
|
|
| 146 |
- HandleFrame(frame frameReader) (r frameReader, err error) |
|
| 147 |
- WriteClose(status int) (err error) |
|
| 148 |
-} |
|
| 149 |
- |
|
| 150 |
-// Conn represents a WebSocket connection. |
|
| 151 |
-type Conn struct {
|
|
| 152 |
- config *Config |
|
| 153 |
- request *http.Request |
|
| 154 |
- |
|
| 155 |
- buf *bufio.ReadWriter |
|
| 156 |
- rwc io.ReadWriteCloser |
|
| 157 |
- |
|
| 158 |
- rio sync.Mutex |
|
| 159 |
- frameReaderFactory |
|
| 160 |
- frameReader |
|
| 161 |
- |
|
| 162 |
- wio sync.Mutex |
|
| 163 |
- frameWriterFactory |
|
| 164 |
- |
|
| 165 |
- frameHandler |
|
| 166 |
- PayloadType byte |
|
| 167 |
- defaultCloseStatus int |
|
| 168 |
-} |
|
| 169 |
- |
|
| 170 |
-// Read implements the io.Reader interface: |
|
| 171 |
-// it reads data of a frame from the WebSocket connection. |
|
| 172 |
-// if msg is not large enough for the frame data, it fills the msg and next Read |
|
| 173 |
-// will read the rest of the frame data. |
|
| 174 |
-// it reads Text frame or Binary frame. |
|
| 175 |
-func (ws *Conn) Read(msg []byte) (n int, err error) {
|
|
| 176 |
- ws.rio.Lock() |
|
| 177 |
- defer ws.rio.Unlock() |
|
| 178 |
-again: |
|
| 179 |
- if ws.frameReader == nil {
|
|
| 180 |
- frame, err := ws.frameReaderFactory.NewFrameReader() |
|
| 181 |
- if err != nil {
|
|
| 182 |
- return 0, err |
|
| 183 |
- } |
|
| 184 |
- ws.frameReader, err = ws.frameHandler.HandleFrame(frame) |
|
| 185 |
- if err != nil {
|
|
| 186 |
- return 0, err |
|
| 187 |
- } |
|
| 188 |
- if ws.frameReader == nil {
|
|
| 189 |
- goto again |
|
| 190 |
- } |
|
| 191 |
- } |
|
| 192 |
- n, err = ws.frameReader.Read(msg) |
|
| 193 |
- if err == io.EOF {
|
|
| 194 |
- if trailer := ws.frameReader.TrailerReader(); trailer != nil {
|
|
| 195 |
- io.Copy(ioutil.Discard, trailer) |
|
| 196 |
- } |
|
| 197 |
- ws.frameReader = nil |
|
| 198 |
- goto again |
|
| 199 |
- } |
|
| 200 |
- return n, err |
|
| 201 |
-} |
|
| 202 |
- |
|
| 203 |
-// Write implements the io.Writer interface: |
|
| 204 |
-// it writes data as a frame to the WebSocket connection. |
|
| 205 |
-func (ws *Conn) Write(msg []byte) (n int, err error) {
|
|
| 206 |
- ws.wio.Lock() |
|
| 207 |
- defer ws.wio.Unlock() |
|
| 208 |
- w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType) |
|
| 209 |
- if err != nil {
|
|
| 210 |
- return 0, err |
|
| 211 |
- } |
|
| 212 |
- n, err = w.Write(msg) |
|
| 213 |
- w.Close() |
|
| 214 |
- if err != nil {
|
|
| 215 |
- return n, err |
|
| 216 |
- } |
|
| 217 |
- return n, err |
|
| 218 |
-} |
|
| 219 |
- |
|
| 220 |
-// Close implements the io.Closer interface. |
|
| 221 |
-func (ws *Conn) Close() error {
|
|
| 222 |
- err := ws.frameHandler.WriteClose(ws.defaultCloseStatus) |
|
| 223 |
- if err != nil {
|
|
| 224 |
- return err |
|
| 225 |
- } |
|
| 226 |
- return ws.rwc.Close() |
|
| 227 |
-} |
|
| 228 |
- |
|
| 229 |
-func (ws *Conn) IsClientConn() bool { return ws.request == nil }
|
|
| 230 |
-func (ws *Conn) IsServerConn() bool { return ws.request != nil }
|
|
| 231 |
- |
|
| 232 |
-// LocalAddr returns the WebSocket Origin for the connection for client, or |
|
| 233 |
-// the WebSocket location for server. |
|
| 234 |
-func (ws *Conn) LocalAddr() net.Addr {
|
|
| 235 |
- if ws.IsClientConn() {
|
|
| 236 |
- return &Addr{ws.config.Origin}
|
|
| 237 |
- } |
|
| 238 |
- return &Addr{ws.config.Location}
|
|
| 239 |
-} |
|
| 240 |
- |
|
| 241 |
-// RemoteAddr returns the WebSocket location for the connection for client, or |
|
| 242 |
-// the Websocket Origin for server. |
|
| 243 |
-func (ws *Conn) RemoteAddr() net.Addr {
|
|
| 244 |
- if ws.IsClientConn() {
|
|
| 245 |
- return &Addr{ws.config.Location}
|
|
| 246 |
- } |
|
| 247 |
- return &Addr{ws.config.Origin}
|
|
| 248 |
-} |
|
| 249 |
- |
|
| 250 |
-var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn")
|
|
| 251 |
- |
|
| 252 |
-// SetDeadline sets the connection's network read & write deadlines. |
|
| 253 |
-func (ws *Conn) SetDeadline(t time.Time) error {
|
|
| 254 |
- if conn, ok := ws.rwc.(net.Conn); ok {
|
|
| 255 |
- return conn.SetDeadline(t) |
|
| 256 |
- } |
|
| 257 |
- return errSetDeadline |
|
| 258 |
-} |
|
| 259 |
- |
|
| 260 |
-// SetReadDeadline sets the connection's network read deadline. |
|
| 261 |
-func (ws *Conn) SetReadDeadline(t time.Time) error {
|
|
| 262 |
- if conn, ok := ws.rwc.(net.Conn); ok {
|
|
| 263 |
- return conn.SetReadDeadline(t) |
|
| 264 |
- } |
|
| 265 |
- return errSetDeadline |
|
| 266 |
-} |
|
| 267 |
- |
|
| 268 |
-// SetWriteDeadline sets the connection's network write deadline. |
|
| 269 |
-func (ws *Conn) SetWriteDeadline(t time.Time) error {
|
|
| 270 |
- if conn, ok := ws.rwc.(net.Conn); ok {
|
|
| 271 |
- return conn.SetWriteDeadline(t) |
|
| 272 |
- } |
|
| 273 |
- return errSetDeadline |
|
| 274 |
-} |
|
| 275 |
- |
|
| 276 |
-// Config returns the WebSocket config. |
|
| 277 |
-func (ws *Conn) Config() *Config { return ws.config }
|
|
| 278 |
- |
|
| 279 |
-// Request returns the http request upgraded to the WebSocket. |
|
| 280 |
-// It is nil for client side. |
|
| 281 |
-func (ws *Conn) Request() *http.Request { return ws.request }
|
|
| 282 |
- |
|
| 283 |
-// Codec represents a symmetric pair of functions that implement a codec. |
|
| 284 |
-type Codec struct {
|
|
| 285 |
- Marshal func(v interface{}) (data []byte, payloadType byte, err error)
|
|
| 286 |
- Unmarshal func(data []byte, payloadType byte, v interface{}) (err error)
|
|
| 287 |
-} |
|
| 288 |
- |
|
| 289 |
-// Send sends v marshaled by cd.Marshal as single frame to ws. |
|
| 290 |
-func (cd Codec) Send(ws *Conn, v interface{}) (err error) {
|
|
| 291 |
- data, payloadType, err := cd.Marshal(v) |
|
| 292 |
- if err != nil {
|
|
| 293 |
- return err |
|
| 294 |
- } |
|
| 295 |
- ws.wio.Lock() |
|
| 296 |
- defer ws.wio.Unlock() |
|
| 297 |
- w, err := ws.frameWriterFactory.NewFrameWriter(payloadType) |
|
| 298 |
- if err != nil {
|
|
| 299 |
- return err |
|
| 300 |
- } |
|
| 301 |
- _, err = w.Write(data) |
|
| 302 |
- w.Close() |
|
| 303 |
- return err |
|
| 304 |
-} |
|
| 305 |
- |
|
| 306 |
-// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores in v. |
|
| 307 |
-func (cd Codec) Receive(ws *Conn, v interface{}) (err error) {
|
|
| 308 |
- ws.rio.Lock() |
|
| 309 |
- defer ws.rio.Unlock() |
|
| 310 |
- if ws.frameReader != nil {
|
|
| 311 |
- _, err = io.Copy(ioutil.Discard, ws.frameReader) |
|
| 312 |
- if err != nil {
|
|
| 313 |
- return err |
|
| 314 |
- } |
|
| 315 |
- ws.frameReader = nil |
|
| 316 |
- } |
|
| 317 |
-again: |
|
| 318 |
- frame, err := ws.frameReaderFactory.NewFrameReader() |
|
| 319 |
- if err != nil {
|
|
| 320 |
- return err |
|
| 321 |
- } |
|
| 322 |
- frame, err = ws.frameHandler.HandleFrame(frame) |
|
| 323 |
- if err != nil {
|
|
| 324 |
- return err |
|
| 325 |
- } |
|
| 326 |
- if frame == nil {
|
|
| 327 |
- goto again |
|
| 328 |
- } |
|
| 329 |
- payloadType := frame.PayloadType() |
|
| 330 |
- data, err := ioutil.ReadAll(frame) |
|
| 331 |
- if err != nil {
|
|
| 332 |
- return err |
|
| 333 |
- } |
|
| 334 |
- return cd.Unmarshal(data, payloadType, v) |
|
| 335 |
-} |
|
| 336 |
- |
|
| 337 |
-func marshal(v interface{}) (msg []byte, payloadType byte, err error) {
|
|
| 338 |
- switch data := v.(type) {
|
|
| 339 |
- case string: |
|
| 340 |
- return []byte(data), TextFrame, nil |
|
| 341 |
- case []byte: |
|
| 342 |
- return data, BinaryFrame, nil |
|
| 343 |
- } |
|
| 344 |
- return nil, UnknownFrame, ErrNotSupported |
|
| 345 |
-} |
|
| 346 |
- |
|
| 347 |
-func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
|
|
| 348 |
- switch data := v.(type) {
|
|
| 349 |
- case *string: |
|
| 350 |
- *data = string(msg) |
|
| 351 |
- return nil |
|
| 352 |
- case *[]byte: |
|
| 353 |
- *data = msg |
|
| 354 |
- return nil |
|
| 355 |
- } |
|
| 356 |
- return ErrNotSupported |
|
| 357 |
-} |
|
| 358 |
- |
|
| 359 |
-/* |
|
| 360 |
-Message is a codec to send/receive text/binary data in a frame on WebSocket connection. |
|
| 361 |
-To send/receive text frame, use string type. |
|
| 362 |
-To send/receive binary frame, use []byte type. |
|
| 363 |
- |
|
| 364 |
-Trivial usage: |
|
| 365 |
- |
|
| 366 |
- import "websocket" |
|
| 367 |
- |
|
| 368 |
- // receive text frame |
|
| 369 |
- var message string |
|
| 370 |
- websocket.Message.Receive(ws, &message) |
|
| 371 |
- |
|
| 372 |
- // send text frame |
|
| 373 |
- message = "hello" |
|
| 374 |
- websocket.Message.Send(ws, message) |
|
| 375 |
- |
|
| 376 |
- // receive binary frame |
|
| 377 |
- var data []byte |
|
| 378 |
- websocket.Message.Receive(ws, &data) |
|
| 379 |
- |
|
| 380 |
- // send binary frame |
|
| 381 |
- data = []byte{0, 1, 2}
|
|
| 382 |
- websocket.Message.Send(ws, data) |
|
| 383 |
- |
|
| 384 |
-*/ |
|
| 385 |
-var Message = Codec{marshal, unmarshal}
|
|
| 386 |
- |
|
| 387 |
-func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) {
|
|
| 388 |
- msg, err = json.Marshal(v) |
|
| 389 |
- return msg, TextFrame, err |
|
| 390 |
-} |
|
| 391 |
- |
|
| 392 |
-func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
|
|
| 393 |
- return json.Unmarshal(msg, v) |
|
| 394 |
-} |
|
| 395 |
- |
|
| 396 |
-/* |
|
| 397 |
-JSON is a codec to send/receive JSON data in a frame from a WebSocket connection. |
|
| 398 |
- |
|
| 399 |
-Trivial usage: |
|
| 400 |
- |
|
| 401 |
- import "websocket" |
|
| 402 |
- |
|
| 403 |
- type T struct {
|
|
| 404 |
- Msg string |
|
| 405 |
- Count int |
|
| 406 |
- } |
|
| 407 |
- |
|
| 408 |
- // receive JSON type T |
|
| 409 |
- var data T |
|
| 410 |
- websocket.JSON.Receive(ws, &data) |
|
| 411 |
- |
|
| 412 |
- // send JSON type T |
|
| 413 |
- websocket.JSON.Send(ws, data) |
|
| 414 |
-*/ |
|
| 415 |
-var JSON = Codec{jsonMarshal, jsonUnmarshal}
|
| 416 | 1 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,113 @@ |
| 0 |
+// Copyright 2009 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+package websocket |
|
| 5 |
+ |
|
| 6 |
+import ( |
|
| 7 |
+ "bufio" |
|
| 8 |
+ "crypto/tls" |
|
| 9 |
+ "io" |
|
| 10 |
+ "net" |
|
| 11 |
+ "net/http" |
|
| 12 |
+ "net/url" |
|
| 13 |
+) |
|
| 14 |
+ |
|
| 15 |
+// DialError is an error that occurs while dialling a websocket server. |
|
| 16 |
+type DialError struct {
|
|
| 17 |
+ *Config |
|
| 18 |
+ Err error |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 21 |
+func (e *DialError) Error() string {
|
|
| 22 |
+ return "websocket.Dial " + e.Config.Location.String() + ": " + e.Err.Error() |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+// NewConfig creates a new WebSocket config for client connection. |
|
| 26 |
+func NewConfig(server, origin string) (config *Config, err error) {
|
|
| 27 |
+ config = new(Config) |
|
| 28 |
+ config.Version = ProtocolVersionHybi13 |
|
| 29 |
+ config.Location, err = url.ParseRequestURI(server) |
|
| 30 |
+ if err != nil {
|
|
| 31 |
+ return |
|
| 32 |
+ } |
|
| 33 |
+ config.Origin, err = url.ParseRequestURI(origin) |
|
| 34 |
+ if err != nil {
|
|
| 35 |
+ return |
|
| 36 |
+ } |
|
| 37 |
+ config.Header = http.Header(make(map[string][]string)) |
|
| 38 |
+ return |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+// NewClient creates a new WebSocket client connection over rwc. |
|
| 42 |
+func NewClient(config *Config, rwc io.ReadWriteCloser) (ws *Conn, err error) {
|
|
| 43 |
+ br := bufio.NewReader(rwc) |
|
| 44 |
+ bw := bufio.NewWriter(rwc) |
|
| 45 |
+ err = hybiClientHandshake(config, br, bw) |
|
| 46 |
+ if err != nil {
|
|
| 47 |
+ return |
|
| 48 |
+ } |
|
| 49 |
+ buf := bufio.NewReadWriter(br, bw) |
|
| 50 |
+ ws = newHybiClientConn(config, buf, rwc) |
|
| 51 |
+ return |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+// Dial opens a new client connection to a WebSocket. |
|
| 55 |
+func Dial(url_, protocol, origin string) (ws *Conn, err error) {
|
|
| 56 |
+ config, err := NewConfig(url_, origin) |
|
| 57 |
+ if err != nil {
|
|
| 58 |
+ return nil, err |
|
| 59 |
+ } |
|
| 60 |
+ if protocol != "" {
|
|
| 61 |
+ config.Protocol = []string{protocol}
|
|
| 62 |
+ } |
|
| 63 |
+ return DialConfig(config) |
|
| 64 |
+} |
|
| 65 |
+ |
|
| 66 |
+var portMap = map[string]string{
|
|
| 67 |
+ "ws": "80", |
|
| 68 |
+ "wss": "443", |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+func parseAuthority(location *url.URL) string {
|
|
| 72 |
+ if _, ok := portMap[location.Scheme]; ok {
|
|
| 73 |
+ if _, _, err := net.SplitHostPort(location.Host); err != nil {
|
|
| 74 |
+ return net.JoinHostPort(location.Host, portMap[location.Scheme]) |
|
| 75 |
+ } |
|
| 76 |
+ } |
|
| 77 |
+ return location.Host |
|
| 78 |
+} |
|
| 79 |
+ |
|
| 80 |
+// DialConfig opens a new client connection to a WebSocket with a config. |
|
| 81 |
+func DialConfig(config *Config) (ws *Conn, err error) {
|
|
| 82 |
+ var client net.Conn |
|
| 83 |
+ if config.Location == nil {
|
|
| 84 |
+ return nil, &DialError{config, ErrBadWebSocketLocation}
|
|
| 85 |
+ } |
|
| 86 |
+ if config.Origin == nil {
|
|
| 87 |
+ return nil, &DialError{config, ErrBadWebSocketOrigin}
|
|
| 88 |
+ } |
|
| 89 |
+ switch config.Location.Scheme {
|
|
| 90 |
+ case "ws": |
|
| 91 |
+ client, err = net.Dial("tcp", parseAuthority(config.Location))
|
|
| 92 |
+ |
|
| 93 |
+ case "wss": |
|
| 94 |
+ client, err = tls.Dial("tcp", parseAuthority(config.Location), config.TlsConfig)
|
|
| 95 |
+ |
|
| 96 |
+ default: |
|
| 97 |
+ err = ErrBadScheme |
|
| 98 |
+ } |
|
| 99 |
+ if err != nil {
|
|
| 100 |
+ goto Error |
|
| 101 |
+ } |
|
| 102 |
+ |
|
| 103 |
+ ws, err = NewClient(config, client) |
|
| 104 |
+ if err != nil {
|
|
| 105 |
+ client.Close() |
|
| 106 |
+ goto Error |
|
| 107 |
+ } |
|
| 108 |
+ return |
|
| 109 |
+ |
|
| 110 |
+Error: |
|
| 111 |
+ return nil, &DialError{config, err}
|
|
| 112 |
+} |
| 0 | 113 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,564 @@ |
| 0 |
+// Copyright 2011 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+package websocket |
|
| 5 |
+ |
|
| 6 |
+// This file implements a protocol of hybi draft. |
|
| 7 |
+// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17 |
|
| 8 |
+ |
|
| 9 |
+import ( |
|
| 10 |
+ "bufio" |
|
| 11 |
+ "bytes" |
|
| 12 |
+ "crypto/rand" |
|
| 13 |
+ "crypto/sha1" |
|
| 14 |
+ "encoding/base64" |
|
| 15 |
+ "encoding/binary" |
|
| 16 |
+ "fmt" |
|
| 17 |
+ "io" |
|
| 18 |
+ "io/ioutil" |
|
| 19 |
+ "net/http" |
|
| 20 |
+ "net/url" |
|
| 21 |
+ "strings" |
|
| 22 |
+) |
|
| 23 |
+ |
|
| 24 |
+const ( |
|
| 25 |
+ websocketGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" |
|
| 26 |
+ |
|
| 27 |
+ closeStatusNormal = 1000 |
|
| 28 |
+ closeStatusGoingAway = 1001 |
|
| 29 |
+ closeStatusProtocolError = 1002 |
|
| 30 |
+ closeStatusUnsupportedData = 1003 |
|
| 31 |
+ closeStatusFrameTooLarge = 1004 |
|
| 32 |
+ closeStatusNoStatusRcvd = 1005 |
|
| 33 |
+ closeStatusAbnormalClosure = 1006 |
|
| 34 |
+ closeStatusBadMessageData = 1007 |
|
| 35 |
+ closeStatusPolicyViolation = 1008 |
|
| 36 |
+ closeStatusTooBigData = 1009 |
|
| 37 |
+ closeStatusExtensionMismatch = 1010 |
|
| 38 |
+ |
|
| 39 |
+ maxControlFramePayloadLength = 125 |
|
| 40 |
+) |
|
| 41 |
+ |
|
| 42 |
+var ( |
|
| 43 |
+ ErrBadMaskingKey = &ProtocolError{"bad masking key"}
|
|
| 44 |
+ ErrBadPongMessage = &ProtocolError{"bad pong message"}
|
|
| 45 |
+ ErrBadClosingStatus = &ProtocolError{"bad closing status"}
|
|
| 46 |
+ ErrUnsupportedExtensions = &ProtocolError{"unsupported extensions"}
|
|
| 47 |
+ ErrNotImplemented = &ProtocolError{"not implemented"}
|
|
| 48 |
+ |
|
| 49 |
+ handshakeHeader = map[string]bool{
|
|
| 50 |
+ "Host": true, |
|
| 51 |
+ "Upgrade": true, |
|
| 52 |
+ "Connection": true, |
|
| 53 |
+ "Sec-Websocket-Key": true, |
|
| 54 |
+ "Sec-Websocket-Origin": true, |
|
| 55 |
+ "Sec-Websocket-Version": true, |
|
| 56 |
+ "Sec-Websocket-Protocol": true, |
|
| 57 |
+ "Sec-Websocket-Accept": true, |
|
| 58 |
+ } |
|
| 59 |
+) |
|
| 60 |
+ |
|
| 61 |
+// A hybiFrameHeader is a frame header as defined in hybi draft. |
|
| 62 |
+type hybiFrameHeader struct {
|
|
| 63 |
+ Fin bool |
|
| 64 |
+ Rsv [3]bool |
|
| 65 |
+ OpCode byte |
|
| 66 |
+ Length int64 |
|
| 67 |
+ MaskingKey []byte |
|
| 68 |
+ |
|
| 69 |
+ data *bytes.Buffer |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+// A hybiFrameReader is a reader for hybi frame. |
|
| 73 |
+type hybiFrameReader struct {
|
|
| 74 |
+ reader io.Reader |
|
| 75 |
+ |
|
| 76 |
+ header hybiFrameHeader |
|
| 77 |
+ pos int64 |
|
| 78 |
+ length int |
|
| 79 |
+} |
|
| 80 |
+ |
|
| 81 |
+func (frame *hybiFrameReader) Read(msg []byte) (n int, err error) {
|
|
| 82 |
+ n, err = frame.reader.Read(msg) |
|
| 83 |
+ if err != nil {
|
|
| 84 |
+ return 0, err |
|
| 85 |
+ } |
|
| 86 |
+ if frame.header.MaskingKey != nil {
|
|
| 87 |
+ for i := 0; i < n; i++ {
|
|
| 88 |
+ msg[i] = msg[i] ^ frame.header.MaskingKey[frame.pos%4] |
|
| 89 |
+ frame.pos++ |
|
| 90 |
+ } |
|
| 91 |
+ } |
|
| 92 |
+ return n, err |
|
| 93 |
+} |
|
| 94 |
+ |
|
| 95 |
+func (frame *hybiFrameReader) PayloadType() byte { return frame.header.OpCode }
|
|
| 96 |
+ |
|
| 97 |
+func (frame *hybiFrameReader) HeaderReader() io.Reader {
|
|
| 98 |
+ if frame.header.data == nil {
|
|
| 99 |
+ return nil |
|
| 100 |
+ } |
|
| 101 |
+ if frame.header.data.Len() == 0 {
|
|
| 102 |
+ return nil |
|
| 103 |
+ } |
|
| 104 |
+ return frame.header.data |
|
| 105 |
+} |
|
| 106 |
+ |
|
| 107 |
+func (frame *hybiFrameReader) TrailerReader() io.Reader { return nil }
|
|
| 108 |
+ |
|
| 109 |
+func (frame *hybiFrameReader) Len() (n int) { return frame.length }
|
|
| 110 |
+ |
|
| 111 |
+// A hybiFrameReaderFactory creates new frame reader based on its frame type. |
|
| 112 |
+type hybiFrameReaderFactory struct {
|
|
| 113 |
+ *bufio.Reader |
|
| 114 |
+} |
|
| 115 |
+ |
|
| 116 |
+// NewFrameReader reads a frame header from the connection, and creates new reader for the frame. |
|
| 117 |
+// See Section 5.2 Base Framing protocol for detail. |
|
| 118 |
+// http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17#section-5.2 |
|
| 119 |
+func (buf hybiFrameReaderFactory) NewFrameReader() (frame frameReader, err error) {
|
|
| 120 |
+ hybiFrame := new(hybiFrameReader) |
|
| 121 |
+ frame = hybiFrame |
|
| 122 |
+ var header []byte |
|
| 123 |
+ var b byte |
|
| 124 |
+ // First byte. FIN/RSV1/RSV2/RSV3/OpCode(4bits) |
|
| 125 |
+ b, err = buf.ReadByte() |
|
| 126 |
+ if err != nil {
|
|
| 127 |
+ return |
|
| 128 |
+ } |
|
| 129 |
+ header = append(header, b) |
|
| 130 |
+ hybiFrame.header.Fin = ((header[0] >> 7) & 1) != 0 |
|
| 131 |
+ for i := 0; i < 3; i++ {
|
|
| 132 |
+ j := uint(6 - i) |
|
| 133 |
+ hybiFrame.header.Rsv[i] = ((header[0] >> j) & 1) != 0 |
|
| 134 |
+ } |
|
| 135 |
+ hybiFrame.header.OpCode = header[0] & 0x0f |
|
| 136 |
+ |
|
| 137 |
+ // Second byte. Mask/Payload len(7bits) |
|
| 138 |
+ b, err = buf.ReadByte() |
|
| 139 |
+ if err != nil {
|
|
| 140 |
+ return |
|
| 141 |
+ } |
|
| 142 |
+ header = append(header, b) |
|
| 143 |
+ mask := (b & 0x80) != 0 |
|
| 144 |
+ b &= 0x7f |
|
| 145 |
+ lengthFields := 0 |
|
| 146 |
+ switch {
|
|
| 147 |
+ case b <= 125: // Payload length 7bits. |
|
| 148 |
+ hybiFrame.header.Length = int64(b) |
|
| 149 |
+ case b == 126: // Payload length 7+16bits |
|
| 150 |
+ lengthFields = 2 |
|
| 151 |
+ case b == 127: // Payload length 7+64bits |
|
| 152 |
+ lengthFields = 8 |
|
| 153 |
+ } |
|
| 154 |
+ for i := 0; i < lengthFields; i++ {
|
|
| 155 |
+ b, err = buf.ReadByte() |
|
| 156 |
+ if err != nil {
|
|
| 157 |
+ return |
|
| 158 |
+ } |
|
| 159 |
+ header = append(header, b) |
|
| 160 |
+ hybiFrame.header.Length = hybiFrame.header.Length*256 + int64(b) |
|
| 161 |
+ } |
|
| 162 |
+ if mask {
|
|
| 163 |
+ // Masking key. 4 bytes. |
|
| 164 |
+ for i := 0; i < 4; i++ {
|
|
| 165 |
+ b, err = buf.ReadByte() |
|
| 166 |
+ if err != nil {
|
|
| 167 |
+ return |
|
| 168 |
+ } |
|
| 169 |
+ header = append(header, b) |
|
| 170 |
+ hybiFrame.header.MaskingKey = append(hybiFrame.header.MaskingKey, b) |
|
| 171 |
+ } |
|
| 172 |
+ } |
|
| 173 |
+ hybiFrame.reader = io.LimitReader(buf.Reader, hybiFrame.header.Length) |
|
| 174 |
+ hybiFrame.header.data = bytes.NewBuffer(header) |
|
| 175 |
+ hybiFrame.length = len(header) + int(hybiFrame.header.Length) |
|
| 176 |
+ return |
|
| 177 |
+} |
|
| 178 |
+ |
|
| 179 |
+// A HybiFrameWriter is a writer for hybi frame. |
|
| 180 |
+type hybiFrameWriter struct {
|
|
| 181 |
+ writer *bufio.Writer |
|
| 182 |
+ |
|
| 183 |
+ header *hybiFrameHeader |
|
| 184 |
+} |
|
| 185 |
+ |
|
| 186 |
+func (frame *hybiFrameWriter) Write(msg []byte) (n int, err error) {
|
|
| 187 |
+ var header []byte |
|
| 188 |
+ var b byte |
|
| 189 |
+ if frame.header.Fin {
|
|
| 190 |
+ b |= 0x80 |
|
| 191 |
+ } |
|
| 192 |
+ for i := 0; i < 3; i++ {
|
|
| 193 |
+ if frame.header.Rsv[i] {
|
|
| 194 |
+ j := uint(6 - i) |
|
| 195 |
+ b |= 1 << j |
|
| 196 |
+ } |
|
| 197 |
+ } |
|
| 198 |
+ b |= frame.header.OpCode |
|
| 199 |
+ header = append(header, b) |
|
| 200 |
+ if frame.header.MaskingKey != nil {
|
|
| 201 |
+ b = 0x80 |
|
| 202 |
+ } else {
|
|
| 203 |
+ b = 0 |
|
| 204 |
+ } |
|
| 205 |
+ lengthFields := 0 |
|
| 206 |
+ length := len(msg) |
|
| 207 |
+ switch {
|
|
| 208 |
+ case length <= 125: |
|
| 209 |
+ b |= byte(length) |
|
| 210 |
+ case length < 65536: |
|
| 211 |
+ b |= 126 |
|
| 212 |
+ lengthFields = 2 |
|
| 213 |
+ default: |
|
| 214 |
+ b |= 127 |
|
| 215 |
+ lengthFields = 8 |
|
| 216 |
+ } |
|
| 217 |
+ header = append(header, b) |
|
| 218 |
+ for i := 0; i < lengthFields; i++ {
|
|
| 219 |
+ j := uint((lengthFields - i - 1) * 8) |
|
| 220 |
+ b = byte((length >> j) & 0xff) |
|
| 221 |
+ header = append(header, b) |
|
| 222 |
+ } |
|
| 223 |
+ if frame.header.MaskingKey != nil {
|
|
| 224 |
+ if len(frame.header.MaskingKey) != 4 {
|
|
| 225 |
+ return 0, ErrBadMaskingKey |
|
| 226 |
+ } |
|
| 227 |
+ header = append(header, frame.header.MaskingKey...) |
|
| 228 |
+ frame.writer.Write(header) |
|
| 229 |
+ data := make([]byte, length) |
|
| 230 |
+ for i := range data {
|
|
| 231 |
+ data[i] = msg[i] ^ frame.header.MaskingKey[i%4] |
|
| 232 |
+ } |
|
| 233 |
+ frame.writer.Write(data) |
|
| 234 |
+ err = frame.writer.Flush() |
|
| 235 |
+ return length, err |
|
| 236 |
+ } |
|
| 237 |
+ frame.writer.Write(header) |
|
| 238 |
+ frame.writer.Write(msg) |
|
| 239 |
+ err = frame.writer.Flush() |
|
| 240 |
+ return length, err |
|
| 241 |
+} |
|
| 242 |
+ |
|
| 243 |
+func (frame *hybiFrameWriter) Close() error { return nil }
|
|
| 244 |
+ |
|
| 245 |
+type hybiFrameWriterFactory struct {
|
|
| 246 |
+ *bufio.Writer |
|
| 247 |
+ needMaskingKey bool |
|
| 248 |
+} |
|
| 249 |
+ |
|
| 250 |
+func (buf hybiFrameWriterFactory) NewFrameWriter(payloadType byte) (frame frameWriter, err error) {
|
|
| 251 |
+ frameHeader := &hybiFrameHeader{Fin: true, OpCode: payloadType}
|
|
| 252 |
+ if buf.needMaskingKey {
|
|
| 253 |
+ frameHeader.MaskingKey, err = generateMaskingKey() |
|
| 254 |
+ if err != nil {
|
|
| 255 |
+ return nil, err |
|
| 256 |
+ } |
|
| 257 |
+ } |
|
| 258 |
+ return &hybiFrameWriter{writer: buf.Writer, header: frameHeader}, nil
|
|
| 259 |
+} |
|
| 260 |
+ |
|
| 261 |
+type hybiFrameHandler struct {
|
|
| 262 |
+ conn *Conn |
|
| 263 |
+ payloadType byte |
|
| 264 |
+} |
|
| 265 |
+ |
|
| 266 |
+func (handler *hybiFrameHandler) HandleFrame(frame frameReader) (r frameReader, err error) {
|
|
| 267 |
+ if handler.conn.IsServerConn() {
|
|
| 268 |
+ // The client MUST mask all frames sent to the server. |
|
| 269 |
+ if frame.(*hybiFrameReader).header.MaskingKey == nil {
|
|
| 270 |
+ handler.WriteClose(closeStatusProtocolError) |
|
| 271 |
+ return nil, io.EOF |
|
| 272 |
+ } |
|
| 273 |
+ } else {
|
|
| 274 |
+ // The server MUST NOT mask all frames. |
|
| 275 |
+ if frame.(*hybiFrameReader).header.MaskingKey != nil {
|
|
| 276 |
+ handler.WriteClose(closeStatusProtocolError) |
|
| 277 |
+ return nil, io.EOF |
|
| 278 |
+ } |
|
| 279 |
+ } |
|
| 280 |
+ if header := frame.HeaderReader(); header != nil {
|
|
| 281 |
+ io.Copy(ioutil.Discard, header) |
|
| 282 |
+ } |
|
| 283 |
+ switch frame.PayloadType() {
|
|
| 284 |
+ case ContinuationFrame: |
|
| 285 |
+ frame.(*hybiFrameReader).header.OpCode = handler.payloadType |
|
| 286 |
+ case TextFrame, BinaryFrame: |
|
| 287 |
+ handler.payloadType = frame.PayloadType() |
|
| 288 |
+ case CloseFrame: |
|
| 289 |
+ return nil, io.EOF |
|
| 290 |
+ case PingFrame: |
|
| 291 |
+ pingMsg := make([]byte, maxControlFramePayloadLength) |
|
| 292 |
+ n, err := io.ReadFull(frame, pingMsg) |
|
| 293 |
+ if err != nil && err != io.ErrUnexpectedEOF {
|
|
| 294 |
+ return nil, err |
|
| 295 |
+ } |
|
| 296 |
+ io.Copy(ioutil.Discard, frame) |
|
| 297 |
+ n, err = handler.WritePong(pingMsg[:n]) |
|
| 298 |
+ if err != nil {
|
|
| 299 |
+ return nil, err |
|
| 300 |
+ } |
|
| 301 |
+ return nil, nil |
|
| 302 |
+ case PongFrame: |
|
| 303 |
+ return nil, ErrNotImplemented |
|
| 304 |
+ } |
|
| 305 |
+ return frame, nil |
|
| 306 |
+} |
|
| 307 |
+ |
|
| 308 |
+func (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 |
+ |
|
| 322 |
+func (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. |
|
| 335 |
+func 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. |
|
| 352 |
+func 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. |
|
| 362 |
+func 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 |
+// getNonceAccept computes the base64-encoded SHA-1 of the concatenation of |
|
| 373 |
+// the nonce ("Sec-WebSocket-Key" value) with the websocket GUID string.
|
|
| 374 |
+func getNonceAccept(nonce []byte) (expected []byte, err error) {
|
|
| 375 |
+ h := sha1.New() |
|
| 376 |
+ if _, err = h.Write(nonce); err != nil {
|
|
| 377 |
+ return |
|
| 378 |
+ } |
|
| 379 |
+ if _, err = h.Write([]byte(websocketGUID)); err != nil {
|
|
| 380 |
+ return |
|
| 381 |
+ } |
|
| 382 |
+ expected = make([]byte, 28) |
|
| 383 |
+ base64.StdEncoding.Encode(expected, h.Sum(nil)) |
|
| 384 |
+ return |
|
| 385 |
+} |
|
| 386 |
+ |
|
| 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 |
+ bw.WriteString("GET " + config.Location.RequestURI() + " HTTP/1.1\r\n")
|
|
| 390 |
+ |
|
| 391 |
+ bw.WriteString("Host: " + config.Location.Host + "\r\n")
|
|
| 392 |
+ bw.WriteString("Upgrade: websocket\r\n")
|
|
| 393 |
+ bw.WriteString("Connection: Upgrade\r\n")
|
|
| 394 |
+ nonce := generateNonce() |
|
| 395 |
+ if config.handshakeData != nil {
|
|
| 396 |
+ nonce = []byte(config.handshakeData["key"]) |
|
| 397 |
+ } |
|
| 398 |
+ bw.WriteString("Sec-WebSocket-Key: " + string(nonce) + "\r\n")
|
|
| 399 |
+ bw.WriteString("Origin: " + strings.ToLower(config.Origin.String()) + "\r\n")
|
|
| 400 |
+ |
|
| 401 |
+ if config.Version != ProtocolVersionHybi13 {
|
|
| 402 |
+ return ErrBadProtocolVersion |
|
| 403 |
+ } |
|
| 404 |
+ |
|
| 405 |
+ bw.WriteString("Sec-WebSocket-Version: " + fmt.Sprintf("%d", config.Version) + "\r\n")
|
|
| 406 |
+ if len(config.Protocol) > 0 {
|
|
| 407 |
+ bw.WriteString("Sec-WebSocket-Protocol: " + strings.Join(config.Protocol, ", ") + "\r\n")
|
|
| 408 |
+ } |
|
| 409 |
+ // TODO(ukai): send Sec-WebSocket-Extensions. |
|
| 410 |
+ err = config.Header.WriteSubset(bw, handshakeHeader) |
|
| 411 |
+ if err != nil {
|
|
| 412 |
+ return err |
|
| 413 |
+ } |
|
| 414 |
+ |
|
| 415 |
+ bw.WriteString("\r\n")
|
|
| 416 |
+ if err = bw.Flush(); err != nil {
|
|
| 417 |
+ return err |
|
| 418 |
+ } |
|
| 419 |
+ |
|
| 420 |
+ resp, err := http.ReadResponse(br, &http.Request{Method: "GET"})
|
|
| 421 |
+ if err != nil {
|
|
| 422 |
+ return err |
|
| 423 |
+ } |
|
| 424 |
+ if resp.StatusCode != 101 {
|
|
| 425 |
+ return ErrBadStatus |
|
| 426 |
+ } |
|
| 427 |
+ if strings.ToLower(resp.Header.Get("Upgrade")) != "websocket" ||
|
|
| 428 |
+ strings.ToLower(resp.Header.Get("Connection")) != "upgrade" {
|
|
| 429 |
+ return ErrBadUpgrade |
|
| 430 |
+ } |
|
| 431 |
+ expectedAccept, err := getNonceAccept(nonce) |
|
| 432 |
+ if err != nil {
|
|
| 433 |
+ return err |
|
| 434 |
+ } |
|
| 435 |
+ if resp.Header.Get("Sec-WebSocket-Accept") != string(expectedAccept) {
|
|
| 436 |
+ return ErrChallengeResponse |
|
| 437 |
+ } |
|
| 438 |
+ if resp.Header.Get("Sec-WebSocket-Extensions") != "" {
|
|
| 439 |
+ return ErrUnsupportedExtensions |
|
| 440 |
+ } |
|
| 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 |
|
| 447 |
+ break |
|
| 448 |
+ } |
|
| 449 |
+ } |
|
| 450 |
+ if !protocolMatched {
|
|
| 451 |
+ return ErrBadWebSocketProtocol |
|
| 452 |
+ } |
|
| 453 |
+ config.Protocol = []string{offeredProtocol}
|
|
| 454 |
+ } |
|
| 455 |
+ |
|
| 456 |
+ return nil |
|
| 457 |
+} |
|
| 458 |
+ |
|
| 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) |
|
| 462 |
+} |
|
| 463 |
+ |
|
| 464 |
+// A HybiServerHandshaker performs a server handshake using hybi draft protocol. |
|
| 465 |
+type hybiServerHandshaker struct {
|
|
| 466 |
+ *Config |
|
| 467 |
+ accept []byte |
|
| 468 |
+} |
|
| 469 |
+ |
|
| 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 |
|
| 474 |
+ } |
|
| 475 |
+ // HTTP version can be safely ignored. |
|
| 476 |
+ |
|
| 477 |
+ if strings.ToLower(req.Header.Get("Upgrade")) != "websocket" ||
|
|
| 478 |
+ !strings.Contains(strings.ToLower(req.Header.Get("Connection")), "upgrade") {
|
|
| 479 |
+ return http.StatusBadRequest, ErrNotWebSocket |
|
| 480 |
+ } |
|
| 481 |
+ |
|
| 482 |
+ key := req.Header.Get("Sec-Websocket-Key")
|
|
| 483 |
+ if key == "" {
|
|
| 484 |
+ return http.StatusBadRequest, ErrChallengeResponse |
|
| 485 |
+ } |
|
| 486 |
+ version := req.Header.Get("Sec-Websocket-Version")
|
|
| 487 |
+ switch version {
|
|
| 488 |
+ case "13": |
|
| 489 |
+ c.Version = ProtocolVersionHybi13 |
|
| 490 |
+ default: |
|
| 491 |
+ return http.StatusBadRequest, ErrBadWebSocketVersion |
|
| 492 |
+ } |
|
| 493 |
+ var scheme string |
|
| 494 |
+ if req.TLS != nil {
|
|
| 495 |
+ scheme = "wss" |
|
| 496 |
+ } else {
|
|
| 497 |
+ scheme = "ws" |
|
| 498 |
+ } |
|
| 499 |
+ c.Location, err = url.ParseRequestURI(scheme + "://" + req.Host + req.URL.RequestURI()) |
|
| 500 |
+ if err != nil {
|
|
| 501 |
+ return http.StatusBadRequest, err |
|
| 502 |
+ } |
|
| 503 |
+ protocol := strings.TrimSpace(req.Header.Get("Sec-Websocket-Protocol"))
|
|
| 504 |
+ if protocol != "" {
|
|
| 505 |
+ protocols := strings.Split(protocol, ",") |
|
| 506 |
+ for i := 0; i < len(protocols); i++ {
|
|
| 507 |
+ c.Protocol = append(c.Protocol, strings.TrimSpace(protocols[i])) |
|
| 508 |
+ } |
|
| 509 |
+ } |
|
| 510 |
+ c.accept, err = getNonceAccept([]byte(key)) |
|
| 511 |
+ if err != nil {
|
|
| 512 |
+ return http.StatusInternalServerError, err |
|
| 513 |
+ } |
|
| 514 |
+ return http.StatusSwitchingProtocols, nil |
|
| 515 |
+} |
|
| 516 |
+ |
|
| 517 |
+// Origin parses Origin header in "req". |
|
| 518 |
+// If origin is "null", returns (nil, nil). |
|
| 519 |
+func Origin(config *Config, req *http.Request) (*url.URL, error) {
|
|
| 520 |
+ var origin string |
|
| 521 |
+ switch config.Version {
|
|
| 522 |
+ case ProtocolVersionHybi13: |
|
| 523 |
+ origin = req.Header.Get("Origin")
|
|
| 524 |
+ } |
|
| 525 |
+ if origin == "null" {
|
|
| 526 |
+ return nil, nil |
|
| 527 |
+ } |
|
| 528 |
+ return url.ParseRequestURI(origin) |
|
| 529 |
+} |
|
| 530 |
+ |
|
| 531 |
+func (c *hybiServerHandshaker) AcceptHandshake(buf *bufio.Writer) (err error) {
|
|
| 532 |
+ if len(c.Protocol) > 0 {
|
|
| 533 |
+ if len(c.Protocol) != 1 {
|
|
| 534 |
+ // You need choose a Protocol in Handshake func in Server. |
|
| 535 |
+ return ErrBadWebSocketProtocol |
|
| 536 |
+ } |
|
| 537 |
+ } |
|
| 538 |
+ buf.WriteString("HTTP/1.1 101 Switching Protocols\r\n")
|
|
| 539 |
+ buf.WriteString("Upgrade: websocket\r\n")
|
|
| 540 |
+ buf.WriteString("Connection: Upgrade\r\n")
|
|
| 541 |
+ buf.WriteString("Sec-WebSocket-Accept: " + string(c.accept) + "\r\n")
|
|
| 542 |
+ if len(c.Protocol) > 0 {
|
|
| 543 |
+ buf.WriteString("Sec-WebSocket-Protocol: " + c.Protocol[0] + "\r\n")
|
|
| 544 |
+ } |
|
| 545 |
+ // TODO(ukai): send Sec-WebSocket-Extensions. |
|
| 546 |
+ if c.Header != nil {
|
|
| 547 |
+ err := c.Header.WriteSubset(buf, handshakeHeader) |
|
| 548 |
+ if err != nil {
|
|
| 549 |
+ return err |
|
| 550 |
+ } |
|
| 551 |
+ } |
|
| 552 |
+ buf.WriteString("\r\n")
|
|
| 553 |
+ return buf.Flush() |
|
| 554 |
+} |
|
| 555 |
+ |
|
| 556 |
+func (c *hybiServerHandshaker) NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
|
| 557 |
+ return newHybiServerConn(c.Config, buf, rwc, request) |
|
| 558 |
+} |
|
| 559 |
+ |
|
| 560 |
+// newHybiServerConn returns a new WebSocket connection speaking hybi draft protocol. |
|
| 561 |
+func newHybiServerConn(config *Config, buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) *Conn {
|
|
| 562 |
+ return newHybiConn(config, buf, rwc, request) |
|
| 563 |
+} |
| 0 | 564 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,114 @@ |
| 0 |
+// Copyright 2009 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+package websocket |
|
| 5 |
+ |
|
| 6 |
+import ( |
|
| 7 |
+ "bufio" |
|
| 8 |
+ "fmt" |
|
| 9 |
+ "io" |
|
| 10 |
+ "net/http" |
|
| 11 |
+) |
|
| 12 |
+ |
|
| 13 |
+func newServerConn(rwc io.ReadWriteCloser, buf *bufio.ReadWriter, req *http.Request, config *Config, handshake func(*Config, *http.Request) error) (conn *Conn, err error) {
|
|
| 14 |
+ var hs serverHandshaker = &hybiServerHandshaker{Config: config}
|
|
| 15 |
+ code, err := hs.ReadHandshake(buf.Reader, req) |
|
| 16 |
+ if err == ErrBadWebSocketVersion {
|
|
| 17 |
+ fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) |
|
| 18 |
+ fmt.Fprintf(buf, "Sec-WebSocket-Version: %s\r\n", SupportedProtocolVersion) |
|
| 19 |
+ buf.WriteString("\r\n")
|
|
| 20 |
+ buf.WriteString(err.Error()) |
|
| 21 |
+ buf.Flush() |
|
| 22 |
+ return |
|
| 23 |
+ } |
|
| 24 |
+ if err != nil {
|
|
| 25 |
+ fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) |
|
| 26 |
+ buf.WriteString("\r\n")
|
|
| 27 |
+ buf.WriteString(err.Error()) |
|
| 28 |
+ buf.Flush() |
|
| 29 |
+ return |
|
| 30 |
+ } |
|
| 31 |
+ if handshake != nil {
|
|
| 32 |
+ err = handshake(config, req) |
|
| 33 |
+ if err != nil {
|
|
| 34 |
+ code = http.StatusForbidden |
|
| 35 |
+ fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) |
|
| 36 |
+ buf.WriteString("\r\n")
|
|
| 37 |
+ buf.Flush() |
|
| 38 |
+ return |
|
| 39 |
+ } |
|
| 40 |
+ } |
|
| 41 |
+ err = hs.AcceptHandshake(buf.Writer) |
|
| 42 |
+ if err != nil {
|
|
| 43 |
+ code = http.StatusBadRequest |
|
| 44 |
+ fmt.Fprintf(buf, "HTTP/1.1 %03d %s\r\n", code, http.StatusText(code)) |
|
| 45 |
+ buf.WriteString("\r\n")
|
|
| 46 |
+ buf.Flush() |
|
| 47 |
+ return |
|
| 48 |
+ } |
|
| 49 |
+ conn = hs.NewServerConn(buf, rwc, req) |
|
| 50 |
+ return |
|
| 51 |
+} |
|
| 52 |
+ |
|
| 53 |
+// Server represents a server of a WebSocket. |
|
| 54 |
+type Server struct {
|
|
| 55 |
+ // Config is a WebSocket configuration for new WebSocket connection. |
|
| 56 |
+ Config |
|
| 57 |
+ |
|
| 58 |
+ // Handshake is an optional function in WebSocket handshake. |
|
| 59 |
+ // For example, you can check, or don't check Origin header. |
|
| 60 |
+ // Another example, you can select config.Protocol. |
|
| 61 |
+ Handshake func(*Config, *http.Request) error |
|
| 62 |
+ |
|
| 63 |
+ // Handler handles a WebSocket connection. |
|
| 64 |
+ Handler |
|
| 65 |
+} |
|
| 66 |
+ |
|
| 67 |
+// ServeHTTP implements the http.Handler interface for a WebSocket |
|
| 68 |
+func (s Server) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
| 69 |
+ s.serveWebSocket(w, req) |
|
| 70 |
+} |
|
| 71 |
+ |
|
| 72 |
+func (s Server) serveWebSocket(w http.ResponseWriter, req *http.Request) {
|
|
| 73 |
+ rwc, buf, err := w.(http.Hijacker).Hijack() |
|
| 74 |
+ if err != nil {
|
|
| 75 |
+ panic("Hijack failed: " + err.Error())
|
|
| 76 |
+ return |
|
| 77 |
+ } |
|
| 78 |
+ // The server should abort the WebSocket connection if it finds |
|
| 79 |
+ // the client did not send a handshake that matches with protocol |
|
| 80 |
+ // specification. |
|
| 81 |
+ defer rwc.Close() |
|
| 82 |
+ conn, err := newServerConn(rwc, buf, req, &s.Config, s.Handshake) |
|
| 83 |
+ if err != nil {
|
|
| 84 |
+ return |
|
| 85 |
+ } |
|
| 86 |
+ if conn == nil {
|
|
| 87 |
+ panic("unexpected nil conn")
|
|
| 88 |
+ } |
|
| 89 |
+ s.Handler(conn) |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+// Handler is a simple interface to a WebSocket browser client. |
|
| 93 |
+// It checks if Origin header is valid URL by default. |
|
| 94 |
+// You might want to verify websocket.Conn.Config().Origin in the func. |
|
| 95 |
+// If you use Server instead of Handler, you could call websocket.Origin and |
|
| 96 |
+// check the origin in your Handshake func. So, if you want to accept |
|
| 97 |
+// non-browser clients, which do not send an Origin header, set a |
|
| 98 |
+// Server.Handshake that does not check the origin. |
|
| 99 |
+type Handler func(*Conn) |
|
| 100 |
+ |
|
| 101 |
+func checkOrigin(config *Config, req *http.Request) (err error) {
|
|
| 102 |
+ config.Origin, err = Origin(config, req) |
|
| 103 |
+ if err == nil && config.Origin == nil {
|
|
| 104 |
+ return fmt.Errorf("null origin")
|
|
| 105 |
+ } |
|
| 106 |
+ return err |
|
| 107 |
+} |
|
| 108 |
+ |
|
| 109 |
+// ServeHTTP implements the http.Handler interface for a WebSocket |
|
| 110 |
+func (h Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
|
|
| 111 |
+ s := Server{Handler: h, Handshake: checkOrigin}
|
|
| 112 |
+ s.serveWebSocket(w, req) |
|
| 113 |
+} |
| 0 | 114 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,412 @@ |
| 0 |
+// Copyright 2009 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+// Package websocket implements a client and server for the WebSocket protocol |
|
| 5 |
+// as specified in RFC 6455. |
|
| 6 |
+package websocket // import "golang.org/x/net/websocket" |
|
| 7 |
+ |
|
| 8 |
+import ( |
|
| 9 |
+ "bufio" |
|
| 10 |
+ "crypto/tls" |
|
| 11 |
+ "encoding/json" |
|
| 12 |
+ "errors" |
|
| 13 |
+ "io" |
|
| 14 |
+ "io/ioutil" |
|
| 15 |
+ "net" |
|
| 16 |
+ "net/http" |
|
| 17 |
+ "net/url" |
|
| 18 |
+ "sync" |
|
| 19 |
+ "time" |
|
| 20 |
+) |
|
| 21 |
+ |
|
| 22 |
+const ( |
|
| 23 |
+ ProtocolVersionHybi13 = 13 |
|
| 24 |
+ ProtocolVersionHybi = ProtocolVersionHybi13 |
|
| 25 |
+ SupportedProtocolVersion = "13" |
|
| 26 |
+ |
|
| 27 |
+ ContinuationFrame = 0 |
|
| 28 |
+ TextFrame = 1 |
|
| 29 |
+ BinaryFrame = 2 |
|
| 30 |
+ CloseFrame = 8 |
|
| 31 |
+ PingFrame = 9 |
|
| 32 |
+ PongFrame = 10 |
|
| 33 |
+ UnknownFrame = 255 |
|
| 34 |
+) |
|
| 35 |
+ |
|
| 36 |
+// ProtocolError represents WebSocket protocol errors. |
|
| 37 |
+type ProtocolError struct {
|
|
| 38 |
+ ErrorString string |
|
| 39 |
+} |
|
| 40 |
+ |
|
| 41 |
+func (err *ProtocolError) Error() string { return err.ErrorString }
|
|
| 42 |
+ |
|
| 43 |
+var ( |
|
| 44 |
+ ErrBadProtocolVersion = &ProtocolError{"bad protocol version"}
|
|
| 45 |
+ ErrBadScheme = &ProtocolError{"bad scheme"}
|
|
| 46 |
+ ErrBadStatus = &ProtocolError{"bad status"}
|
|
| 47 |
+ ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"}
|
|
| 48 |
+ ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"}
|
|
| 49 |
+ ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"}
|
|
| 50 |
+ ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"}
|
|
| 51 |
+ ErrBadWebSocketVersion = &ProtocolError{"missing or bad WebSocket Version"}
|
|
| 52 |
+ ErrChallengeResponse = &ProtocolError{"mismatch challenge/response"}
|
|
| 53 |
+ ErrBadFrame = &ProtocolError{"bad frame"}
|
|
| 54 |
+ ErrBadFrameBoundary = &ProtocolError{"not on frame boundary"}
|
|
| 55 |
+ ErrNotWebSocket = &ProtocolError{"not websocket protocol"}
|
|
| 56 |
+ ErrBadRequestMethod = &ProtocolError{"bad method"}
|
|
| 57 |
+ ErrNotSupported = &ProtocolError{"not supported"}
|
|
| 58 |
+) |
|
| 59 |
+ |
|
| 60 |
+// Addr is an implementation of net.Addr for WebSocket. |
|
| 61 |
+type Addr struct {
|
|
| 62 |
+ *url.URL |
|
| 63 |
+} |
|
| 64 |
+ |
|
| 65 |
+// Network returns the network type for a WebSocket, "websocket". |
|
| 66 |
+func (addr *Addr) Network() string { return "websocket" }
|
|
| 67 |
+ |
|
| 68 |
+// Config is a WebSocket configuration |
|
| 69 |
+type Config struct {
|
|
| 70 |
+ // A WebSocket server address. |
|
| 71 |
+ Location *url.URL |
|
| 72 |
+ |
|
| 73 |
+ // A Websocket client origin. |
|
| 74 |
+ Origin *url.URL |
|
| 75 |
+ |
|
| 76 |
+ // WebSocket subprotocols. |
|
| 77 |
+ Protocol []string |
|
| 78 |
+ |
|
| 79 |
+ // WebSocket protocol version. |
|
| 80 |
+ Version int |
|
| 81 |
+ |
|
| 82 |
+ // TLS config for secure WebSocket (wss). |
|
| 83 |
+ TlsConfig *tls.Config |
|
| 84 |
+ |
|
| 85 |
+ // Additional header fields to be sent in WebSocket opening handshake. |
|
| 86 |
+ Header http.Header |
|
| 87 |
+ |
|
| 88 |
+ handshakeData map[string]string |
|
| 89 |
+} |
|
| 90 |
+ |
|
| 91 |
+// serverHandshaker is an interface to handle WebSocket server side handshake. |
|
| 92 |
+type serverHandshaker interface {
|
|
| 93 |
+ // ReadHandshake reads handshake request message from client. |
|
| 94 |
+ // Returns http response code and error if any. |
|
| 95 |
+ ReadHandshake(buf *bufio.Reader, req *http.Request) (code int, err error) |
|
| 96 |
+ |
|
| 97 |
+ // AcceptHandshake accepts the client handshake request and sends |
|
| 98 |
+ // handshake response back to client. |
|
| 99 |
+ AcceptHandshake(buf *bufio.Writer) (err error) |
|
| 100 |
+ |
|
| 101 |
+ // NewServerConn creates a new WebSocket connection. |
|
| 102 |
+ NewServerConn(buf *bufio.ReadWriter, rwc io.ReadWriteCloser, request *http.Request) (conn *Conn) |
|
| 103 |
+} |
|
| 104 |
+ |
|
| 105 |
+// frameReader is an interface to read a WebSocket frame. |
|
| 106 |
+type frameReader interface {
|
|
| 107 |
+ // Reader is to read payload of the frame. |
|
| 108 |
+ io.Reader |
|
| 109 |
+ |
|
| 110 |
+ // PayloadType returns payload type. |
|
| 111 |
+ PayloadType() byte |
|
| 112 |
+ |
|
| 113 |
+ // HeaderReader returns a reader to read header of the frame. |
|
| 114 |
+ HeaderReader() io.Reader |
|
| 115 |
+ |
|
| 116 |
+ // TrailerReader returns a reader to read trailer of the frame. |
|
| 117 |
+ // If it returns nil, there is no trailer in the frame. |
|
| 118 |
+ TrailerReader() io.Reader |
|
| 119 |
+ |
|
| 120 |
+ // Len returns total length of the frame, including header and trailer. |
|
| 121 |
+ Len() int |
|
| 122 |
+} |
|
| 123 |
+ |
|
| 124 |
+// frameReaderFactory is an interface to creates new frame reader. |
|
| 125 |
+type frameReaderFactory interface {
|
|
| 126 |
+ NewFrameReader() (r frameReader, err error) |
|
| 127 |
+} |
|
| 128 |
+ |
|
| 129 |
+// frameWriter is an interface to write a WebSocket frame. |
|
| 130 |
+type frameWriter interface {
|
|
| 131 |
+ // Writer is to write payload of the frame. |
|
| 132 |
+ io.WriteCloser |
|
| 133 |
+} |
|
| 134 |
+ |
|
| 135 |
+// frameWriterFactory is an interface to create new frame writer. |
|
| 136 |
+type frameWriterFactory interface {
|
|
| 137 |
+ NewFrameWriter(payloadType byte) (w frameWriter, err error) |
|
| 138 |
+} |
|
| 139 |
+ |
|
| 140 |
+type frameHandler interface {
|
|
| 141 |
+ HandleFrame(frame frameReader) (r frameReader, err error) |
|
| 142 |
+ WriteClose(status int) (err error) |
|
| 143 |
+} |
|
| 144 |
+ |
|
| 145 |
+// Conn represents a WebSocket connection. |
|
| 146 |
+type Conn struct {
|
|
| 147 |
+ config *Config |
|
| 148 |
+ request *http.Request |
|
| 149 |
+ |
|
| 150 |
+ buf *bufio.ReadWriter |
|
| 151 |
+ rwc io.ReadWriteCloser |
|
| 152 |
+ |
|
| 153 |
+ rio sync.Mutex |
|
| 154 |
+ frameReaderFactory |
|
| 155 |
+ frameReader |
|
| 156 |
+ |
|
| 157 |
+ wio sync.Mutex |
|
| 158 |
+ frameWriterFactory |
|
| 159 |
+ |
|
| 160 |
+ frameHandler |
|
| 161 |
+ PayloadType byte |
|
| 162 |
+ defaultCloseStatus int |
|
| 163 |
+} |
|
| 164 |
+ |
|
| 165 |
+// Read implements the io.Reader interface: |
|
| 166 |
+// it reads data of a frame from the WebSocket connection. |
|
| 167 |
+// if msg is not large enough for the frame data, it fills the msg and next Read |
|
| 168 |
+// will read the rest of the frame data. |
|
| 169 |
+// it reads Text frame or Binary frame. |
|
| 170 |
+func (ws *Conn) Read(msg []byte) (n int, err error) {
|
|
| 171 |
+ ws.rio.Lock() |
|
| 172 |
+ defer ws.rio.Unlock() |
|
| 173 |
+again: |
|
| 174 |
+ if ws.frameReader == nil {
|
|
| 175 |
+ frame, err := ws.frameReaderFactory.NewFrameReader() |
|
| 176 |
+ if err != nil {
|
|
| 177 |
+ return 0, err |
|
| 178 |
+ } |
|
| 179 |
+ ws.frameReader, err = ws.frameHandler.HandleFrame(frame) |
|
| 180 |
+ if err != nil {
|
|
| 181 |
+ return 0, err |
|
| 182 |
+ } |
|
| 183 |
+ if ws.frameReader == nil {
|
|
| 184 |
+ goto again |
|
| 185 |
+ } |
|
| 186 |
+ } |
|
| 187 |
+ n, err = ws.frameReader.Read(msg) |
|
| 188 |
+ if err == io.EOF {
|
|
| 189 |
+ if trailer := ws.frameReader.TrailerReader(); trailer != nil {
|
|
| 190 |
+ io.Copy(ioutil.Discard, trailer) |
|
| 191 |
+ } |
|
| 192 |
+ ws.frameReader = nil |
|
| 193 |
+ goto again |
|
| 194 |
+ } |
|
| 195 |
+ return n, err |
|
| 196 |
+} |
|
| 197 |
+ |
|
| 198 |
+// Write implements the io.Writer interface: |
|
| 199 |
+// it writes data as a frame to the WebSocket connection. |
|
| 200 |
+func (ws *Conn) Write(msg []byte) (n int, err error) {
|
|
| 201 |
+ ws.wio.Lock() |
|
| 202 |
+ defer ws.wio.Unlock() |
|
| 203 |
+ w, err := ws.frameWriterFactory.NewFrameWriter(ws.PayloadType) |
|
| 204 |
+ if err != nil {
|
|
| 205 |
+ return 0, err |
|
| 206 |
+ } |
|
| 207 |
+ n, err = w.Write(msg) |
|
| 208 |
+ w.Close() |
|
| 209 |
+ if err != nil {
|
|
| 210 |
+ return n, err |
|
| 211 |
+ } |
|
| 212 |
+ return n, err |
|
| 213 |
+} |
|
| 214 |
+ |
|
| 215 |
+// Close implements the io.Closer interface. |
|
| 216 |
+func (ws *Conn) Close() error {
|
|
| 217 |
+ err := ws.frameHandler.WriteClose(ws.defaultCloseStatus) |
|
| 218 |
+ err1 := ws.rwc.Close() |
|
| 219 |
+ if err != nil {
|
|
| 220 |
+ return err |
|
| 221 |
+ } |
|
| 222 |
+ return err1 |
|
| 223 |
+} |
|
| 224 |
+ |
|
| 225 |
+func (ws *Conn) IsClientConn() bool { return ws.request == nil }
|
|
| 226 |
+func (ws *Conn) IsServerConn() bool { return ws.request != nil }
|
|
| 227 |
+ |
|
| 228 |
+// LocalAddr returns the WebSocket Origin for the connection for client, or |
|
| 229 |
+// the WebSocket location for server. |
|
| 230 |
+func (ws *Conn) LocalAddr() net.Addr {
|
|
| 231 |
+ if ws.IsClientConn() {
|
|
| 232 |
+ return &Addr{ws.config.Origin}
|
|
| 233 |
+ } |
|
| 234 |
+ return &Addr{ws.config.Location}
|
|
| 235 |
+} |
|
| 236 |
+ |
|
| 237 |
+// RemoteAddr returns the WebSocket location for the connection for client, or |
|
| 238 |
+// the Websocket Origin for server. |
|
| 239 |
+func (ws *Conn) RemoteAddr() net.Addr {
|
|
| 240 |
+ if ws.IsClientConn() {
|
|
| 241 |
+ return &Addr{ws.config.Location}
|
|
| 242 |
+ } |
|
| 243 |
+ return &Addr{ws.config.Origin}
|
|
| 244 |
+} |
|
| 245 |
+ |
|
| 246 |
+var errSetDeadline = errors.New("websocket: cannot set deadline: not using a net.Conn")
|
|
| 247 |
+ |
|
| 248 |
+// SetDeadline sets the connection's network read & write deadlines. |
|
| 249 |
+func (ws *Conn) SetDeadline(t time.Time) error {
|
|
| 250 |
+ if conn, ok := ws.rwc.(net.Conn); ok {
|
|
| 251 |
+ return conn.SetDeadline(t) |
|
| 252 |
+ } |
|
| 253 |
+ return errSetDeadline |
|
| 254 |
+} |
|
| 255 |
+ |
|
| 256 |
+// SetReadDeadline sets the connection's network read deadline. |
|
| 257 |
+func (ws *Conn) SetReadDeadline(t time.Time) error {
|
|
| 258 |
+ if conn, ok := ws.rwc.(net.Conn); ok {
|
|
| 259 |
+ return conn.SetReadDeadline(t) |
|
| 260 |
+ } |
|
| 261 |
+ return errSetDeadline |
|
| 262 |
+} |
|
| 263 |
+ |
|
| 264 |
+// SetWriteDeadline sets the connection's network write deadline. |
|
| 265 |
+func (ws *Conn) SetWriteDeadline(t time.Time) error {
|
|
| 266 |
+ if conn, ok := ws.rwc.(net.Conn); ok {
|
|
| 267 |
+ return conn.SetWriteDeadline(t) |
|
| 268 |
+ } |
|
| 269 |
+ return errSetDeadline |
|
| 270 |
+} |
|
| 271 |
+ |
|
| 272 |
+// Config returns the WebSocket config. |
|
| 273 |
+func (ws *Conn) Config() *Config { return ws.config }
|
|
| 274 |
+ |
|
| 275 |
+// Request returns the http request upgraded to the WebSocket. |
|
| 276 |
+// It is nil for client side. |
|
| 277 |
+func (ws *Conn) Request() *http.Request { return ws.request }
|
|
| 278 |
+ |
|
| 279 |
+// Codec represents a symmetric pair of functions that implement a codec. |
|
| 280 |
+type Codec struct {
|
|
| 281 |
+ Marshal func(v interface{}) (data []byte, payloadType byte, err error)
|
|
| 282 |
+ Unmarshal func(data []byte, payloadType byte, v interface{}) (err error)
|
|
| 283 |
+} |
|
| 284 |
+ |
|
| 285 |
+// Send sends v marshaled by cd.Marshal as single frame to ws. |
|
| 286 |
+func (cd Codec) Send(ws *Conn, v interface{}) (err error) {
|
|
| 287 |
+ data, payloadType, err := cd.Marshal(v) |
|
| 288 |
+ if err != nil {
|
|
| 289 |
+ return err |
|
| 290 |
+ } |
|
| 291 |
+ ws.wio.Lock() |
|
| 292 |
+ defer ws.wio.Unlock() |
|
| 293 |
+ w, err := ws.frameWriterFactory.NewFrameWriter(payloadType) |
|
| 294 |
+ if err != nil {
|
|
| 295 |
+ return err |
|
| 296 |
+ } |
|
| 297 |
+ _, err = w.Write(data) |
|
| 298 |
+ w.Close() |
|
| 299 |
+ return err |
|
| 300 |
+} |
|
| 301 |
+ |
|
| 302 |
+// Receive receives single frame from ws, unmarshaled by cd.Unmarshal and stores in v. |
|
| 303 |
+func (cd Codec) Receive(ws *Conn, v interface{}) (err error) {
|
|
| 304 |
+ ws.rio.Lock() |
|
| 305 |
+ defer ws.rio.Unlock() |
|
| 306 |
+ if ws.frameReader != nil {
|
|
| 307 |
+ _, err = io.Copy(ioutil.Discard, ws.frameReader) |
|
| 308 |
+ if err != nil {
|
|
| 309 |
+ return err |
|
| 310 |
+ } |
|
| 311 |
+ ws.frameReader = nil |
|
| 312 |
+ } |
|
| 313 |
+again: |
|
| 314 |
+ frame, err := ws.frameReaderFactory.NewFrameReader() |
|
| 315 |
+ if err != nil {
|
|
| 316 |
+ return err |
|
| 317 |
+ } |
|
| 318 |
+ frame, err = ws.frameHandler.HandleFrame(frame) |
|
| 319 |
+ if err != nil {
|
|
| 320 |
+ return err |
|
| 321 |
+ } |
|
| 322 |
+ if frame == nil {
|
|
| 323 |
+ goto again |
|
| 324 |
+ } |
|
| 325 |
+ payloadType := frame.PayloadType() |
|
| 326 |
+ data, err := ioutil.ReadAll(frame) |
|
| 327 |
+ if err != nil {
|
|
| 328 |
+ return err |
|
| 329 |
+ } |
|
| 330 |
+ return cd.Unmarshal(data, payloadType, v) |
|
| 331 |
+} |
|
| 332 |
+ |
|
| 333 |
+func marshal(v interface{}) (msg []byte, payloadType byte, err error) {
|
|
| 334 |
+ switch data := v.(type) {
|
|
| 335 |
+ case string: |
|
| 336 |
+ return []byte(data), TextFrame, nil |
|
| 337 |
+ case []byte: |
|
| 338 |
+ return data, BinaryFrame, nil |
|
| 339 |
+ } |
|
| 340 |
+ return nil, UnknownFrame, ErrNotSupported |
|
| 341 |
+} |
|
| 342 |
+ |
|
| 343 |
+func unmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
|
|
| 344 |
+ switch data := v.(type) {
|
|
| 345 |
+ case *string: |
|
| 346 |
+ *data = string(msg) |
|
| 347 |
+ return nil |
|
| 348 |
+ case *[]byte: |
|
| 349 |
+ *data = msg |
|
| 350 |
+ return nil |
|
| 351 |
+ } |
|
| 352 |
+ return ErrNotSupported |
|
| 353 |
+} |
|
| 354 |
+ |
|
| 355 |
+/* |
|
| 356 |
+Message is a codec to send/receive text/binary data in a frame on WebSocket connection. |
|
| 357 |
+To send/receive text frame, use string type. |
|
| 358 |
+To send/receive binary frame, use []byte type. |
|
| 359 |
+ |
|
| 360 |
+Trivial usage: |
|
| 361 |
+ |
|
| 362 |
+ import "websocket" |
|
| 363 |
+ |
|
| 364 |
+ // receive text frame |
|
| 365 |
+ var message string |
|
| 366 |
+ websocket.Message.Receive(ws, &message) |
|
| 367 |
+ |
|
| 368 |
+ // send text frame |
|
| 369 |
+ message = "hello" |
|
| 370 |
+ websocket.Message.Send(ws, message) |
|
| 371 |
+ |
|
| 372 |
+ // receive binary frame |
|
| 373 |
+ var data []byte |
|
| 374 |
+ websocket.Message.Receive(ws, &data) |
|
| 375 |
+ |
|
| 376 |
+ // send binary frame |
|
| 377 |
+ data = []byte{0, 1, 2}
|
|
| 378 |
+ websocket.Message.Send(ws, data) |
|
| 379 |
+ |
|
| 380 |
+*/ |
|
| 381 |
+var Message = Codec{marshal, unmarshal}
|
|
| 382 |
+ |
|
| 383 |
+func jsonMarshal(v interface{}) (msg []byte, payloadType byte, err error) {
|
|
| 384 |
+ msg, err = json.Marshal(v) |
|
| 385 |
+ return msg, TextFrame, err |
|
| 386 |
+} |
|
| 387 |
+ |
|
| 388 |
+func jsonUnmarshal(msg []byte, payloadType byte, v interface{}) (err error) {
|
|
| 389 |
+ return json.Unmarshal(msg, v) |
|
| 390 |
+} |
|
| 391 |
+ |
|
| 392 |
+/* |
|
| 393 |
+JSON is a codec to send/receive JSON data in a frame from a WebSocket connection. |
|
| 394 |
+ |
|
| 395 |
+Trivial usage: |
|
| 396 |
+ |
|
| 397 |
+ import "websocket" |
|
| 398 |
+ |
|
| 399 |
+ type T struct {
|
|
| 400 |
+ Msg string |
|
| 401 |
+ Count int |
|
| 402 |
+ } |
|
| 403 |
+ |
|
| 404 |
+ // receive JSON type T |
|
| 405 |
+ var data T |
|
| 406 |
+ websocket.JSON.Receive(ws, &data) |
|
| 407 |
+ |
|
| 408 |
+ // send JSON type T |
|
| 409 |
+ websocket.JSON.Send(ws, data) |
|
| 410 |
+*/ |
|
| 411 |
+var JSON = Codec{jsonMarshal, jsonUnmarshal}
|