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