Left: | ||
Right: |
OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2012 The Go 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 cookiejar | |
6 | |
7 import ( | |
8 "bytes" | |
9 "fmt" | |
10 "net/http" | |
11 "net/url" | |
12 "reflect" | |
13 "sort" | |
14 "strings" | |
15 "sync" | |
16 "testing" | |
17 "time" | |
18 ) | |
19 | |
20 // memStorage is an in-memory storage used for testing. | |
21 type memStorage struct { | |
22 lock sync.Mutex | |
23 m map[string]string | |
24 } | |
25 | |
26 func (m *memStorage) Get(key string) (value []byte, err error) { | |
27 m.lock.Lock() | |
28 defer m.lock.Unlock() | |
29 | |
30 if m.m == nil { | |
31 m.m = make(map[string]string) | |
32 } | |
33 return []byte(m.m[key]), nil | |
34 } | |
35 | |
36 func (m *memStorage) Set(key string, value []byte) error { | |
37 m.lock.Lock() | |
38 defer m.lock.Unlock() | |
39 | |
40 if m.m == nil { | |
41 m.m = make(map[string]string) | |
42 } | |
43 if len(value) != 0 { | |
44 m.m[key] = string(value) | |
45 } else { | |
46 delete(m.m, key) | |
47 } | |
48 return nil | |
49 } | |
50 | |
51 func (m *memStorage) Keys() ([]string, error) { | |
52 m.lock.Lock() | |
53 defer m.lock.Unlock() | |
54 | |
55 keys := make([]string, 0, len(m.m)) | |
56 for k := range m.m { | |
57 keys = append(keys, k) | |
58 } | |
59 return keys, nil | |
60 } | |
61 | |
62 // fakePSL is a public suffix list used for testing. | |
63 type fakePSL struct{} | |
64 | |
65 func (fakePSL) PublicSuffix(domain string) string { | |
66 if domain == "" { | |
67 return "" | |
68 } | |
69 knownPublicSuffixes := [...]string{ | |
70 ".com", | |
71 ".co.uk", | |
72 ".uk", | |
73 // TODO: double-check this. http://publicsuffix.org/ currently l ists | |
74 // pvt.k12.wy.us, but Simone Carletti at submissions@publicsuffi x.org | |
75 // says that that's wrong; pvt.k12.wy.us is not in effective_tld _names.dat. | |
76 ".pvt.k12.wy.us", | |
77 ".us", | |
78 } | |
79 for _, s := range knownPublicSuffixes { | |
80 if domain == s[1:] || strings.HasSuffix(domain, s) { | |
81 return s[1:] | |
82 } | |
83 } | |
84 panic("unreachable") | |
85 } | |
86 | |
87 func (fakePSL) String() string { | |
88 return "fakePSL" | |
89 } | |
90 | |
91 func TestEffectiveTLDPlus1(t *testing.T) { | |
92 psl := PublicSuffixList(fakePSL{}) | |
93 testCases := []struct { | |
94 domain, want string | |
95 }{ | |
96 {"", "error"}, | |
97 | |
98 {"com", "error"}, | |
99 {"example.com", "example.com"}, | |
100 {"www.example.com", "example.com"}, | |
101 {"www.xxx.example.com", "example.com"}, | |
102 {"www.xxx.yyy.example.com", "example.com"}, | |
103 | |
104 {"uk", "error"}, | |
105 {"co.uk", "error"}, | |
106 {"example.co.uk", "example.co.uk"}, | |
107 {"www.example.co.uk", "example.co.uk"}, | |
108 {"www.xxx.example.co.uk", "example.co.uk"}, | |
109 {"www.xxx.yyy.example.co.uk", "example.co.uk"}, | |
110 | |
111 {"us", "error"}, | |
112 {"wy.us", "wy.us"}, | |
113 {"k12.wy.us", "wy.us"}, | |
114 {"pvt.k12.wy.us", "error"}, | |
115 {"example.pvt.k12.wy.us", "example.pvt.k12.wy.us"}, | |
116 } | |
117 for _, tc := range testCases { | |
118 got, err := effectiveTLDPlus1(tc.domain, psl) | |
119 if tc.want == "error" { | |
120 if err == nil { | |
121 t.Errorf("%q: got nil error, want non-nil", tc.d omain) | |
122 } | |
123 continue | |
124 } | |
125 if err != nil { | |
126 t.Errorf("%q: %v", tc.domain, err) | |
127 continue | |
128 } | |
129 if got != tc.want { | |
130 t.Errorf("%q: got %q, want %q", tc.domain, got, tc.want) | |
131 continue | |
132 } | |
133 } | |
134 } | |
135 | |
136 func urlMustParse(rawurl string) *url.URL { | |
137 u, err := url.Parse(rawurl) | |
138 if err != nil { | |
139 panic(err) | |
140 } | |
141 return u | |
142 } | |
143 | |
144 var ( | |
145 urlAt = urlMustParse("http://at.com/index.html") | |
146 urlOg = urlMustParse("http://og.com/index.html") | |
147 | |
148 time0 = time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC) | |
149 time1 = time0.AddDate(1, 0, 0) | |
150 time2 = time0.AddDate(2, 0, 0) | |
151 time3 = time0.AddDate(3, 0, 0) | |
152 time4 = time0.AddDate(4, 0, 0) | |
153 time9 = time0.AddDate(9, 0, 0) | |
154 ) | |
155 | |
156 type byName []*http.Cookie | |
157 | |
158 func (b byName) Len() int { return len(b) } | |
159 func (b byName) Less(i, j int) bool { return b[i].Name < b[j].Name } | |
160 func (b byName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } | |
161 | |
162 func str(cookies []*http.Cookie) string { | |
163 b := &bytes.Buffer{} | |
164 b.WriteString("{\n") | |
165 for _, c := range cookies { | |
166 fmt.Fprintf(b, "\t%v,\n", c) | |
167 } | |
168 b.WriteString("}") | |
169 return b.String() | |
170 } | |
171 | |
172 func TestBasics(t *testing.T) { | |
volker.dobler
2012/12/22 00:27:10
This type of test is hard to understand, and e.g.
| |
173 jar, err := New(nil) | |
174 if err != nil { | |
175 t.Fatalf("New: %v", err) | |
176 } | |
177 | |
178 cBatA2 := &http.Cookie{ | |
179 Name: "b", | |
180 Value: "bat-a", | |
181 Path: "/", | |
182 Domain: "at.com", | |
183 Expires: time2, | |
184 } | |
185 cCatA4 := &http.Cookie{ | |
186 Name: "c", | |
187 Value: "cat-a", | |
188 Path: "/", | |
189 Domain: "at.com", | |
190 Expires: time4, | |
191 } | |
192 cCatB4 := &http.Cookie{ | |
193 Name: "c", | |
194 Value: "cat-b", | |
195 Path: "/", | |
196 Domain: "at.com", | |
197 Expires: time4, | |
198 } | |
199 cCatC0 := &http.Cookie{ | |
200 Name: "c", | |
201 Value: "cat-c", | |
202 Path: "/", | |
203 Domain: "at.com", | |
204 Expires: time0, | |
205 } | |
206 cDogA4 := &http.Cookie{ | |
207 Name: "d", | |
208 Value: "dog-a", | |
209 Path: "/", | |
210 Domain: "og.com", | |
211 Expires: time4, | |
212 } | |
213 cHatB9 := &http.Cookie{ | |
214 Name: "h", | |
215 Value: "hat-b", | |
216 Path: "/", | |
217 Domain: "at.com", | |
218 Expires: time9, | |
219 } | |
220 | |
221 // Add some cookies for at.com. | |
222 jar.SetCookies(urlAt, []*http.Cookie{cBatA2, cCatA4}) | |
223 got, err := jar.Cookies2(urlAt, time1, nil) | |
224 if err != nil { | |
225 t.Fatalf("Cookies2: %v", err) | |
226 } | |
227 sort.Sort(byName(got)) | |
228 want := []*http.Cookie{cBatA2, cCatA4} | |
229 if !reflect.DeepEqual(got, want) { | |
230 t.Fatalf("cookies for %s:\ngot %v\nwant %v", urlAt, str(got), s tr(want)) | |
231 } | |
232 | |
233 // Add some cookies for og.com. | |
234 jar.SetCookies(urlOg, []*http.Cookie{cDogA4}) | |
235 got, err = jar.Cookies2(urlOg, time1, nil) | |
236 if err != nil { | |
237 t.Fatalf("Cookies2: %v", err) | |
238 } | |
239 sort.Sort(byName(got)) | |
240 want = []*http.Cookie{cDogA4} | |
241 if !reflect.DeepEqual(got, want) { | |
242 t.Fatalf("cookies for %s:\ngot %v\nwant %v", urlOg, str(got), s tr(want)) | |
243 } | |
244 | |
245 // Add two more cookies, overriding a previous one. | |
246 jar.SetCookies(urlAt, []*http.Cookie{cCatB4, cHatB9}) | |
247 got, err = jar.Cookies2(urlAt, time1, nil) | |
248 if err != nil { | |
249 t.Fatalf("Cookies2: %v", err) | |
250 } | |
251 sort.Sort(byName(got)) | |
252 want = []*http.Cookie{cBatA2, cCatB4, cHatB9} | |
253 if !reflect.DeepEqual(got, want) { | |
254 t.Fatalf("cookies for %s:\ngot %v\nwant %v", urlAt, str(got), s tr(want)) | |
255 } | |
256 | |
257 // Implicitly delete an expired cookie. | |
258 got, err = jar.Cookies2(urlAt, time3, nil) | |
259 if err != nil { | |
260 t.Fatalf("Cookies2: %v", err) | |
261 } | |
262 sort.Sort(byName(got)) | |
263 want = []*http.Cookie{cCatB4, cHatB9} | |
264 if !reflect.DeepEqual(got, want) { | |
265 t.Fatalf("cookies for %s:\ngot %v\nwant %v", urlAt, str(got), s tr(want)) | |
266 } | |
267 | |
268 // Explicitly delete a cookie by setting its expiry to some point in the past. | |
269 jar.SetCookies(urlAt, []*http.Cookie{cCatC0}) | |
270 got, err = jar.Cookies2(urlAt, time3, nil) | |
271 if err != nil { | |
272 t.Fatalf("Cookies2: %v", err) | |
273 } | |
274 sort.Sort(byName(got)) | |
275 want = []*http.Cookie{cHatB9} | |
276 if !reflect.DeepEqual(got, want) { | |
277 t.Fatalf("cookies for %s:\ngot %v\nwant %v", urlAt, str(got), s tr(want)) | |
278 } | |
279 } | |
280 | |
281 func TestStorage(t *testing.T) { | |
282 mem := &memStorage{} | |
283 | |
284 cHog := &http.Cookie{ | |
285 Name: "h", | |
286 Value: "hog", | |
287 Path: "/", | |
288 Domain: "og.com", | |
289 Expires: time9, | |
290 } | |
291 cLog := &http.Cookie{ | |
292 Name: "l", | |
293 Value: "log", | |
294 Path: "/", | |
295 Domain: "og.com", | |
296 Expires: time9, | |
297 } | |
298 cMat := &http.Cookie{ | |
299 Name: "m", | |
300 Value: "mat", | |
301 Path: "/", | |
302 Domain: "at.com", | |
303 Expires: time9, | |
304 } | |
305 | |
306 // Save some cookies with one jar, backed by a memStorage. | |
307 jar0, err := New(&Options{Storage: mem}) | |
308 if err != nil { | |
309 t.Fatalf("New: %v", err) | |
310 } | |
311 jar0.SetCookies(urlAt, []*http.Cookie{cMat}) | |
312 jar0.SetCookies(urlOg, []*http.Cookie{cHog, cLog}) | |
313 | |
314 // Load those cookies with another jar, backed by the same storage. | |
315 jar1, err := New(&Options{Storage: mem}) | |
316 if err != nil { | |
317 t.Fatalf("New: %v", err) | |
318 } | |
319 got, err := jar1.Cookies2(urlOg, time1, nil) | |
320 if err != nil { | |
321 t.Fatalf("Cookies2: %v", err) | |
322 } | |
323 sort.Sort(byName(got)) | |
324 want := []*http.Cookie{cHog, cLog} | |
325 if !reflect.DeepEqual(got, want) { | |
326 t.Fatalf("cookies for %s:\ngot %v\nwant %v", urlAt, str(got), s tr(want)) | |
327 } | |
328 } | |
329 | |
330 // TODO: test creation times. | |
331 // TODO: test last access times. | |
332 // TODO: test max-age. | |
333 // TODO: test sub-domains and non-/ paths. | |
334 // TODO: test Secure, HttpOnly, Persistent, HostOnly cookie bits. | |
335 // TODO: test SkipHttpOnly, ReadOnly Cookie2Options. | |
336 // TODO: test non-canonical hosts: EXAMPLE.COM, IP addresses, IDNA? | |
volker.dobler
2012/12/22 00:27:10
One more: TODO: test re-keying on changed public s
| |
OLD | NEW |