http-cookie 1.0.0.pre12 → 1.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +12 -8
- data/CHANGELOG.md +29 -0
- data/README.md +28 -0
- data/http-cookie.gemspec +7 -5
- data/lib/http/cookie.rb +37 -32
- data/lib/http/cookie/ruby_compat.rb +5 -1
- data/lib/http/cookie/scanner.rb +12 -25
- data/lib/http/cookie/version.rb +1 -1
- data/lib/http/cookie_jar.rb +57 -35
- data/lib/http/cookie_jar/abstract_saver.rb +16 -2
- data/lib/http/cookie_jar/abstract_store.rb +30 -6
- data/lib/http/cookie_jar/cookiestxt_saver.rb +23 -2
- data/lib/http/cookie_jar/hash_store.rb +6 -15
- data/lib/http/cookie_jar/mozilla_store.rb +147 -33
- data/lib/http/cookie_jar/yaml_saver.rb +29 -4
- data/test/helper.rb +30 -0
- data/test/test_http_cookie.rb +76 -23
- data/test/test_http_cookie_jar.rb +130 -14
- metadata +34 -29
data/lib/http/cookie/version.rb
CHANGED
data/lib/http/cookie_jar.rb
CHANGED
@@ -6,9 +6,6 @@ require 'http/cookie'
|
|
6
6
|
# any particular website.
|
7
7
|
|
8
8
|
class HTTP::CookieJar
|
9
|
-
require 'http/cookie_jar/abstract_store'
|
10
|
-
require 'http/cookie_jar/abstract_saver'
|
11
|
-
|
12
9
|
class << self
|
13
10
|
def const_missing(name)
|
14
11
|
case name.to_s
|
@@ -19,7 +16,7 @@ class HTTP::CookieJar
|
|
19
16
|
end
|
20
17
|
begin
|
21
18
|
require file
|
22
|
-
rescue LoadError
|
19
|
+
rescue LoadError
|
23
20
|
raise NameError, 'can\'t resolve constant %s; failed to load %s' % [name, file]
|
24
21
|
end
|
25
22
|
if const_defined?(name)
|
@@ -32,16 +29,38 @@ class HTTP::CookieJar
|
|
32
29
|
|
33
30
|
attr_reader :store
|
34
31
|
|
32
|
+
def get_impl(base, value, *args)
|
33
|
+
case value
|
34
|
+
when base
|
35
|
+
value
|
36
|
+
when Symbol
|
37
|
+
begin
|
38
|
+
base.implementation(value).new(*args)
|
39
|
+
rescue IndexError => e
|
40
|
+
raise ArgumentError, e.message
|
41
|
+
end
|
42
|
+
when Class
|
43
|
+
if base >= value
|
44
|
+
value.new(*args)
|
45
|
+
else
|
46
|
+
raise TypeError, 'not a subclass of %s: %s' % [base, value]
|
47
|
+
end
|
48
|
+
else
|
49
|
+
raise TypeError, 'invalid object: %s' % value.inspect
|
50
|
+
end
|
51
|
+
end
|
52
|
+
private :get_impl
|
53
|
+
|
35
54
|
# Generates a new cookie jar.
|
36
55
|
#
|
37
56
|
# Available option keywords are as below:
|
38
57
|
#
|
39
58
|
# :store
|
40
59
|
# : The store class that backs this jar. (default: `:hash`)
|
41
|
-
# A symbol
|
42
|
-
#
|
43
|
-
# HTTP::CookieJar::HashStore and `:mozilla`
|
44
|
-
# HTTP::CookieJar::MozillaStore.
|
60
|
+
# A symbol addressing a store class, a store class, or an instance
|
61
|
+
# of a store class is accepted. Symbols are mapped to store
|
62
|
+
# classes, like `:hash` to HTTP::CookieJar::HashStore and `:mozilla`
|
63
|
+
# to HTTP::CookieJar::MozillaStore.
|
45
64
|
#
|
46
65
|
# Any options given are passed through to the initializer of the
|
47
66
|
# specified store class. For example, the `:mozilla`
|
@@ -52,16 +71,10 @@ class HTTP::CookieJar
|
|
52
71
|
:store => :hash,
|
53
72
|
}
|
54
73
|
opthash.update(options) if options
|
55
|
-
|
56
|
-
when Symbol
|
57
|
-
@store = AbstractStore.implementation(store).new(opthash)
|
58
|
-
when AbstractStore
|
59
|
-
@store = store
|
60
|
-
else
|
61
|
-
raise TypeError, 'wrong object given as cookie store: %s' % store.inspect
|
62
|
-
end
|
74
|
+
@store = get_impl(AbstractStore, opthash[:store], opthash)
|
63
75
|
end
|
64
76
|
|
77
|
+
# The copy constructor. Not all backend store classes support cloning.
|
65
78
|
def initialize_copy(other)
|
66
79
|
@store = other.instance_eval { @store.dup }
|
67
80
|
end
|
@@ -94,7 +107,7 @@ class HTTP::CookieJar
|
|
94
107
|
begin
|
95
108
|
cookie.acceptable?
|
96
109
|
rescue RuntimeError => e
|
97
|
-
|
110
|
+
raise ArgumentError, e.message
|
98
111
|
end
|
99
112
|
self
|
100
113
|
end
|
@@ -139,7 +152,7 @@ class HTTP::CookieJar
|
|
139
152
|
#
|
140
153
|
# If (and only if) the `uri` option is given, last access time of
|
141
154
|
# each cookie is updated to the current time.
|
142
|
-
def each(uri = nil, &block)
|
155
|
+
def each(uri = nil, &block) # :yield: cookie
|
143
156
|
block_given? or return enum_for(__method__, uri)
|
144
157
|
|
145
158
|
if uri
|
@@ -193,6 +206,10 @@ class HTTP::CookieJar
|
|
193
206
|
#
|
194
207
|
# * `:format`
|
195
208
|
#
|
209
|
+
# Specifies the format for saving. A saver class, a symbol
|
210
|
+
# addressing a saver class, or a pre-generated instance of a
|
211
|
+
# saver class is accepted.
|
212
|
+
#
|
196
213
|
# <dl class="rdoc-list note-list">
|
197
214
|
# <dt>:yaml</dt>
|
198
215
|
# <dd>YAML structure (default)</dd>
|
@@ -210,7 +227,7 @@ class HTTP::CookieJar
|
|
210
227
|
# </dl>
|
211
228
|
#
|
212
229
|
# All options given are passed through to the underlying cookie
|
213
|
-
# saver module.
|
230
|
+
# saver module's constructor.
|
214
231
|
def save(writable, *options)
|
215
232
|
opthash = {
|
216
233
|
:format => :yaml,
|
@@ -223,20 +240,20 @@ class HTTP::CookieJar
|
|
223
240
|
when Symbol
|
224
241
|
opthash[:format] = options
|
225
242
|
else
|
226
|
-
|
243
|
+
if hash = Hash.try_convert(options)
|
244
|
+
opthash.update(hash)
|
245
|
+
end
|
227
246
|
end
|
228
247
|
when 2
|
229
248
|
opthash[:format], options = options
|
230
|
-
|
249
|
+
if hash = Hash.try_convert(options)
|
250
|
+
opthash.update(hash)
|
251
|
+
end
|
231
252
|
else
|
232
253
|
raise ArgumentError, 'wrong number of arguments (%d for 1-3)' % (1 + options.size)
|
233
254
|
end
|
234
255
|
|
235
|
-
|
236
|
-
saver = AbstractSaver.implementation(opthash[:format]).new(opthash)
|
237
|
-
rescue IndexError => e
|
238
|
-
raise ArgumentError, e.message
|
239
|
-
end
|
256
|
+
saver = get_impl(AbstractSaver, opthash[:format], opthash)
|
240
257
|
|
241
258
|
if writable.respond_to?(:write)
|
242
259
|
saver.save(writable, self)
|
@@ -261,6 +278,10 @@ class HTTP::CookieJar
|
|
261
278
|
#
|
262
279
|
# * `:format`
|
263
280
|
#
|
281
|
+
# Specifies the format for loading. A saver class, a symbol
|
282
|
+
# addressing a saver class, or a pre-generated instance of a
|
283
|
+
# saver class is accepted.
|
284
|
+
#
|
264
285
|
# <dl class="rdoc-list note-list">
|
265
286
|
# <dt>:yaml</dt>
|
266
287
|
# <dd>YAML structure (default)</dd>
|
@@ -269,7 +290,7 @@ class HTTP::CookieJar
|
|
269
290
|
# </dl>
|
270
291
|
#
|
271
292
|
# All options given are passed through to the underlying cookie
|
272
|
-
# saver module.
|
293
|
+
# saver module's constructor.
|
273
294
|
def load(readable, *options)
|
274
295
|
opthash = {
|
275
296
|
:format => :yaml,
|
@@ -282,20 +303,20 @@ class HTTP::CookieJar
|
|
282
303
|
when Symbol
|
283
304
|
opthash[:format] = options
|
284
305
|
else
|
285
|
-
|
306
|
+
if hash = Hash.try_convert(options)
|
307
|
+
opthash.update(hash)
|
308
|
+
end
|
286
309
|
end
|
287
310
|
when 2
|
288
311
|
opthash[:format], options = options
|
289
|
-
|
312
|
+
if hash = Hash.try_convert(options)
|
313
|
+
opthash.update(hash)
|
314
|
+
end
|
290
315
|
else
|
291
316
|
raise ArgumentError, 'wrong number of arguments (%d for 1-3)' % (1 + options.size)
|
292
317
|
end
|
293
318
|
|
294
|
-
|
295
|
-
saver = AbstractSaver.implementation(opthash[:format]).new(opthash)
|
296
|
-
rescue IndexError => e
|
297
|
-
raise ArgumentError, e.message
|
298
|
-
end
|
319
|
+
saver = get_impl(AbstractSaver, opthash[:format], opthash)
|
299
320
|
|
300
321
|
if readable.respond_to?(:write)
|
301
322
|
saver.load(readable, self)
|
@@ -314,7 +335,8 @@ class HTTP::CookieJar
|
|
314
335
|
self
|
315
336
|
end
|
316
337
|
|
317
|
-
# Removes expired cookies and returns self.
|
338
|
+
# Removes expired cookies and returns self. If `session` is true,
|
339
|
+
# all session cookies are removed as well.
|
318
340
|
def cleanup(session = false)
|
319
341
|
@store.cleanup session
|
320
342
|
self
|
@@ -1,3 +1,6 @@
|
|
1
|
+
# :markup: markdown
|
2
|
+
|
3
|
+
# An abstract superclass for all saver classes.
|
1
4
|
class HTTP::CookieJar::AbstractSaver
|
2
5
|
class << self
|
3
6
|
@@class_map = {}
|
@@ -16,20 +19,25 @@ class HTTP::CookieJar::AbstractSaver
|
|
16
19
|
end
|
17
20
|
end
|
18
21
|
|
19
|
-
def inherited(subclass)
|
22
|
+
def inherited(subclass) # :nodoc:
|
20
23
|
@@class_map[class_to_symbol(subclass)] = subclass
|
21
24
|
end
|
22
25
|
|
23
|
-
def class_to_symbol(klass)
|
26
|
+
def class_to_symbol(klass) # :nodoc:
|
24
27
|
klass.name[/[^:]+?(?=Saver$|$)/].downcase.to_sym
|
25
28
|
end
|
26
29
|
end
|
27
30
|
|
31
|
+
# Defines options and their default values.
|
28
32
|
def default_options
|
29
33
|
# {}
|
30
34
|
end
|
31
35
|
private :default_options
|
32
36
|
|
37
|
+
# :call-seq:
|
38
|
+
# new(**options)
|
39
|
+
#
|
40
|
+
# Called by the constructor of each subclass using super().
|
33
41
|
def initialize(options = nil)
|
34
42
|
options ||= {}
|
35
43
|
@logger = options[:logger]
|
@@ -41,10 +49,16 @@ class HTTP::CookieJar::AbstractSaver
|
|
41
49
|
}
|
42
50
|
end
|
43
51
|
|
52
|
+
# Implements HTTP::CookieJar#save().
|
53
|
+
#
|
54
|
+
# This is an abstract method that each subclass must override.
|
44
55
|
def save(io, jar)
|
45
56
|
# self
|
46
57
|
end
|
47
58
|
|
59
|
+
# Implements HTTP::CookieJar#load().
|
60
|
+
#
|
61
|
+
# This is an abstract method that each subclass must override.
|
48
62
|
def load(io, jar)
|
49
63
|
# self
|
50
64
|
end
|
@@ -1,5 +1,7 @@
|
|
1
|
+
# :markup: markdown
|
1
2
|
require 'monitor'
|
2
3
|
|
4
|
+
# An abstract superclass for all store classes.
|
3
5
|
class HTTP::CookieJar::AbstractStore
|
4
6
|
include MonitorMixin
|
5
7
|
|
@@ -15,25 +17,30 @@ class HTTP::CookieJar::AbstractStore
|
|
15
17
|
begin
|
16
18
|
require 'http/cookie_jar/%s_store' % symbol
|
17
19
|
@@class_map.fetch(symbol)
|
18
|
-
rescue LoadError, IndexError
|
19
|
-
raise IndexError, 'cookie store unavailable: %s' % symbol.inspect
|
20
|
+
rescue LoadError, IndexError => e
|
21
|
+
raise IndexError, 'cookie store unavailable: %s, error: %s' % symbol.inspect, e.message
|
20
22
|
end
|
21
23
|
end
|
22
24
|
|
23
|
-
def inherited(subclass)
|
25
|
+
def inherited(subclass) # :nodoc:
|
24
26
|
@@class_map[class_to_symbol(subclass)] = subclass
|
25
27
|
end
|
26
28
|
|
27
|
-
def class_to_symbol(klass)
|
29
|
+
def class_to_symbol(klass) # :nodoc:
|
28
30
|
klass.name[/[^:]+?(?=Store$|$)/].downcase.to_sym
|
29
31
|
end
|
30
32
|
end
|
31
33
|
|
34
|
+
# Defines options and their default values.
|
32
35
|
def default_options
|
33
36
|
# {}
|
34
37
|
end
|
35
38
|
private :default_options
|
36
39
|
|
40
|
+
# :call-seq:
|
41
|
+
# new(**options)
|
42
|
+
#
|
43
|
+
# Called by the constructor of each subclass using super().
|
37
44
|
def initialize(options = nil)
|
38
45
|
super() # MonitorMixin
|
39
46
|
options ||= {}
|
@@ -45,14 +52,21 @@ class HTTP::CookieJar::AbstractStore
|
|
45
52
|
}
|
46
53
|
end
|
47
54
|
|
55
|
+
# This is an abstract method that each subclass must override.
|
48
56
|
def initialize_copy(other)
|
49
57
|
# self
|
50
58
|
end
|
51
59
|
|
60
|
+
# Implements HTTP::CookieJar#add().
|
61
|
+
#
|
62
|
+
# This is an abstract method that each subclass must override.
|
52
63
|
def add(cookie)
|
53
64
|
# self
|
54
65
|
end
|
55
66
|
|
67
|
+
# Implements HTTP::CookieJar#delete().
|
68
|
+
#
|
69
|
+
# This is an abstract method that each subclass must override.
|
56
70
|
def delete(cookie)
|
57
71
|
# self
|
58
72
|
end
|
@@ -66,7 +80,9 @@ class HTTP::CookieJar::AbstractStore
|
|
66
80
|
#
|
67
81
|
# If (and only if) the +uri+ option is given, last access time of
|
68
82
|
# each cookie is updated to the current time.
|
69
|
-
|
83
|
+
#
|
84
|
+
# This is an abstract method that each subclass must override.
|
85
|
+
def each(uri = nil, &block) # :yield: cookie
|
70
86
|
# if uri
|
71
87
|
# ...
|
72
88
|
# else
|
@@ -78,14 +94,22 @@ class HTTP::CookieJar::AbstractStore
|
|
78
94
|
end
|
79
95
|
include Enumerable
|
80
96
|
|
97
|
+
# Implements HTTP::CookieJar#empty?().
|
81
98
|
def empty?
|
82
|
-
|
99
|
+
each { return false }
|
100
|
+
true
|
83
101
|
end
|
84
102
|
|
103
|
+
# Implements HTTP::CookieJar#clear().
|
104
|
+
#
|
105
|
+
# This is an abstract method that each subclass must override.
|
85
106
|
def clear
|
86
107
|
# self
|
87
108
|
end
|
88
109
|
|
110
|
+
# Implements HTTP::CookieJar#cleanup().
|
111
|
+
#
|
112
|
+
# This is an abstract method that each subclass must override.
|
89
113
|
def cleanup(session = false)
|
90
114
|
# if session
|
91
115
|
# select { |cookie| cookie.session? || cookie.expired? }
|
@@ -1,9 +1,25 @@
|
|
1
|
+
# :markup: markdown
|
1
2
|
require 'http/cookie_jar'
|
2
3
|
|
3
4
|
# CookiestxtSaver saves and loads cookies in the cookies.txt format.
|
4
5
|
class HTTP::CookieJar::CookiestxtSaver < HTTP::CookieJar::AbstractSaver
|
5
|
-
|
6
|
-
|
6
|
+
# :singleton-method: new
|
7
|
+
# :call-seq:
|
8
|
+
# new(**options)
|
9
|
+
#
|
10
|
+
# Available option keywords are below:
|
11
|
+
#
|
12
|
+
# * `:header`
|
13
|
+
#
|
14
|
+
# Specifies the header line not including a line feed, which is
|
15
|
+
# only used by #save(). None is output if nil is
|
16
|
+
# given. (default: `"# HTTP Cookie File"`)
|
17
|
+
#
|
18
|
+
# * `:linefeed`
|
19
|
+
#
|
20
|
+
# Specifies the line separator (default: `"\n"`).
|
21
|
+
|
22
|
+
##
|
7
23
|
|
8
24
|
def save(io, jar)
|
9
25
|
io.puts @header if @header
|
@@ -28,8 +44,13 @@ class HTTP::CookieJar::CookiestxtSaver < HTTP::CookieJar::AbstractSaver
|
|
28
44
|
}
|
29
45
|
end
|
30
46
|
|
47
|
+
# :stopdoc:
|
48
|
+
True = "TRUE"
|
49
|
+
False = "FALSE"
|
50
|
+
|
31
51
|
HTTPONLY_PREFIX = '#HttpOnly_'
|
32
52
|
RE_HTTPONLY_PREFIX = /\A#{HTTPONLY_PREFIX}/
|
53
|
+
# :startdoc:
|
33
54
|
|
34
55
|
# Serializes the cookie into a cookies.txt line.
|
35
56
|
def cookie_to_record(cookie)
|
@@ -1,13 +1,6 @@
|
|
1
|
+
# :markup: markdown
|
1
2
|
require 'http/cookie_jar'
|
2
3
|
|
3
|
-
# :stopdoc:
|
4
|
-
class Array
|
5
|
-
def sort_by!(&block)
|
6
|
-
replace(sort_by(&block))
|
7
|
-
end unless method_defined?(:sort_by!)
|
8
|
-
end
|
9
|
-
# :startdoc:
|
10
|
-
|
11
4
|
class HTTP::CookieJar
|
12
5
|
# A store class that uses a hash-based cookie store.
|
13
6
|
#
|
@@ -26,6 +19,9 @@ class HTTP::CookieJar
|
|
26
19
|
}
|
27
20
|
end
|
28
21
|
|
22
|
+
# :call-seq:
|
23
|
+
# new(**options)
|
24
|
+
#
|
29
25
|
# Generates a hash based cookie store.
|
30
26
|
#
|
31
27
|
# Available option keywords are as below:
|
@@ -50,6 +46,7 @@ class HTTP::CookieJar
|
|
50
46
|
@gc_index = 0
|
51
47
|
end
|
52
48
|
|
49
|
+
# The copy constructor. This store class supports cloning.
|
53
50
|
def initialize_copy(other)
|
54
51
|
@jar = Marshal.load(Marshal.dump(other.instance_variable_get(:@jar)))
|
55
52
|
end
|
@@ -67,13 +64,11 @@ class HTTP::CookieJar
|
|
67
64
|
self
|
68
65
|
end
|
69
66
|
|
70
|
-
def each(uri = nil)
|
67
|
+
def each(uri = nil) # :yield: cookie
|
71
68
|
now = Time.now
|
72
69
|
if uri
|
73
|
-
thost = DomainName.new(uri.host)
|
74
70
|
tpath = uri.path
|
75
71
|
@jar.each { |domain, paths|
|
76
|
-
next unless thost.cookie_domain?(domain)
|
77
72
|
paths.each { |path, hash|
|
78
73
|
next unless HTTP::Cookie.path_match?(path, tpath)
|
79
74
|
hash.delete_if { |name, cookie|
|
@@ -113,10 +108,6 @@ class HTTP::CookieJar
|
|
113
108
|
self
|
114
109
|
end
|
115
110
|
|
116
|
-
def empty?
|
117
|
-
@jar.empty?
|
118
|
-
end
|
119
|
-
|
120
111
|
def cleanup(session = false)
|
121
112
|
now = Time.now
|
122
113
|
all_cookies = []
|