util.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. // Copyright 2013 The Gorilla WebSocket 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. "crypto/rand"
  7. "crypto/sha1"
  8. "encoding/base64"
  9. "io"
  10. "net/http"
  11. "strings"
  12. "unicode/utf8"
  13. )
  14. var keyGUID = []byte("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
  15. func computeAcceptKey(challengeKey string) string {
  16. h := sha1.New()
  17. h.Write([]byte(challengeKey))
  18. h.Write(keyGUID)
  19. return base64.StdEncoding.EncodeToString(h.Sum(nil))
  20. }
  21. func generateChallengeKey() (string, error) {
  22. p := make([]byte, 16)
  23. if _, err := io.ReadFull(rand.Reader, p); err != nil {
  24. return "", err
  25. }
  26. return base64.StdEncoding.EncodeToString(p), nil
  27. }
  28. // Token octets per RFC 2616.
  29. var isTokenOctet = [256]bool{
  30. '!': true,
  31. '#': true,
  32. '$': true,
  33. '%': true,
  34. '&': true,
  35. '\'': true,
  36. '*': true,
  37. '+': true,
  38. '-': true,
  39. '.': true,
  40. '0': true,
  41. '1': true,
  42. '2': true,
  43. '3': true,
  44. '4': true,
  45. '5': true,
  46. '6': true,
  47. '7': true,
  48. '8': true,
  49. '9': true,
  50. 'A': true,
  51. 'B': true,
  52. 'C': true,
  53. 'D': true,
  54. 'E': true,
  55. 'F': true,
  56. 'G': true,
  57. 'H': true,
  58. 'I': true,
  59. 'J': true,
  60. 'K': true,
  61. 'L': true,
  62. 'M': true,
  63. 'N': true,
  64. 'O': true,
  65. 'P': true,
  66. 'Q': true,
  67. 'R': true,
  68. 'S': true,
  69. 'T': true,
  70. 'U': true,
  71. 'W': true,
  72. 'V': true,
  73. 'X': true,
  74. 'Y': true,
  75. 'Z': true,
  76. '^': true,
  77. '_': true,
  78. '`': true,
  79. 'a': true,
  80. 'b': true,
  81. 'c': true,
  82. 'd': true,
  83. 'e': true,
  84. 'f': true,
  85. 'g': true,
  86. 'h': true,
  87. 'i': true,
  88. 'j': true,
  89. 'k': true,
  90. 'l': true,
  91. 'm': true,
  92. 'n': true,
  93. 'o': true,
  94. 'p': true,
  95. 'q': true,
  96. 'r': true,
  97. 's': true,
  98. 't': true,
  99. 'u': true,
  100. 'v': true,
  101. 'w': true,
  102. 'x': true,
  103. 'y': true,
  104. 'z': true,
  105. '|': true,
  106. '~': true,
  107. }
  108. // skipSpace returns a slice of the string s with all leading RFC 2616 linear
  109. // whitespace removed.
  110. func skipSpace(s string) (rest string) {
  111. i := 0
  112. for ; i < len(s); i++ {
  113. if b := s[i]; b != ' ' && b != '\t' {
  114. break
  115. }
  116. }
  117. return s[i:]
  118. }
  119. // nextToken returns the leading RFC 2616 token of s and the string following
  120. // the token.
  121. func nextToken(s string) (token, rest string) {
  122. i := 0
  123. for ; i < len(s); i++ {
  124. if !isTokenOctet[s[i]] {
  125. break
  126. }
  127. }
  128. return s[:i], s[i:]
  129. }
  130. // nextTokenOrQuoted returns the leading token or quoted string per RFC 2616
  131. // and the string following the token or quoted string.
  132. func nextTokenOrQuoted(s string) (value string, rest string) {
  133. if !strings.HasPrefix(s, "\"") {
  134. return nextToken(s)
  135. }
  136. s = s[1:]
  137. for i := 0; i < len(s); i++ {
  138. switch s[i] {
  139. case '"':
  140. return s[:i], s[i+1:]
  141. case '\\':
  142. p := make([]byte, len(s)-1)
  143. j := copy(p, s[:i])
  144. escape := true
  145. for i = i + 1; i < len(s); i++ {
  146. b := s[i]
  147. switch {
  148. case escape:
  149. escape = false
  150. p[j] = b
  151. j++
  152. case b == '\\':
  153. escape = true
  154. case b == '"':
  155. return string(p[:j]), s[i+1:]
  156. default:
  157. p[j] = b
  158. j++
  159. }
  160. }
  161. return "", ""
  162. }
  163. }
  164. return "", ""
  165. }
  166. // equalASCIIFold returns true if s is equal to t with ASCII case folding as
  167. // defined in RFC 4790.
  168. func equalASCIIFold(s, t string) bool {
  169. for s != "" && t != "" {
  170. sr, size := utf8.DecodeRuneInString(s)
  171. s = s[size:]
  172. tr, size := utf8.DecodeRuneInString(t)
  173. t = t[size:]
  174. if sr == tr {
  175. continue
  176. }
  177. if 'A' <= sr && sr <= 'Z' {
  178. sr = sr + 'a' - 'A'
  179. }
  180. if 'A' <= tr && tr <= 'Z' {
  181. tr = tr + 'a' - 'A'
  182. }
  183. if sr != tr {
  184. return false
  185. }
  186. }
  187. return s == t
  188. }
  189. // tokenListContainsValue returns true if the 1#token header with the given
  190. // name contains a token equal to value with ASCII case folding.
  191. func tokenListContainsValue(header http.Header, name string, value string) bool {
  192. headers:
  193. for _, s := range header[name] {
  194. for {
  195. var t string
  196. t, s = nextToken(skipSpace(s))
  197. if t == "" {
  198. continue headers
  199. }
  200. s = skipSpace(s)
  201. if s != "" && s[0] != ',' {
  202. continue headers
  203. }
  204. if equalASCIIFold(t, value) {
  205. return true
  206. }
  207. if s == "" {
  208. continue headers
  209. }
  210. s = s[1:]
  211. }
  212. }
  213. return false
  214. }
  215. // parseExtensions parses WebSocket extensions from a header.
  216. func parseExtensions(header http.Header) []map[string]string {
  217. // From RFC 6455:
  218. //
  219. // Sec-WebSocket-Extensions = extension-list
  220. // extension-list = 1#extension
  221. // extension = extension-token *( ";" extension-param )
  222. // extension-token = registered-token
  223. // registered-token = token
  224. // extension-param = token [ "=" (token | quoted-string) ]
  225. // ;When using the quoted-string syntax variant, the value
  226. // ;after quoted-string unescaping MUST conform to the
  227. // ;'token' ABNF.
  228. var result []map[string]string
  229. headers:
  230. for _, s := range header["Sec-Websocket-Extensions"] {
  231. for {
  232. var t string
  233. t, s = nextToken(skipSpace(s))
  234. if t == "" {
  235. continue headers
  236. }
  237. ext := map[string]string{"": t}
  238. for {
  239. s = skipSpace(s)
  240. if !strings.HasPrefix(s, ";") {
  241. break
  242. }
  243. var k string
  244. k, s = nextToken(skipSpace(s[1:]))
  245. if k == "" {
  246. continue headers
  247. }
  248. s = skipSpace(s)
  249. var v string
  250. if strings.HasPrefix(s, "=") {
  251. v, s = nextTokenOrQuoted(skipSpace(s[1:]))
  252. s = skipSpace(s)
  253. }
  254. if s != "" && s[0] != ',' && s[0] != ';' {
  255. continue headers
  256. }
  257. ext[k] = v
  258. }
  259. if s != "" && s[0] != ',' {
  260. continue headers
  261. }
  262. result = append(result, ext)
  263. if s == "" {
  264. continue headers
  265. }
  266. s = s[1:]
  267. }
  268. }
  269. return result
  270. }