另客网go项目公用的代码库

util.go 4.7KB

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