PageRenderTime 43ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/pkg/websocket/client.go

http://github.com/border/golang-china
Go | 316 lines | 192 code | 32 blank | 92 comment | 56 complexity | 4b80ebd135834ff70fe47b40cda661ea MD5 | raw file
  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. package websocket
  5. import (
  6. "encoding/binary"
  7. "bufio"
  8. "bytes"
  9. "container/vector"
  10. "crypto/md5"
  11. "fmt"
  12. "http"
  13. "io"
  14. "net"
  15. "os"
  16. "rand"
  17. "strings"
  18. )
  19. type ProtocolError struct {
  20. os.ErrorString
  21. }
  22. var (
  23. ErrBadStatus = &ProtocolError{"bad status"}
  24. ErrBadUpgrade = &ProtocolError{"missing or bad upgrade"}
  25. ErrBadWebSocketOrigin = &ProtocolError{"missing or bad WebSocket-Origin"}
  26. ErrBadWebSocketLocation = &ProtocolError{"missing or bad WebSocket-Location"}
  27. ErrBadWebSocketProtocol = &ProtocolError{"missing or bad WebSocket-Protocol"}
  28. ErrChallengeResponse = &ProtocolError{"mismatch challange/response"}
  29. 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. type handshaker func(resourceName, host, origin, location, protocol string, br *bufio.Reader, bw *bufio.Writer) os.Error
  43. // newClient creates a new Web Socket client connection.
  44. func newClient(resourceName, host, origin, location, protocol string, rwc io.ReadWriteCloser, handshake handshaker) (ws *Conn, err os.Error) {
  45. br := bufio.NewReader(rwc)
  46. bw := bufio.NewWriter(rwc)
  47. err = handshake(resourceName, host, origin, location, protocol, br, bw)
  48. if err != nil {
  49. return
  50. }
  51. buf := bufio.NewReadWriter(br, bw)
  52. ws = newConn(origin, location, protocol, buf, rwc)
  53. return
  54. }
  55. /*
  56. Dial opens a new client connection to a Web Socket.
  57. A trivial example client is:
  58. package main
  59. import (
  60. "websocket"
  61. "strings"
  62. )
  63. func main() {
  64. ws, err := websocket.Dial("ws://localhost/ws", "", "http://localhost/");
  65. if err != nil {
  66. panic("Dial: " + err.String())
  67. }
  68. if _, err := ws.Write([]byte("hello, world!\n")); err != nil {
  69. panic("Write: " + err.String())
  70. }
  71. var msg = make([]byte, 512);
  72. if n, err := ws.Read(msg); err != nil {
  73. panic("Read: " + err.String())
  74. }
  75. // use msg[0:n]
  76. }
  77. */
  78. func Dial(url, protocol, origin string) (ws *Conn, err os.Error) {
  79. parsedUrl, err := http.ParseURL(url)
  80. if err != nil {
  81. return
  82. }
  83. client, err := net.Dial("tcp", "", parsedUrl.Host)
  84. if err != nil {
  85. return
  86. }
  87. return newClient(parsedUrl.RawPath, parsedUrl.Host, origin, url, protocol, client, handshake)
  88. }
  89. /*
  90. Generates handshake key as described in 4.1 Opening handshake
  91. step 16 to 22.
  92. cf. http://www.whatwg.org/specs/web-socket-protocol/
  93. */
  94. func generateKeyNumber() (key string, number uint32) {
  95. // 16. Let /spaces_n/ be a random integer from 1 to 12 inclusive.
  96. spaces := rand.Intn(12) + 1
  97. // 17. Let /max_n/ be the largest integer not greater than
  98. // 4,294,967,295 divided by /spaces_n/
  99. max := int(4294967295 / uint32(spaces))
  100. // 18. Let /number_n/ be a random integer from 0 to /max_n/ inclusive.
  101. number = uint32(rand.Intn(max + 1))
  102. // 19. Let /product_n/ be the result of multiplying /number_n/ and
  103. // /spaces_n/ together.
  104. product := number * uint32(spaces)
  105. // 20. Let /key_n/ be a string consisting of /product_n/, expressed
  106. // in base ten using the numerals in the range U+0030 DIGIT ZERO (0)
  107. // to U+0039 DIGIT NINE (9).
  108. key = fmt.Sprintf("%d", product)
  109. // 21. Insert /spaces_n/ U+0020 SPACE characters into /key_n/ at random
  110. // posisions.
  111. for i := 0; i < spaces; i++ {
  112. pos := rand.Intn(len(key)-1) + 1
  113. key = key[0:pos] + " " + key[pos:]
  114. }
  115. // 22. Insert between one and twelve random characters from the ranges
  116. // U+0021 to U+002F and U+003A to U+007E into /key_n/ at random
  117. // positions.
  118. n := rand.Intn(12) + 1
  119. for i := 0; i < n; i++ {
  120. pos := rand.Intn(len(key)) + 1
  121. ch := secKeyRandomChars[rand.Intn(len(secKeyRandomChars))]
  122. key = key[0:pos] + string(ch) + key[pos:]
  123. }
  124. return
  125. }
  126. /*
  127. Generates handshake key_3 as described in 4.1 Opening handshake
  128. step 26.
  129. cf. http://www.whatwg.org/specs/web-socket-protocol/
  130. */
  131. func generateKey3() (key []byte) {
  132. // 26. Let /key3/ be a string consisting of eight random bytes (or
  133. // equivalently, a random 64 bit integer encoded in big-endian order).
  134. key = make([]byte, 8)
  135. for i := 0; i < 8; i++ {
  136. key[i] = byte(rand.Intn(256))
  137. }
  138. return
  139. }
  140. /*
  141. Gets expected from challenge as described in 4.1 Opening handshake
  142. Step 42 to 43.
  143. cf. http://www.whatwg.org/specs/web-socket-protocol/
  144. */
  145. func getExpectedForChallenge(number1, number2 uint32, key3 []byte) (expected []byte, err os.Error) {
  146. // 41. Let /challenge/ be the concatenation of /number_1/, expressed
  147. // a big-endian 32 bit integer, /number_2/, expressed in a big-
  148. // endian 32 bit integer, and the eight bytes of /key_3/ in the
  149. // order they were sent to the wire.
  150. challenge := make([]byte, 16)
  151. challengeBuf := bytes.NewBuffer(challenge)
  152. binary.Write(challengeBuf, binary.BigEndian, number1)
  153. binary.Write(challengeBuf, binary.BigEndian, number2)
  154. copy(challenge[8:], key3)
  155. // 42. Let /expected/ be the MD5 fingerprint of /challenge/ as a big-
  156. // endian 128 bit string.
  157. h := md5.New()
  158. if _, err = h.Write(challenge); err != nil {
  159. return
  160. }
  161. expected = h.Sum()
  162. return
  163. }
  164. /*
  165. Web Socket protocol handshake based on
  166. http://www.whatwg.org/specs/web-socket-protocol/
  167. (draft of http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol)
  168. */
  169. func handshake(resourceName, host, origin, location, protocol string, br *bufio.Reader, bw *bufio.Writer) (err os.Error) {
  170. // 4.1. Opening handshake.
  171. // Step 5. send a request line.
  172. bw.WriteString("GET " + resourceName + " HTTP/1.1\r\n")
  173. // Step 6-14. push request headers in fields.
  174. var fields vector.StringVector
  175. fields.Push("Upgrade: WebSocket\r\n")
  176. fields.Push("Connection: Upgrade\r\n")
  177. fields.Push("Host: " + host + "\r\n")
  178. fields.Push("Origin: " + origin + "\r\n")
  179. if protocol != "" {
  180. fields.Push("Sec-WebSocket-Protocol: " + protocol + "\r\n")
  181. }
  182. // TODO(ukai): Step 15. send cookie if any.
  183. // Step 16-23. generate keys and push Sec-WebSocket-Key<n> in fields.
  184. key1, number1 := generateKeyNumber()
  185. key2, number2 := generateKeyNumber()
  186. fields.Push("Sec-WebSocket-Key1: " + key1 + "\r\n")
  187. fields.Push("Sec-WebSocket-Key2: " + key2 + "\r\n")
  188. // Step 24. shuffle fields and send them out.
  189. for i := 1; i < len(fields); i++ {
  190. j := rand.Intn(i)
  191. fields[i], fields[j] = fields[j], fields[i]
  192. }
  193. for i := 0; i < len(fields); i++ {
  194. bw.WriteString(fields[i])
  195. }
  196. // Step 25. send CRLF.
  197. bw.WriteString("\r\n")
  198. // Step 26. genearte 8 bytes random key.
  199. key3 := generateKey3()
  200. // Step 27. send it out.
  201. bw.Write(key3)
  202. if err = bw.Flush(); err != nil {
  203. return
  204. }
  205. // Step 28-29, 32-40. read response from server.
  206. resp, err := http.ReadResponse(br, "GET")
  207. if err != nil {
  208. return err
  209. }
  210. // Step 30. check response code is 101.
  211. if resp.StatusCode != 101 {
  212. return ErrBadStatus
  213. }
  214. // Step 41. check websocket headers.
  215. if resp.Header["Upgrade"] != "WebSocket" ||
  216. strings.ToLower(resp.Header["Connection"]) != "upgrade" {
  217. return ErrBadUpgrade
  218. }
  219. if resp.Header["Sec-Websocket-Origin"] != origin {
  220. return ErrBadWebSocketOrigin
  221. }
  222. if resp.Header["Sec-Websocket-Location"] != location {
  223. return ErrBadWebSocketLocation
  224. }
  225. if protocol != "" && resp.Header["Sec-Websocket-Protocol"] != protocol {
  226. return ErrBadWebSocketProtocol
  227. }
  228. // Step 42-43. get expected data from challange data.
  229. expected, err := getExpectedForChallenge(number1, number2, key3)
  230. if err != nil {
  231. return err
  232. }
  233. // Step 44. read 16 bytes from server.
  234. reply := make([]byte, 16)
  235. if _, err = io.ReadFull(br, reply); err != nil {
  236. return err
  237. }
  238. // Step 45. check the reply equals to expected data.
  239. if !bytes.Equal(expected, reply) {
  240. return ErrChallengeResponse
  241. }
  242. // WebSocket connection is established.
  243. return
  244. }
  245. /*
  246. Handhake described in (soon obsolete)
  247. draft-hixie-thewebsocket-protocol-75.
  248. */
  249. func draft75handshake(resourceName, host, origin, location, protocol string, br *bufio.Reader, bw *bufio.Writer) (err os.Error) {
  250. bw.WriteString("GET " + resourceName + " HTTP/1.1\r\n")
  251. bw.WriteString("Upgrade: WebSocket\r\n")
  252. bw.WriteString("Connection: Upgrade\r\n")
  253. bw.WriteString("Host: " + host + "\r\n")
  254. bw.WriteString("Origin: " + origin + "\r\n")
  255. if protocol != "" {
  256. bw.WriteString("WebSocket-Protocol: " + protocol + "\r\n")
  257. }
  258. bw.WriteString("\r\n")
  259. bw.Flush()
  260. resp, err := http.ReadResponse(br, "GET")
  261. if err != nil {
  262. return
  263. }
  264. if resp.Status != "101 Web Socket Protocol Handshake" {
  265. return ErrBadStatus
  266. }
  267. if resp.Header["Upgrade"] != "WebSocket" ||
  268. resp.Header["Connection"] != "Upgrade" {
  269. return ErrBadUpgrade
  270. }
  271. if resp.Header["Websocket-Origin"] != origin {
  272. return ErrBadWebSocketOrigin
  273. }
  274. if resp.Header["Websocket-Location"] != location {
  275. return ErrBadWebSocketLocation
  276. }
  277. if protocol != "" && resp.Header["Websocket-Protocol"] != protocol {
  278. return ErrBadWebSocketProtocol
  279. }
  280. return
  281. }