OSDN Git Service

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