...
1 package challenge
2
3 import (
4 "fmt"
5 "net/http"
6 "net/url"
7 "strings"
8 "sync"
9 )
10
11
12
13 type Challenge struct {
14
15 Scheme string
16
17
18 Parameters map[string]string
19 }
20
21
22
23
24
25
26 type Manager interface {
27
28
29 GetChallenges(endpoint url.URL) ([]Challenge, error)
30
31
32
33
34
35
36
37 AddResponse(resp *http.Response) error
38 }
39
40
41
42
43
44
45
46 func NewSimpleManager() Manager {
47 return &simpleManager{
48 Challenges: make(map[string][]Challenge),
49 }
50 }
51
52 type simpleManager struct {
53 sync.RWMutex
54 Challenges map[string][]Challenge
55 }
56
57 func normalizeURL(endpoint *url.URL) {
58 endpoint.Host = strings.ToLower(endpoint.Host)
59 endpoint.Host = canonicalAddr(endpoint)
60 }
61
62 func (m *simpleManager) GetChallenges(endpoint url.URL) ([]Challenge, error) {
63 normalizeURL(&endpoint)
64
65 m.RLock()
66 defer m.RUnlock()
67 challenges := m.Challenges[endpoint.String()]
68 return challenges, nil
69 }
70
71 func (m *simpleManager) AddResponse(resp *http.Response) error {
72 challenges := ResponseChallenges(resp)
73 if resp.Request == nil {
74 return fmt.Errorf("missing request reference")
75 }
76 urlCopy := url.URL{
77 Path: resp.Request.URL.Path,
78 Host: resp.Request.URL.Host,
79 Scheme: resp.Request.URL.Scheme,
80 }
81 normalizeURL(&urlCopy)
82
83 m.Lock()
84 defer m.Unlock()
85 m.Challenges[urlCopy.String()] = challenges
86 return nil
87 }
88
89
90 type octetType byte
91
92 var octetTypes [256]octetType
93
94 const (
95 isToken octetType = 1 << iota
96 isSpace
97 )
98
99 func init() {
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 for c := 0; c < 256; c++ {
117 var t octetType
118 isCtl := c <= 31 || c == 127
119 isChar := 0 <= c && c <= 127
120 isSeparator := strings.ContainsRune(" \t\"(),/:;<=>?@[]\\{}", rune(c))
121 if strings.ContainsRune(" \t\r\n", rune(c)) {
122 t |= isSpace
123 }
124 if isChar && !isCtl && !isSeparator {
125 t |= isToken
126 }
127 octetTypes[c] = t
128 }
129 }
130
131
132
133
134 func ResponseChallenges(resp *http.Response) []Challenge {
135 if resp.StatusCode == http.StatusUnauthorized {
136
137
138 return parseAuthHeader(resp.Header)
139 }
140
141 return nil
142 }
143
144 func parseAuthHeader(header http.Header) []Challenge {
145 challenges := []Challenge{}
146 for _, h := range header[http.CanonicalHeaderKey("WWW-Authenticate")] {
147 v, p := parseValueAndParams(h)
148 if v != "" {
149 challenges = append(challenges, Challenge{Scheme: v, Parameters: p})
150 }
151 }
152 return challenges
153 }
154
155 func parseValueAndParams(header string) (value string, params map[string]string) {
156 params = make(map[string]string)
157 value, s := expectToken(header)
158 if value == "" {
159 return
160 }
161 value = strings.ToLower(value)
162 s = "," + skipSpace(s)
163 for strings.HasPrefix(s, ",") {
164 var pkey string
165 pkey, s = expectToken(skipSpace(s[1:]))
166 if pkey == "" {
167 return
168 }
169 if !strings.HasPrefix(s, "=") {
170 return
171 }
172 var pvalue string
173 pvalue, s = expectTokenOrQuoted(s[1:])
174 if pvalue == "" {
175 return
176 }
177 pkey = strings.ToLower(pkey)
178 params[pkey] = pvalue
179 s = skipSpace(s)
180 }
181 return
182 }
183
184 func skipSpace(s string) (rest string) {
185 i := 0
186 for ; i < len(s); i++ {
187 if octetTypes[s[i]]&isSpace == 0 {
188 break
189 }
190 }
191 return s[i:]
192 }
193
194 func expectToken(s string) (token, rest string) {
195 i := 0
196 for ; i < len(s); i++ {
197 if octetTypes[s[i]]&isToken == 0 {
198 break
199 }
200 }
201 return s[:i], s[i:]
202 }
203
204 func expectTokenOrQuoted(s string) (value string, rest string) {
205 if !strings.HasPrefix(s, "\"") {
206 return expectToken(s)
207 }
208 s = s[1:]
209 for i := 0; i < len(s); i++ {
210 switch s[i] {
211 case '"':
212 return s[:i], s[i+1:]
213 case '\\':
214 p := make([]byte, len(s)-1)
215 j := copy(p, s[:i])
216 escape := true
217 for i = i + 1; i < len(s); i++ {
218 b := s[i]
219 switch {
220 case escape:
221 escape = false
222 p[j] = b
223 j++
224 case b == '\\':
225 escape = true
226 case b == '"':
227 return string(p[:j]), s[i+1:]
228 default:
229 p[j] = b
230 j++
231 }
232 }
233 return "", ""
234 }
235 }
236 return "", ""
237 }
238
View as plain text