http-cookie 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,141 @@
1
+ require 'http/cookie_jar'
2
+
3
+ class Array
4
+ def sort_by!(&block)
5
+ replace(sort_by(&block))
6
+ end unless method_defined?(:sort_by!)
7
+ end
8
+
9
+ class HTTP::CookieJar
10
+ class HashStore < AbstractStore
11
+ GC_THRESHOLD = HTTP::Cookie::MAX_COOKIES_TOTAL / 20
12
+
13
+ def default_options
14
+ {}
15
+ end
16
+
17
+ def initialize(options = nil)
18
+ super
19
+
20
+ @jar = {}
21
+ # {
22
+ # hostname => {
23
+ # path => {
24
+ # name => cookie,
25
+ # ...
26
+ # },
27
+ # ...
28
+ # },
29
+ # ...
30
+ # }
31
+
32
+ @gc_index = 0
33
+ end
34
+
35
+ def initialize_copy(other)
36
+ @jar = Marshal.load(Marshal.dump(other.instance_variable_get(:@jar)))
37
+ end
38
+
39
+ def add(cookie)
40
+ path_cookies = ((@jar[cookie.domain_name.hostname] ||= {})[cookie.path] ||= {})
41
+
42
+ if cookie.expired?
43
+ path_cookies.delete(cookie.name)
44
+ else
45
+ path_cookies[cookie.name] = cookie
46
+ cleanup if (@gc_index += 1) >= GC_THRESHOLD
47
+ end
48
+
49
+ self
50
+ end
51
+
52
+ def each(uri = nil)
53
+ if uri
54
+ uri = URI(uri)
55
+ thost = DomainName.new(uri.host)
56
+ tpath = HTTP::Cookie.normalize_path(uri.path)
57
+ @jar.each { |domain, paths|
58
+ next unless thost.cookie_domain?(domain)
59
+ paths.each { |path, hash|
60
+ next unless tpath.start_with?(path)
61
+ hash.delete_if { |name, cookie|
62
+ if cookie.expired?
63
+ true
64
+ else
65
+ cookie.accessed_at = Time.now
66
+ yield cookie
67
+ false
68
+ end
69
+ }
70
+ }
71
+ }
72
+ else
73
+ @jar.each { |domain, paths|
74
+ paths.each { |path, hash|
75
+ hash.delete_if { |name, cookie|
76
+ if cookie.expired?
77
+ true
78
+ else
79
+ yield cookie
80
+ false
81
+ end
82
+ }
83
+ }
84
+ }
85
+ end
86
+ end
87
+
88
+ def clear
89
+ @jar.clear
90
+ self
91
+ end
92
+
93
+ def empty?
94
+ @jar.empty?
95
+ end
96
+
97
+ def cleanup(session = false)
98
+ all_cookies = []
99
+
100
+ @jar.each { |domain, paths|
101
+ domain_cookies = []
102
+
103
+ paths.each { |path, hash|
104
+ hash.delete_if { |name, cookie|
105
+ if cookie.expired? || (session && cookie.session?)
106
+ true
107
+ else
108
+ domain_cookies << cookie
109
+ false
110
+ end
111
+ }
112
+ }
113
+
114
+ if (debt = domain_cookies.size - HTTP::Cookie::MAX_COOKIES_PER_DOMAIN) > 0
115
+ domain_cookies.sort_by!(&:created_at)
116
+ domain_cookies.slice!(0, debt).each { |cookie|
117
+ add(cookie.expire)
118
+ }
119
+ end
120
+
121
+ all_cookies.concat(domain_cookies)
122
+ }
123
+
124
+ if (debt = all_cookies.size - HTTP::Cookie::MAX_COOKIES_TOTAL) > 0
125
+ all_cookies.sort_by!(&:created_at)
126
+ all_cookies.slice!(0, debt).each { |cookie|
127
+ add(cookie.expire)
128
+ }
129
+ end
130
+
131
+ @jar.delete_if { |domain, paths|
132
+ paths.delete_if { |path, hash|
133
+ hash.empty?
134
+ }
135
+ paths.empty?
136
+ }
137
+
138
+ @gc_index = 0
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,37 @@
1
+ require 'http/cookie_jar'
2
+ begin
3
+ require 'psych'
4
+ rescue LoadError
5
+ end
6
+ require 'yaml'
7
+
8
+ # YAMLSaver saves and loads cookies in the YAML format.
9
+ class HTTP::CookieJar::YAMLSaver < HTTP::CookieJar::AbstractSaver
10
+ def save(io, jar)
11
+ YAML.dump(@session ? jar.to_a : jar.reject(&:session?), io)
12
+ end
13
+
14
+ def load(io, jar)
15
+ begin
16
+ data = YAML.load(io)
17
+ rescue ArgumentError
18
+ @logger.warn "unloadable YAML cookie data discarded" if @logger
19
+ return
20
+ end
21
+
22
+ unless data.instance_of?(Array)
23
+ @logger.warn "incompatible YAML cookie data discarded" if @logger
24
+ return
25
+ end
26
+
27
+ data.each { |cookie|
28
+ jar.add(cookie)
29
+ }
30
+ end
31
+
32
+ private
33
+
34
+ def default_options
35
+ {}
36
+ end
37
+ end
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require 'test-unit'
3
+ require 'uri'
4
+ require 'http/cookie'
5
+
6
+ module Enumerable
7
+ def combine
8
+ masks = inject([[], 1]){|(ar, m), e| [ar << m, m << 1 ] }[0]
9
+ all = masks.inject(0){ |al, m| al|m }
10
+
11
+ result = []
12
+ for i in 1..all do
13
+ tmp = []
14
+ each_with_index do |e, idx|
15
+ tmp << e unless (masks[idx] & i) == 0
16
+ end
17
+ result << tmp
18
+ end
19
+ result
20
+ end
21
+ end
22
+
23
+ module Test::Unit::Assertions
24
+ def assert_raises_with_message(exc, re, message = nil, &block)
25
+ e = nil
26
+ begin
27
+ block.call
28
+ rescue Exception => e
29
+ end
30
+ assert_instance_of(exc, e, message)
31
+ assert_match(re, e.message, message)
32
+ end
33
+ end
@@ -0,0 +1,2 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
@@ -0,0 +1,624 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.expand_path('helper', File.dirname(__FILE__))
3
+
4
+ class TestHTTPCookie < Test::Unit::TestCase
5
+ def silently
6
+ warn_level = $VERBOSE
7
+ $VERBOSE = false
8
+ res = yield
9
+ $VERBOSE = warn_level
10
+ res
11
+ end
12
+
13
+ def setup
14
+ httpdate = 'Sun, 27-Sep-2037 00:00:00 GMT'
15
+
16
+ @cookie_params = {
17
+ 'expires' => 'expires=%s' % httpdate,
18
+ 'path' => 'path=/',
19
+ 'domain' => 'domain=.rubyforge.org',
20
+ 'httponly' => 'HttpOnly',
21
+ }
22
+
23
+ @expires = Time.parse(httpdate)
24
+ end
25
+
26
+ def test_parse_dates
27
+ url = URI.parse('http://localhost/')
28
+
29
+ yesterday = Time.now - 86400
30
+
31
+ dates = [ "14 Apr 89 03:20:12",
32
+ "14 Apr 89 03:20 GMT",
33
+ "Fri, 17 Mar 89 4:01:33",
34
+ "Fri, 17 Mar 89 4:01 GMT",
35
+ "Mon Jan 16 16:12 PDT 1989",
36
+ "Mon Jan 16 16:12 +0130 1989",
37
+ "6 May 1992 16:41-JST (Wednesday)",
38
+ #"22-AUG-1993 10:59:12.82",
39
+ "22-AUG-1993 10:59pm",
40
+ "22-AUG-1993 12:59am",
41
+ "22-AUG-1993 12:59 PM",
42
+ #"Friday, August 04, 1995 3:54 PM",
43
+ #"06/21/95 04:24:34 PM",
44
+ #"20/06/95 21:07",
45
+ "95-06-08 19:32:48 EDT",
46
+ ]
47
+
48
+ dates.each do |date|
49
+ cookie = "PREF=1; expires=#{date}"
50
+ silently do
51
+ HTTP::Cookie.parse(cookie, :origin => url) { |c|
52
+ assert c.expires, "Tried parsing: #{date}"
53
+ assert_equal(true, c.expires < yesterday)
54
+ }
55
+ end
56
+ end
57
+ end
58
+
59
+ def test_parse_empty
60
+ cookie_str = 'a=b; ; c=d'
61
+
62
+ uri = URI.parse 'http://example'
63
+
64
+ HTTP::Cookie.parse cookie_str, :origin => uri do |cookie|
65
+ assert_equal 'a', cookie.name
66
+ assert_equal 'b', cookie.value
67
+ end
68
+ end
69
+
70
+ def test_parse_no_space
71
+ cookie_str = "foo=bar;Expires=Sun, 06 Nov 2011 00:28:06 GMT;Path=/"
72
+
73
+ uri = URI.parse 'http://example'
74
+
75
+ HTTP::Cookie.parse cookie_str, :origin => uri do |cookie|
76
+ assert_equal 'foo', cookie.name
77
+ assert_equal 'bar', cookie.value
78
+ assert_equal '/', cookie.path
79
+ assert_equal Time.at(1320539286), cookie.expires
80
+ end
81
+ end
82
+
83
+ def test_parse_too_long_cookie
84
+ uri = URI.parse 'http://example'
85
+
86
+ cookie_str = "foo=#{'クッキー' * 340}; path=/ab/"
87
+ assert_equal(HTTP::Cookie::MAX_LENGTH - 1, cookie_str.bytesize)
88
+
89
+ assert_equal 1, HTTP::Cookie.parse(cookie_str, :origin => uri).size
90
+
91
+ assert_equal 1, HTTP::Cookie.parse(cookie_str.sub(';', 'x;'), :origin => uri).size
92
+
93
+ assert_equal 0, HTTP::Cookie.parse(cookie_str.sub(';', 'xx;'), :origin => uri).size
94
+ end
95
+
96
+ def test_parse_quoted
97
+ cookie_str =
98
+ "quoted=\"value\"; Expires=Sun, 06 Nov 2011 00:11:18 GMT; Path=/"
99
+
100
+ uri = URI.parse 'http://example'
101
+
102
+ HTTP::Cookie.parse cookie_str, :origin => uri do |cookie|
103
+ assert_equal 'quoted', cookie.name
104
+ assert_equal '"value"', cookie.value
105
+ end
106
+ end
107
+
108
+ def test_parse_weird_cookie
109
+ cookie = 'n/a, ASPSESSIONIDCSRRQDQR=FBLDGHPBNDJCPCGNCPAENELB; path=/'
110
+ url = URI.parse('http://www.searchinnovation.com/')
111
+ HTTP::Cookie.parse(cookie, :origin => url) { |c|
112
+ assert_equal('ASPSESSIONIDCSRRQDQR', c.name)
113
+ assert_equal('FBLDGHPBNDJCPCGNCPAENELB', c.value)
114
+ }
115
+ end
116
+
117
+ def test_double_semicolon
118
+ double_semi = 'WSIDC=WEST;; domain=.williams-sonoma.com; path=/'
119
+ url = URI.parse('http://williams-sonoma.com/')
120
+ HTTP::Cookie.parse(double_semi, :origin => url) { |cookie|
121
+ assert_equal('WSIDC', cookie.name)
122
+ assert_equal('WEST', cookie.value)
123
+ }
124
+ end
125
+
126
+ def test_parse_bad_version
127
+ bad_cookie = 'PRETANET=TGIAqbFXtt; Name=/PRETANET; Path=/; Version=1.2; Content-type=text/html; Domain=192.168.6.196; expires=Friday, 13-November-2026 23:01:46 GMT;'
128
+ url = URI.parse('http://localhost/')
129
+ HTTP::Cookie.parse(bad_cookie, :origin => url) { |cookie|
130
+ assert_nil(cookie.version)
131
+ }
132
+ end
133
+
134
+ def test_parse_bad_max_age
135
+ bad_cookie = 'PRETANET=TGIAqbFXtt; Name=/PRETANET; Path=/; Max-Age=1.2; Content-type=text/html; Domain=192.168.6.196; expires=Friday, 13-November-2026 23:01:46 GMT;'
136
+ url = URI.parse('http://localhost/')
137
+ HTTP::Cookie.parse(bad_cookie, :origin => url) { |cookie|
138
+ assert_nil(cookie.max_age)
139
+ }
140
+ end
141
+
142
+ def test_parse_date_fail
143
+ url = URI.parse('http://localhost/')
144
+
145
+ dates = [
146
+ "20/06/95 21:07",
147
+ ]
148
+
149
+ silently do
150
+ dates.each do |date|
151
+ cookie = "PREF=1; expires=#{date}"
152
+ HTTP::Cookie.parse(cookie, :origin => url) { |c|
153
+ assert_equal(true, c.expires.nil?)
154
+ }
155
+ end
156
+ end
157
+ end
158
+
159
+ def test_parse_domain_dot
160
+ url = URI.parse('http://host.example.com/')
161
+
162
+ cookie_str = 'a=b; domain=.example.com'
163
+
164
+ cookie = HTTP::Cookie.parse(cookie_str, :origin => url).first
165
+
166
+ assert_equal 'example.com', cookie.domain
167
+ assert cookie.for_domain?
168
+ end
169
+
170
+ def test_parse_domain_no_dot
171
+ url = URI.parse('http://host.example.com/')
172
+
173
+ cookie_str = 'a=b; domain=example.com'
174
+
175
+ cookie = HTTP::Cookie.parse(cookie_str, :origin => url).first
176
+
177
+ assert_equal 'example.com', cookie.domain
178
+ assert cookie.for_domain?
179
+ end
180
+
181
+ def test_parse_domain_none
182
+ url = URI.parse('http://example.com/')
183
+
184
+ cookie_str = 'a=b;'
185
+
186
+ cookie = HTTP::Cookie.parse(cookie_str, :origin => url).first
187
+
188
+ assert_equal 'example.com', cookie.domain
189
+ assert !cookie.for_domain?
190
+ end
191
+
192
+ def test_parse_max_age
193
+ url = URI.parse('http://localhost/')
194
+
195
+ epoch, date = 4485353164, 'Fri, 19 Feb 2112 19:26:04 GMT'
196
+ base = Time.at(1363014000)
197
+
198
+ cookie = HTTP::Cookie.parse("name=Akinori; expires=#{date}", :origin => url).first
199
+ assert_equal Time.at(epoch), cookie.expires
200
+
201
+ cookie = HTTP::Cookie.parse('name=Akinori; max-age=3600', :origin => url).first
202
+ assert_in_delta Time.now + 3600, cookie.expires, 1
203
+ cookie = HTTP::Cookie.parse('name=Akinori; max-age=3600', :origin => url, :date => base).first
204
+ assert_equal base + 3600, cookie.expires
205
+
206
+ # Max-Age has precedence over Expires
207
+ cookie = HTTP::Cookie.parse("name=Akinori; max-age=3600; expires=#{date}", :origin => url).first
208
+ assert_in_delta Time.now + 3600, cookie.expires, 1
209
+ cookie = HTTP::Cookie.parse("name=Akinori; max-age=3600; expires=#{date}", :origin => url, :date => base).first
210
+ assert_equal base + 3600, cookie.expires
211
+
212
+ cookie = HTTP::Cookie.parse("name=Akinori; expires=#{date}; max-age=3600", :origin => url).first
213
+ assert_in_delta Time.now + 3600, cookie.expires, 1
214
+ cookie = HTTP::Cookie.parse("name=Akinori; expires=#{date}; max-age=3600", :origin => url, :date => base).first
215
+ assert_equal base + 3600, cookie.expires
216
+ end
217
+
218
+ def test_parse_expires_session
219
+ url = URI.parse('http://localhost/')
220
+
221
+ [
222
+ 'name=Akinori',
223
+ 'name=Akinori; expires',
224
+ 'name=Akinori; max-age',
225
+ 'name=Akinori; expires=',
226
+ 'name=Akinori; max-age=',
227
+ ].each { |str|
228
+ cookie = HTTP::Cookie.parse(str, :origin => url).first
229
+ assert cookie.session?, str
230
+ }
231
+
232
+ [
233
+ 'name=Akinori; expires=Mon, 19 Feb 2012 19:26:04 GMT',
234
+ 'name=Akinori; max-age=3600',
235
+ ].each { |str|
236
+ cookie = HTTP::Cookie.parse(str, :origin => url).first
237
+ assert !cookie.session?, str
238
+ }
239
+ end
240
+
241
+ def test_parse_many
242
+ url = URI 'http://localhost/'
243
+ cookie_str =
244
+ "abc, " \
245
+ "name=Aaron; Domain=localhost; Expires=Sun, 06 Nov 2011 00:29:51 GMT; Path=/, " \
246
+ "name=Aaron; Domain=localhost; Expires=Sun, 06 Nov 2011 00:29:51 GMT; Path=/, " \
247
+ "name=Aaron; Domain=localhost; Expires=Sun, 06 Nov 2011 00:29:51 GMT; Path=/, " \
248
+ "name=Aaron; Domain=localhost; Expires=Sun, 06 Nov 2011 00:29:51 GMT; Path=/; HttpOnly, " \
249
+ "expired=doh; Expires=Fri, 04 Nov 2011 00:29:51 GMT; Path=/, " \
250
+ "a_path=some_path; Expires=Sun, 06 Nov 2011 00:29:51 GMT; Path=/some_path, " \
251
+ "no_path1=no_path; Expires=Sun, 06 Nov 2011 00:29:52 GMT, no_expires=nope; Path=/, " \
252
+ "no_path2=no_path; Expires=Sun, 06 Nov 2011 00:29:52 GMT; no_expires=nope; Path, " \
253
+ "no_path3=no_path; Expires=Sun, 06 Nov 2011 00:29:52 GMT; no_expires=nope; Path=, " \
254
+ "no_domain1=no_domain; Expires=Sun, 06 Nov 2011 00:29:53 GMT; no_expires=nope, " \
255
+ "no_domain2=no_domain; Expires=Sun, 06 Nov 2011 00:29:53 GMT; no_expires=nope; Domain, " \
256
+ "no_domain3=no_domain; Expires=Sun, 06 Nov 2011 00:29:53 GMT; no_expires=nope; Domain="
257
+
258
+ cookies = HTTP::Cookie.parse cookie_str, :origin => url
259
+ assert_equal 13, cookies.length
260
+
261
+ name = cookies.find { |c| c.name == 'name' }
262
+ assert_equal "Aaron", name.value
263
+ assert_equal "/", name.path
264
+ assert_equal Time.at(1320539391), name.expires
265
+
266
+ a_path = cookies.find { |c| c.name == 'a_path' }
267
+ assert_equal "some_path", a_path.value
268
+ assert_equal "/some_path", a_path.path
269
+ assert_equal Time.at(1320539391), a_path.expires
270
+
271
+ no_expires = cookies.find { |c| c.name == 'no_expires' }
272
+ assert_equal "nope", no_expires.value
273
+ assert_equal "/", no_expires.path
274
+ assert_nil no_expires.expires
275
+
276
+ no_path_cookies = cookies.select { |c| c.value == 'no_path' }
277
+ assert_equal 3, no_path_cookies.size
278
+ no_path_cookies.each { |c|
279
+ assert_equal "/", c.path, c.name
280
+ assert_equal Time.at(1320539392), c.expires, c.name
281
+ }
282
+
283
+ no_domain_cookies = cookies.select { |c| c.value == 'no_domain' }
284
+ assert_equal 3, no_domain_cookies.size
285
+ no_domain_cookies.each { |c|
286
+ assert !c.for_domain?, c.name
287
+ assert_equal c.domain, url.host, c.name
288
+ assert_equal Time.at(1320539393), c.expires, c.name
289
+ }
290
+
291
+ assert cookies.find { |c| c.name == 'expired' }
292
+ end
293
+
294
+ def test_parse_valid_cookie
295
+ url = URI.parse('http://rubyforge.org/')
296
+ cookie_params = @cookie_params
297
+ cookie_value = '12345%7D=ASDFWEE345%3DASda'
298
+
299
+ cookie_params.keys.combine.each do |keys|
300
+ cookie_text = [cookie_value, *keys.map { |key| cookie_params[key] }].join('; ')
301
+ cookie, = HTTP::Cookie.parse(cookie_text, :origin => url)
302
+
303
+ assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
304
+ assert_equal('/', cookie.path)
305
+
306
+ assert_equal(keys.include?('expires') ? @expires : nil, cookie.expires)
307
+ assert_equal(keys.include?('httponly'), cookie.httponly?)
308
+ end
309
+ end
310
+
311
+ def test_parse_valid_cookie_empty_value
312
+ url = URI.parse('http://rubyforge.org/')
313
+ cookie_params = @cookie_params
314
+ cookie_value = '12345%7D='
315
+
316
+ cookie_params.keys.combine.each do |keys|
317
+ cookie_text = [cookie_value, *keys.map { |key| cookie_params[key] }].join('; ')
318
+ cookie, = HTTP::Cookie.parse(cookie_text, :origin => url)
319
+
320
+ assert_equal('12345%7D=', cookie.to_s)
321
+ assert_equal('', cookie.value)
322
+ assert_equal('/', cookie.path)
323
+
324
+ assert_equal(keys.include?('expires') ? @expires : nil, cookie.expires)
325
+ assert_equal(keys.include?('httponly'), cookie.httponly?)
326
+ end
327
+ end
328
+
329
+ # If no path was given, use the one from the URL
330
+ def test_cookie_using_url_path
331
+ url = URI.parse('http://rubyforge.org/login.php')
332
+ cookie_params = @cookie_params
333
+ cookie_value = '12345%7D=ASDFWEE345%3DASda'
334
+
335
+ cookie_params.keys.combine.each do |keys|
336
+ next if keys.include?('path')
337
+ cookie_text = [cookie_value, *keys.map { |key| cookie_params[key] }].join('; ')
338
+ cookie, = HTTP::Cookie.parse(cookie_text, :origin => url)
339
+
340
+ assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
341
+ assert_equal('/', cookie.path)
342
+
343
+ assert_equal(keys.include?('expires') ? @expires : nil, cookie.expires)
344
+ assert_equal(keys.include?('httponly'), cookie.httponly?)
345
+ end
346
+ end
347
+
348
+ # Test using secure cookies
349
+ def test_cookie_with_secure
350
+ url = URI.parse('http://rubyforge.org/')
351
+ cookie_params = @cookie_params.merge('secure' => 'secure')
352
+ cookie_value = '12345%7D=ASDFWEE345%3DASda'
353
+
354
+ cookie_params.keys.combine.each do |keys|
355
+ next unless keys.include?('secure')
356
+ cookie_text = [cookie_value, *keys.map { |key| cookie_params[key] }].join('; ')
357
+ cookie, = HTTP::Cookie.parse(cookie_text, :origin => url)
358
+
359
+ assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
360
+ assert_equal('/', cookie.path)
361
+ assert_equal(true, cookie.secure)
362
+
363
+ assert_equal(keys.include?('expires') ? @expires : nil, cookie.expires)
364
+ assert_equal(keys.include?('httponly'), cookie.httponly?)
365
+ end
366
+ end
367
+
368
+ def test_set_cookie_value
369
+ url = URI.parse('http://rubyforge.org/')
370
+ cookie_params = @cookie_params.merge('secure' => 'secure')
371
+ cookie_value = 'foo=bar'
372
+
373
+ cookie_params.keys.combine.each do |keys|
374
+ cookie_text = [cookie_value, *keys.map { |key| cookie_params[key] }].join('; ')
375
+ cookie, = HTTP::Cookie.parse(cookie_text, :origin => url)
376
+ cookie2, = HTTP::Cookie.parse(cookie.set_cookie_value, :origin => url)
377
+
378
+ assert_equal(cookie.name, cookie2.name)
379
+ assert_equal(cookie.value, cookie2.value)
380
+ assert_equal(cookie.domain, cookie2.domain)
381
+ assert_equal(cookie.for_domain?, cookie2.for_domain?)
382
+ assert_equal(cookie.path, cookie2.path)
383
+ assert_equal(cookie.expires, cookie2.expires)
384
+ assert_equal(cookie.secure?, cookie2.secure?)
385
+ assert_equal(cookie.httponly?, cookie2.httponly?)
386
+ end
387
+ end
388
+
389
+ def test_parse_cookie_no_spaces
390
+ url = URI.parse('http://rubyforge.org/')
391
+ cookie_params = @cookie_params
392
+ cookie_value = '12345%7D=ASDFWEE345%3DASda'
393
+
394
+ cookie_params.keys.combine.each do |keys|
395
+ cookie_text = [cookie_value, *keys.map { |key| cookie_params[key] }].join(';')
396
+ cookie, = HTTP::Cookie.parse(cookie_text, :origin => url)
397
+
398
+ assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
399
+ assert_equal('/', cookie.path)
400
+
401
+ assert_equal(keys.include?('expires') ? @expires : nil, cookie.expires)
402
+ assert_equal(keys.include?('httponly'), cookie.httponly?)
403
+ end
404
+ end
405
+
406
+ def test_new
407
+ cookie = HTTP::Cookie.new('key', 'value')
408
+ assert_equal 'key', cookie.name
409
+ assert_equal 'value', cookie.value
410
+ assert_equal nil, cookie.expires
411
+
412
+ # Minimum unit for the expires attribute is second
413
+ expires = Time.at((Time.now + 3600).to_i)
414
+
415
+ cookie = HTTP::Cookie.new('key', 'value', :expires => expires.dup)
416
+ assert_equal 'key', cookie.name
417
+ assert_equal 'value', cookie.value
418
+ assert_equal expires, cookie.expires
419
+
420
+ cookie = HTTP::Cookie.new(:value => 'value', :name => 'key', :expires => expires.dup)
421
+ assert_equal 'key', cookie.name
422
+ assert_equal 'value', cookie.value
423
+ assert_equal expires, cookie.expires
424
+
425
+ cookie = HTTP::Cookie.new(:value => 'value', :name => 'key', :expires => expires.dup, :domain => 'example.org', :for_domain? => true)
426
+ assert_equal 'key', cookie.name
427
+ assert_equal 'value', cookie.value
428
+ assert_equal expires, cookie.expires
429
+ assert_equal 'example.org', cookie.domain
430
+ assert_equal true, cookie.for_domain?
431
+
432
+ assert_raises(ArgumentError) { HTTP::Cookie.new(:name => 'name') }
433
+ assert_raises(ArgumentError) { HTTP::Cookie.new(:value => 'value') }
434
+ assert_raises(ArgumentError) { HTTP::Cookie.new('', 'value') }
435
+ assert_raises(ArgumentError) { HTTP::Cookie.new('key=key', 'value') }
436
+ assert_raises(ArgumentError) { HTTP::Cookie.new("key\tkey", 'value') }
437
+ end
438
+
439
+ def cookie_values(options = {})
440
+ {
441
+ :name => 'Foo',
442
+ :value => 'Bar',
443
+ :path => '/',
444
+ :expires => Time.now + (10 * 86400),
445
+ :for_domain => true,
446
+ :domain => 'rubyforge.org',
447
+ :origin => 'http://rubyforge.org/'
448
+ }.merge(options)
449
+ end
450
+
451
+ def test_compare
452
+ time = Time.now
453
+ cookies = [
454
+ { :created_at => time + 1 },
455
+ { :created_at => time - 1 },
456
+ { :created_at => time },
457
+ { :created_at => time, :path => '/foo/bar/' },
458
+ { :created_at => time, :path => '/foo/' },
459
+ ].map { |attrs| HTTP::Cookie.new(cookie_values(attrs)) }
460
+
461
+ assert_equal([3, 4, 1, 2, 0], cookies.sort.map { |i|
462
+ cookies.find_index { |j| j.equal?(i) }
463
+ })
464
+ end
465
+
466
+ def test_expiration
467
+ cookie = HTTP::Cookie.new(cookie_values)
468
+
469
+ assert_equal false, cookie.expired?
470
+ assert_equal true, cookie.expired?(cookie.expires + 1)
471
+ assert_equal false, cookie.expired?(cookie.expires - 1)
472
+ cookie.expire
473
+ assert_equal true, cookie.expired?
474
+ end
475
+
476
+ def test_equal
477
+ assert_not_equal(HTTP::Cookie.new(cookie_values),
478
+ HTTP::Cookie.new(cookie_values(:value => 'bar')))
479
+ end
480
+
481
+ def test_new_rejects_cookies_that_do_not_contain_an_embedded_dot
482
+ url = URI 'http://rubyforge.org/'
483
+
484
+ assert_raises(ArgumentError) {
485
+ tld_cookie = HTTP::Cookie.new(cookie_values(:domain => '.org', :origin => url))
486
+ }
487
+ assert_raises(ArgumentError) {
488
+ single_dot_cookie = HTTP::Cookie.new(cookie_values(:domain => '.', :origin => url))
489
+ }
490
+ end
491
+
492
+ def test_fall_back_rules_for_local_domains
493
+ url = URI 'http://www.example.local'
494
+
495
+ assert_raises(ArgumentError) {
496
+ tld_cookie = HTTP::Cookie.new(cookie_values(:domain => '.local', :origin => url))
497
+ }
498
+
499
+ sld_cookie = HTTP::Cookie.new(cookie_values(:domain => '.example.local', :origin => url))
500
+ end
501
+
502
+ def test_new_rejects_cookies_with_ipv4_address_subdomain
503
+ url = URI 'http://192.168.0.1/'
504
+
505
+ assert_raises(ArgumentError) {
506
+ cookie = HTTP::Cookie.new(cookie_values(:domain => '.0.1', :origin => url))
507
+ }
508
+ end
509
+
510
+ def test_domain_nil
511
+ cookie = HTTP::Cookie.parse('a=b').first
512
+ assert_raises(RuntimeError) {
513
+ cookie.valid_for_uri?('http://example.com/')
514
+ }
515
+ end
516
+
517
+ def test_domain=
518
+ url = URI.parse('http://host.dom.example.com:8080/')
519
+
520
+ cookie_str = 'a=b; domain=Example.Com'
521
+ cookie = HTTP::Cookie.parse(cookie_str, :origin => url).first
522
+ assert 'example.com', cookie.domain
523
+
524
+ cookie.domain = DomainName(url.host)
525
+ assert 'host.dom.example.com', cookie.domain
526
+
527
+ cookie.domain = 'Dom.example.com'
528
+ assert 'dom.example.com', cookie.domain
529
+
530
+ cookie.domain = Object.new.tap { |o|
531
+ def o.to_str
532
+ 'Example.com'
533
+ end
534
+ }
535
+ assert 'example.com', cookie.domain
536
+ end
537
+
538
+ def test_origin=
539
+ url = URI.parse('http://example.com/path/')
540
+
541
+ cookie_str = 'a=b'
542
+ cookie = HTTP::Cookie.parse(cookie_str).first
543
+ cookie.origin = url
544
+ assert_equal '/path/', cookie.path
545
+ assert_equal 'example.com', cookie.domain
546
+ assert_equal false, cookie.for_domain
547
+ assert_raises(ArgumentError) {
548
+ cookie.origin = URI.parse('http://www.example.com/')
549
+ }
550
+
551
+ cookie_str = 'a=b; domain=.example.com; path=/'
552
+ cookie = HTTP::Cookie.parse(cookie_str).first
553
+ cookie.origin = url
554
+ assert_equal '/', cookie.path
555
+ assert_equal 'example.com', cookie.domain
556
+ assert_equal true, cookie.for_domain
557
+ assert_raises(ArgumentError) {
558
+ cookie.origin = URI.parse('http://www.example.com/')
559
+ }
560
+
561
+ cookie_str = 'a=b; domain=example.com'
562
+ cookie = HTTP::Cookie.parse(cookie_str).first
563
+ assert_raises(ArgumentError) {
564
+ cookie.origin = URI.parse('http://example.org/')
565
+ }
566
+ end
567
+
568
+ def test_valid_for_uri?
569
+ cookie = HTTP::Cookie.parse('a=b', :origin => URI('http://example.com/dir/file.html')).first
570
+ assert_equal true, cookie.valid_for_uri?(URI('https://example.com/dir/test.html'))
571
+ assert_equal true, cookie.valid_for_uri?('https://example.com/dir/test.html')
572
+ assert_equal true, cookie.valid_for_uri?(URI('http://example.com/dir/test.html'))
573
+ assert_equal false, cookie.valid_for_uri?(URI('https://example.com/dir2/test.html'))
574
+ assert_equal false, cookie.valid_for_uri?(URI('http://example.com/dir2/test.html'))
575
+ assert_equal false, cookie.valid_for_uri?(URI('https://www.example.com/dir/test.html'))
576
+ assert_equal false, cookie.valid_for_uri?(URI('http://www.example.com/dir/test.html'))
577
+ assert_equal false, cookie.valid_for_uri?(URI('https://www.example.com/dir2/test.html'))
578
+ assert_equal false, cookie.valid_for_uri?(URI('http://www.example.com/dir2/test.html'))
579
+
580
+ cookie = HTTP::Cookie.parse('a=b; path=/dir2/', :origin => URI('http://example.com/dir/file.html')).first
581
+ assert_equal false, cookie.valid_for_uri?(URI('https://example.com/dir/test.html'))
582
+ assert_equal false, cookie.valid_for_uri?(URI('http://example.com/dir/test.html'))
583
+ assert_equal true, cookie.valid_for_uri?(URI('https://example.com/dir2/test.html'))
584
+ assert_equal true, cookie.valid_for_uri?(URI('http://example.com/dir2/test.html'))
585
+ assert_equal false, cookie.valid_for_uri?(URI('https://www.example.com/dir/test.html'))
586
+ assert_equal false, cookie.valid_for_uri?(URI('http://www.example.com/dir/test.html'))
587
+ assert_equal false, cookie.valid_for_uri?(URI('https://www.example.com/dir2/test.html'))
588
+ assert_equal false, cookie.valid_for_uri?(URI('http://www.example.com/dir2/test.html'))
589
+
590
+ cookie = HTTP::Cookie.parse('a=b; domain=example.com; path=/dir2/', :origin => URI('http://example.com/dir/file.html')).first
591
+ assert_equal false, cookie.valid_for_uri?(URI('https://example.com/dir/test.html'))
592
+ assert_equal false, cookie.valid_for_uri?(URI('http://example.com/dir/test.html'))
593
+ assert_equal true, cookie.valid_for_uri?(URI('https://example.com/dir2/test.html'))
594
+ assert_equal true, cookie.valid_for_uri?(URI('http://example.com/dir2/test.html'))
595
+ assert_equal false, cookie.valid_for_uri?(URI('https://www.example.com/dir/test.html'))
596
+ assert_equal false, cookie.valid_for_uri?(URI('http://www.example.com/dir/test.html'))
597
+ assert_equal true, cookie.valid_for_uri?(URI('https://www.example.com/dir2/test.html'))
598
+ assert_equal true, cookie.valid_for_uri?(URI('http://www.example.com/dir2/test.html'))
599
+
600
+ cookie = HTTP::Cookie.parse('a=b; secure', :origin => URI('https://example.com/dir/file.html')).first
601
+ assert_equal true, cookie.valid_for_uri?(URI('https://example.com/dir/test.html'))
602
+ assert_equal false, cookie.valid_for_uri?(URI('http://example.com/dir/test.html'))
603
+ assert_equal false, cookie.valid_for_uri?(URI('https://example.com/dir2/test.html'))
604
+ assert_equal false, cookie.valid_for_uri?(URI('http://example.com/dir2/test.html'))
605
+
606
+ cookie = HTTP::Cookie.parse('a=b', :origin => URI('https://example.com/')).first
607
+ assert_equal true, cookie.valid_for_uri?(URI('https://example.com'))
608
+ end
609
+
610
+ def test_migration
611
+ assert_raises_with_message(ArgumentError, /equivalent/) {
612
+ HTTP::Cookie.parse('http://example.com/', 'key=value')
613
+ }
614
+ assert_raises_with_message(ArgumentError, /equivalent/) {
615
+ HTTP::Cookie.parse('http://example.com/', 'key=value', Object.new)
616
+ }
617
+
618
+ cookie = HTTP::Cookie.new('key', 'value')
619
+ assert_raises_with_message(NoMethodError, /equivalent/) {
620
+ cookie.set_domain('www.example.com')
621
+ }
622
+ end
623
+ end
624
+