util.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  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. // Octet types from RFC 2616.
  29. var octetTypes [256]byte
  30. const (
  31. isTokenOctet = 1 << iota
  32. isSpaceOctet
  33. )
  34. func init() {
  35. // From RFC 2616
  36. //
  37. // OCTET = <any 8-bit sequence of data>
  38. // CHAR = <any US-ASCII character (octets 0 - 127)>
  39. // CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
  40. // CR = <US-ASCII CR, carriage return (13)>
  41. // LF = <US-ASCII LF, linefeed (10)>
  42. // SP = <US-ASCII SP, space (32)>
  43. // HT = <US-ASCII HT, horizontal-tab (9)>
  44. // <"> = <US-ASCII double-quote mark (34)>
  45. // CRLF = CR LF
  46. // LWS = [CRLF] 1*( SP | HT )
  47. // TEXT = <any OCTET except CTLs, but including LWS>
  48. // separators = "(" | ")" | "<" | ">" | "@" | "," | ";" | ":" | "\" | <">
  49. // | "/" | "[" | "]" | "?" | "=" | "{" | "}" | SP | HT
  50. // token = 1*<any CHAR except CTLs or separators>
  51. // qdtext = <any TEXT except <">>
  52. for c := 0; c < 256; c++ {
  53. var t byte
  54. isCtl := c <= 31 || c == 127
  55. isChar := 0 <= c && c <= 127
  56. isSeparator := strings.IndexRune(" \t\"(),/:;<=>?@[]\\{}", rune(c)) >= 0
  57. if strings.IndexRune(" \t\r\n", rune(c)) >= 0 {
  58. t |= isSpaceOctet
  59. }
  60. if isChar && !isCtl && !isSeparator {
  61. t |= isTokenOctet
  62. }
  63. octetTypes[c] = t
  64. }
  65. }
  66. func skipSpace(s string) (rest string) {
  67. i := 0
  68. for ; i < len(s); i++ {
  69. if octetTypes[s[i]]&isSpaceOctet == 0 {
  70. break
  71. }
  72. }
  73. return s[i:]
  74. }
  75. func nextToken(s string) (token, rest string) {
  76. i := 0
  77. for ; i < len(s); i++ {
  78. if octetTypes[s[i]]&isTokenOctet == 0 {
  79. break
  80. }
  81. }
  82. return s[:i], s[i:]
  83. }
  84. func nextTokenOrQuoted(s string) (value string, rest string) {
  85. if !strings.HasPrefix(s, "\"") {
  86. return nextToken(s)
  87. }
  88. s = s[1:]
  89. for i := 0; i < len(s); i++ {
  90. switch s[i] {
  91. case '"':
  92. return s[:i], s[i+1:]
  93. case '\\':
  94. p := make([]byte, len(s)-1)
  95. j := copy(p, s[:i])
  96. escape := true
  97. for i = i + 1; i < len(s); i++ {
  98. b := s[i]
  99. switch {
  100. case escape:
  101. escape = false
  102. p[j] = b
  103. j++
  104. case b == '\\':
  105. escape = true
  106. case b == '"':
  107. return string(p[:j]), s[i+1:]
  108. default:
  109. p[j] = b
  110. j++
  111. }
  112. }
  113. return "", ""
  114. }
  115. }
  116. return "", ""
  117. }
  118. // equalASCIIFold returns true if s is equal to t with ASCII case folding.
  119. func equalASCIIFold(s, t string) bool {
  120. for s != "" && t != "" {
  121. sr, size := utf8.DecodeRuneInString(s)
  122. s = s[size:]
  123. tr, size := utf8.DecodeRuneInString(t)
  124. t = t[size:]
  125. if sr == tr {
  126. continue
  127. }
  128. if 'A' <= sr && sr <= 'Z' {
  129. sr = sr + 'a' - 'A'
  130. }
  131. if 'A' <= tr && tr <= 'Z' {
  132. tr = tr + 'a' - 'A'
  133. }
  134. if sr != tr {
  135. return false
  136. }
  137. }
  138. return s == t
  139. }
  140. // tokenListContainsValue returns true if the 1#token header with the given
  141. // name contains a token equal to value with ASCII case folding.
  142. func tokenListContainsValue(header http.Header, name string, value string) bool {
  143. headers:
  144. for _, s := range header[name] {
  145. for {
  146. var t string
  147. t, s = nextToken(skipSpace(s))
  148. if t == "" {
  149. continue headers
  150. }
  151. s = skipSpace(s)
  152. if s != "" && s[0] != ',' {
  153. continue headers
  154. }
  155. if equalASCIIFold(t, value) {
  156. return true
  157. }
  158. if s == "" {
  159. continue headers
  160. }
  161. s = s[1:]
  162. }
  163. }
  164. return false
  165. }
  166. // parseExtensiosn parses WebSocket extensions from a header.
  167. func parseExtensions(header http.Header) []map[string]string {
  168. // From RFC 6455:
  169. //
  170. // Sec-WebSocket-Extensions = extension-list
  171. // extension-list = 1#extension
  172. // extension = extension-token *( ";" extension-param )
  173. // extension-token = registered-token
  174. // registered-token = token
  175. // extension-param = token [ "=" (token | quoted-string) ]
  176. // ;When using the quoted-string syntax variant, the value
  177. // ;after quoted-string unescaping MUST conform to the
  178. // ;'token' ABNF.
  179. var result []map[string]string
  180. headers:
  181. for _, s := range header["Sec-Websocket-Extensions"] {
  182. for {
  183. var t string
  184. t, s = nextToken(skipSpace(s))
  185. if t == "" {
  186. continue headers
  187. }
  188. ext := map[string]string{"": t}
  189. for {
  190. s = skipSpace(s)
  191. if !strings.HasPrefix(s, ";") {
  192. break
  193. }
  194. var k string
  195. k, s = nextToken(skipSpace(s[1:]))
  196. if k == "" {
  197. continue headers
  198. }
  199. s = skipSpace(s)
  200. var v string
  201. if strings.HasPrefix(s, "=") {
  202. v, s = nextTokenOrQuoted(skipSpace(s[1:]))
  203. s = skipSpace(s)
  204. }
  205. if s != "" && s[0] != ',' && s[0] != ';' {
  206. continue headers
  207. }
  208. ext[k] = v
  209. }
  210. if s != "" && s[0] != ',' {
  211. continue headers
  212. }
  213. result = append(result, ext)
  214. if s == "" {
  215. continue headers
  216. }
  217. s = s[1:]
  218. }
  219. }
  220. return result
  221. }