http-cookie 1.0.0.pre12 → 1.0.4
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.
- 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 = []
|