http-cookie 1.0.0.pre2 → 1.0.0.pre3
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 +4 -4
- data/http-cookie.gemspec +1 -0
- data/lib/http/cookie.rb +6 -0
- data/lib/http/cookie/version.rb +1 -1
- data/lib/http/cookie_jar.rb +2 -3
- data/lib/http/cookie_jar/cookiestxt_saver.rb +14 -2
- data/lib/http/cookie_jar/hash_store.rb +23 -18
- data/lib/http/cookie_jar/mozilla_store.rb +327 -0
- data/test/test_http_cookie.rb +3 -0
- data/test/test_http_cookie_jar.rb +45 -8
- metadata +17 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 38b7c03ef15bbe6b866f7128b719962c7776fa16
|
|
4
|
+
data.tar.gz: 79078f90e4250e68ec2fa0b6250ee378d7681aea
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 41d93c054cda8e2fbb07554de72061fcf6218da13dce541fda0581056315b0d0438307c1f95043d59bce7742e4cee08ea9295f75c7fc1ddf297849cf7ed87ee0
|
|
7
|
+
data.tar.gz: a9cebb93040062c160bccf8758a25898af16f8b8fc2e5118a5cfb7210ba96b2f212b8154ca2b4e1fb1e2e740a5efaffd515bd9addc0ddee3be94d6e93e89f7f9
|
data/http-cookie.gemspec
CHANGED
|
@@ -24,6 +24,7 @@ Gem::Specification.new do |gem|
|
|
|
24
24
|
gem.extra_rdoc_files = ['README.md', 'LICENSE.txt']
|
|
25
25
|
|
|
26
26
|
gem.add_runtime_dependency("domain_name", ["~> 0.5"])
|
|
27
|
+
gem.add_runtime_dependency("sqlite3", ["~> 1.3.3"])
|
|
27
28
|
gem.add_development_dependency("bundler", [">= 1.2.0"])
|
|
28
29
|
gem.add_development_dependency("test-unit", [">= 2.4.3"])
|
|
29
30
|
gem.add_development_dependency("rake", [">= 0.9.2.2"])
|
data/lib/http/cookie.rb
CHANGED
|
@@ -386,6 +386,12 @@ class HTTP::Cookie
|
|
|
386
386
|
@domain = @domain_name.hostname
|
|
387
387
|
end
|
|
388
388
|
|
|
389
|
+
# Returns the domain, with a dot prefixed only if the domain flag is
|
|
390
|
+
# on.
|
|
391
|
+
def dot_domain
|
|
392
|
+
@for_domain ? '.' << @domain : @domain
|
|
393
|
+
end
|
|
394
|
+
|
|
389
395
|
# Returns the domain attribute value as a DomainName object.
|
|
390
396
|
attr_reader :domain_name
|
|
391
397
|
|
data/lib/http/cookie/version.rb
CHANGED
data/lib/http/cookie_jar.rb
CHANGED
|
@@ -13,6 +13,8 @@ class HTTP::CookieJar
|
|
|
13
13
|
# Generates a new cookie jar. The default store class is `:hash`,
|
|
14
14
|
# which maps to `HTTP::CookieJar::HashStore`. Any given options are
|
|
15
15
|
# passed through to the initializer of the specified store class.
|
|
16
|
+
# For example, the `:mozilla` (`HTTP::CookieJar::MozillaStore`)
|
|
17
|
+
# store class requires a `:filename` option.
|
|
16
18
|
def initialize(store = :hash, options = nil)
|
|
17
19
|
case store
|
|
18
20
|
when Symbol
|
|
@@ -95,9 +97,6 @@ class HTTP::CookieJar
|
|
|
95
97
|
if uri
|
|
96
98
|
uri = URI(uri)
|
|
97
99
|
return self unless URI::HTTP === uri && uri.host
|
|
98
|
-
block = proc { |cookie|
|
|
99
|
-
yield cookie if cookie.valid_for_uri?(uri)
|
|
100
|
-
}
|
|
101
100
|
end
|
|
102
101
|
|
|
103
102
|
@store.each(uri, &block)
|
|
@@ -28,11 +28,14 @@ class HTTP::CookieJar::CookiestxtSaver < HTTP::CookieJar::AbstractSaver
|
|
|
28
28
|
}
|
|
29
29
|
end
|
|
30
30
|
|
|
31
|
+
HTTPONLY_PREFIX = '#HttpOnly_'
|
|
32
|
+
RE_HTTPONLY_PREFIX = /\A#{HTTPONLY_PREFIX}/
|
|
33
|
+
|
|
31
34
|
# Serializes the cookie into a cookies.txt line.
|
|
32
35
|
def cookie_to_record(cookie)
|
|
33
36
|
cookie.instance_eval {
|
|
34
37
|
[
|
|
35
|
-
@
|
|
38
|
+
@httponly ? HTTPONLY_PREFIX + dot_domain : dot_domain,
|
|
36
39
|
@for_domain ? True : False,
|
|
37
40
|
@path,
|
|
38
41
|
@secure ? True : False,
|
|
@@ -46,7 +49,15 @@ class HTTP::CookieJar::CookiestxtSaver < HTTP::CookieJar::AbstractSaver
|
|
|
46
49
|
# Parses a line from cookies.txt and returns a cookie object if the
|
|
47
50
|
# line represents a cookie record or returns nil otherwise.
|
|
48
51
|
def parse_record(line)
|
|
49
|
-
|
|
52
|
+
case line
|
|
53
|
+
when RE_HTTPONLY_PREFIX
|
|
54
|
+
httponly = true
|
|
55
|
+
line = $'
|
|
56
|
+
when /\A#/
|
|
57
|
+
return nil
|
|
58
|
+
else
|
|
59
|
+
httponly = false
|
|
60
|
+
end
|
|
50
61
|
|
|
51
62
|
domain,
|
|
52
63
|
s_for_domain, # Whether this cookie is for domain
|
|
@@ -68,6 +79,7 @@ class HTTP::CookieJar::CookiestxtSaver < HTTP::CookieJar::AbstractSaver
|
|
|
68
79
|
:for_domain => s_for_domain == True,
|
|
69
80
|
:path => path,
|
|
70
81
|
:secure => s_secure == True,
|
|
82
|
+
:httponly => httponly,
|
|
71
83
|
:expires => expires,
|
|
72
84
|
:version => 0)
|
|
73
85
|
end
|
|
@@ -9,11 +9,12 @@ end
|
|
|
9
9
|
# :startdoc:
|
|
10
10
|
|
|
11
11
|
class HTTP::CookieJar
|
|
12
|
+
# A store class that uses a hash of hashes.
|
|
12
13
|
class HashStore < AbstractStore
|
|
13
|
-
GC_THRESHOLD = HTTP::Cookie::MAX_COOKIES_TOTAL / 20
|
|
14
|
-
|
|
15
14
|
def default_options
|
|
16
|
-
{
|
|
15
|
+
{
|
|
16
|
+
:gc_threshold => HTTP::Cookie::MAX_COOKIES_TOTAL / 20
|
|
17
|
+
}
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def initialize(options = nil)
|
|
@@ -40,18 +41,20 @@ class HTTP::CookieJar
|
|
|
40
41
|
|
|
41
42
|
def add(cookie)
|
|
42
43
|
path_cookies = ((@jar[cookie.domain_name.hostname] ||= {})[cookie.path] ||= {})
|
|
44
|
+
path_cookies[cookie.name] = cookie
|
|
45
|
+
cleanup if (@gc_index += 1) >= @gc_threshold
|
|
46
|
+
self
|
|
47
|
+
end
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
path_cookies[cookie.name] = cookie
|
|
48
|
-
cleanup if (@gc_index += 1) >= GC_THRESHOLD
|
|
49
|
-
end
|
|
50
|
-
|
|
49
|
+
def delete(cookie)
|
|
50
|
+
path_cookies = ((@jar[cookie.domain_name.hostname] ||= {})[cookie.path] ||= {})
|
|
51
|
+
path_cookies.delete(cookie.name)
|
|
51
52
|
self
|
|
52
53
|
end
|
|
54
|
+
private :delete
|
|
53
55
|
|
|
54
56
|
def each(uri = nil)
|
|
57
|
+
now = Time.now
|
|
55
58
|
if uri
|
|
56
59
|
thost = DomainName.new(uri.host)
|
|
57
60
|
tpath = uri.path
|
|
@@ -60,11 +63,13 @@ class HTTP::CookieJar
|
|
|
60
63
|
paths.each { |path, hash|
|
|
61
64
|
next unless HTTP::Cookie.path_match?(path, tpath)
|
|
62
65
|
hash.delete_if { |name, cookie|
|
|
63
|
-
if cookie.expired?
|
|
66
|
+
if cookie.expired?(now)
|
|
64
67
|
true
|
|
65
68
|
else
|
|
66
|
-
cookie.
|
|
67
|
-
|
|
69
|
+
if cookie.valid_for_uri?(uri)
|
|
70
|
+
cookie.accessed_at = now
|
|
71
|
+
yield cookie
|
|
72
|
+
end
|
|
68
73
|
false
|
|
69
74
|
end
|
|
70
75
|
}
|
|
@@ -74,7 +79,7 @@ class HTTP::CookieJar
|
|
|
74
79
|
@jar.each { |domain, paths|
|
|
75
80
|
paths.each { |path, hash|
|
|
76
81
|
hash.delete_if { |name, cookie|
|
|
77
|
-
if cookie.expired?
|
|
82
|
+
if cookie.expired?(now)
|
|
78
83
|
true
|
|
79
84
|
else
|
|
80
85
|
yield cookie
|
|
@@ -97,14 +102,14 @@ class HTTP::CookieJar
|
|
|
97
102
|
end
|
|
98
103
|
|
|
99
104
|
def cleanup(session = false)
|
|
105
|
+
now = Time.now
|
|
100
106
|
all_cookies = []
|
|
101
|
-
|
|
102
107
|
@jar.each { |domain, paths|
|
|
103
108
|
domain_cookies = []
|
|
104
109
|
|
|
105
110
|
paths.each { |path, hash|
|
|
106
111
|
hash.delete_if { |name, cookie|
|
|
107
|
-
if cookie.expired? || (session && cookie.session?)
|
|
112
|
+
if cookie.expired?(now) || (session && cookie.session?)
|
|
108
113
|
true
|
|
109
114
|
else
|
|
110
115
|
domain_cookies << cookie
|
|
@@ -116,7 +121,7 @@ class HTTP::CookieJar
|
|
|
116
121
|
if (debt = domain_cookies.size - HTTP::Cookie::MAX_COOKIES_PER_DOMAIN) > 0
|
|
117
122
|
domain_cookies.sort_by!(&:created_at)
|
|
118
123
|
domain_cookies.slice!(0, debt).each { |cookie|
|
|
119
|
-
|
|
124
|
+
delete(cookie)
|
|
120
125
|
}
|
|
121
126
|
end
|
|
122
127
|
|
|
@@ -126,7 +131,7 @@ class HTTP::CookieJar
|
|
|
126
131
|
if (debt = all_cookies.size - HTTP::Cookie::MAX_COOKIES_TOTAL) > 0
|
|
127
132
|
all_cookies.sort_by!(&:created_at)
|
|
128
133
|
all_cookies.slice!(0, debt).each { |cookie|
|
|
129
|
-
|
|
134
|
+
delete(cookie)
|
|
130
135
|
}
|
|
131
136
|
end
|
|
132
137
|
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
require 'http/cookie_jar'
|
|
2
|
+
require 'sqlite3'
|
|
3
|
+
|
|
4
|
+
class HTTP::CookieJar
|
|
5
|
+
class MozillaStore < AbstractStore
|
|
6
|
+
SCHEMA_VERSION = 5
|
|
7
|
+
|
|
8
|
+
def default_options
|
|
9
|
+
{
|
|
10
|
+
:gc_threshold => HTTP::Cookie::MAX_COOKIES_TOTAL / 20,
|
|
11
|
+
:app_id => 0,
|
|
12
|
+
:in_browser_element => false,
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
ALL_COLUMNS = %w[
|
|
17
|
+
baseDomain
|
|
18
|
+
appId inBrowserElement
|
|
19
|
+
name value
|
|
20
|
+
host path
|
|
21
|
+
expiry creationTime lastAccessed
|
|
22
|
+
isSecure isHttpOnly
|
|
23
|
+
]
|
|
24
|
+
UK_COLUMNS = %w[
|
|
25
|
+
name host path
|
|
26
|
+
appId inBrowserElement
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
def initialize(options = nil)
|
|
30
|
+
super
|
|
31
|
+
|
|
32
|
+
@filename = options[:filename] or raise ArgumentError, ':filename option is missing'
|
|
33
|
+
|
|
34
|
+
@db = SQLite3::Database.new(@filename)
|
|
35
|
+
@db.results_as_hash = true
|
|
36
|
+
|
|
37
|
+
upgrade_database
|
|
38
|
+
|
|
39
|
+
@gc_index = 0
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def schema_version
|
|
43
|
+
@schema_version ||= @db.execute("PRAGMA user_version").first[0]
|
|
44
|
+
rescue SQLite3::SQLException
|
|
45
|
+
@logger.warn "couldn't get schema version!" if @logger
|
|
46
|
+
return nil
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
protected
|
|
50
|
+
|
|
51
|
+
def schema_version=(version)
|
|
52
|
+
@db.execute("PRAGMA user_version = %d" % version)
|
|
53
|
+
@schema_version = version
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def create_table
|
|
57
|
+
self.schema_version = SCHEMA_VERSION
|
|
58
|
+
@db.execute("DROP TABLE IF EXISTS moz_cookies")
|
|
59
|
+
@db.execute(<<-'SQL')
|
|
60
|
+
CREATE TABLE moz_cookies (
|
|
61
|
+
id INTEGER PRIMARY KEY,
|
|
62
|
+
baseDomain TEXT,
|
|
63
|
+
appId INTEGER DEFAULT 0,
|
|
64
|
+
inBrowserElement INTEGER DEFAULT 0,
|
|
65
|
+
name TEXT,
|
|
66
|
+
value TEXT,
|
|
67
|
+
host TEXT,
|
|
68
|
+
path TEXT,
|
|
69
|
+
expiry INTEGER,
|
|
70
|
+
lastAccessed INTEGER,
|
|
71
|
+
creationTime INTEGER,
|
|
72
|
+
isSecure INTEGER,
|
|
73
|
+
isHttpOnly INTEGER,
|
|
74
|
+
CONSTRAINT moz_uniqueid UNIQUE (name, host, path, appId, inBrowserElement)
|
|
75
|
+
)
|
|
76
|
+
SQL
|
|
77
|
+
@db.execute(<<-'SQL')
|
|
78
|
+
CREATE INDEX moz_basedomain
|
|
79
|
+
ON moz_cookies (baseDomain,
|
|
80
|
+
appId,
|
|
81
|
+
inBrowserElement);
|
|
82
|
+
SQL
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def upgrade_database
|
|
86
|
+
loop {
|
|
87
|
+
case schema_version
|
|
88
|
+
when nil, 0
|
|
89
|
+
self.schema_version = SCHEMA_VERSION
|
|
90
|
+
break
|
|
91
|
+
when 1
|
|
92
|
+
@db.execute("ALTER TABLE moz_cookies ADD lastAccessed INTEGER")
|
|
93
|
+
self.schema_version += 1
|
|
94
|
+
when 2
|
|
95
|
+
@db.execute("ALTER TABLE moz_cookies ADD baseDomain TEXT")
|
|
96
|
+
|
|
97
|
+
st_update = @db.prepare("UPDATE moz_cookies SET baseDomain = :baseDomain WHERE id = :id")
|
|
98
|
+
|
|
99
|
+
@db.execute("SELECT id, host FROM moz_cookies") { |row|
|
|
100
|
+
domain = DomainName.new(row[:host]).domain
|
|
101
|
+
st_update.execute(:baseDomain => domain, :id => row[:id])
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@db.execute("CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)")
|
|
105
|
+
self.schema_version += 1
|
|
106
|
+
when 3
|
|
107
|
+
st_delete = @db.prepare("DELETE FROM moz_cookies WHERE id = :id")
|
|
108
|
+
|
|
109
|
+
prev_row = nil
|
|
110
|
+
@db.execute(<<-'SQL') { |row|
|
|
111
|
+
SELECT id, name, host, path FROM moz_cookies
|
|
112
|
+
ORDER BY name ASC, host ASC, path ASC, expiry ASC
|
|
113
|
+
SQL
|
|
114
|
+
if %w[name host path].all? { |col| row[col] == prev_row[col] }
|
|
115
|
+
st_delete.execute(prev_row['id'])
|
|
116
|
+
end
|
|
117
|
+
prev_row = row
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@db.execute("ALTER TABLE moz_cookies ADD creationTime INTEGER")
|
|
121
|
+
@db.execute("UPDATE moz_cookies SET creationTime = (SELECT id WHERE id = moz_cookies.id)")
|
|
122
|
+
@db.execute("CREATE UNIQUE INDEX moz_uniqueid ON moz_cookies (name, host, path)")
|
|
123
|
+
self.schema_version += 1
|
|
124
|
+
when 4
|
|
125
|
+
@db.execute("ALTER TABLE moz_cookies RENAME TO moz_cookies_old")
|
|
126
|
+
@db.execute("DROP INDEX moz_basedomain")
|
|
127
|
+
create_table
|
|
128
|
+
@db.execute(<<-'SQL')
|
|
129
|
+
INSERT INTO moz_cookies
|
|
130
|
+
(baseDomain, appId, inBrowserElement, name, value, host, path, expiry,
|
|
131
|
+
lastAccessed, creationTime, isSecure, isHttpOnly)
|
|
132
|
+
SELECT baseDomain, 0, 0, name, value, host, path, expiry,
|
|
133
|
+
lastAccessed, creationTime, isSecure, isHttpOnly
|
|
134
|
+
FROM moz_cookies_old
|
|
135
|
+
SQL
|
|
136
|
+
@db.execute("DROP TABLE moz_cookies_old")
|
|
137
|
+
@logger.info("Upgraded database to schema version %d" % schema_version) if @logger
|
|
138
|
+
else
|
|
139
|
+
break
|
|
140
|
+
end
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
begin
|
|
144
|
+
@db.execute("SELECT %s from moz_cookies limit 1" % ALL_COLUMNS.join(', '))
|
|
145
|
+
rescue SQLite3::SQLException
|
|
146
|
+
create_table
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
public
|
|
151
|
+
|
|
152
|
+
def add(cookie)
|
|
153
|
+
@st_add ||=
|
|
154
|
+
@db.prepare('INSERT OR REPLACE INTO moz_cookies (%s) VALUES (%s)' % [
|
|
155
|
+
ALL_COLUMNS.join(', '),
|
|
156
|
+
ALL_COLUMNS.map { |col| ":#{col}" }.join(', ')
|
|
157
|
+
])
|
|
158
|
+
|
|
159
|
+
@st_add.execute({
|
|
160
|
+
:baseDomain => cookie.domain_name.domain,
|
|
161
|
+
:appId => @app_id,
|
|
162
|
+
:inBrowserElement => @in_browser_element ? 1 : 0,
|
|
163
|
+
:name => cookie.name, :value => cookie.value,
|
|
164
|
+
:host => cookie.dot_domain,
|
|
165
|
+
:path => cookie.path,
|
|
166
|
+
:expiry => cookie.expires_at.to_i,
|
|
167
|
+
:creationTime => cookie.created_at.to_i,
|
|
168
|
+
:lastAccessed => cookie.accessed_at.to_i,
|
|
169
|
+
:isSecure => cookie.secure? ? 1 : 0,
|
|
170
|
+
:isHttpOnly => cookie.httponly? ? 1 : 0,
|
|
171
|
+
})
|
|
172
|
+
cleanup if (@gc_index += 1) >= @gc_threshold
|
|
173
|
+
|
|
174
|
+
self
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def each(uri = nil)
|
|
178
|
+
now = Time.now
|
|
179
|
+
if uri
|
|
180
|
+
@st_cookies_for_domain ||=
|
|
181
|
+
@db.prepare(<<-'SQL')
|
|
182
|
+
SELECT * FROM moz_cookies
|
|
183
|
+
WHERE baseDomain = :baseDomain AND
|
|
184
|
+
appId = :appId AND
|
|
185
|
+
inBrowserElement = :inBrowserElement AND
|
|
186
|
+
expiry >= :expiry
|
|
187
|
+
SQL
|
|
188
|
+
|
|
189
|
+
@st_update_lastaccessed ||=
|
|
190
|
+
@db.prepare("UPDATE moz_cookies SET lastAccessed = :lastAccessed where id = :id")
|
|
191
|
+
|
|
192
|
+
thost = DomainName.new(uri.host)
|
|
193
|
+
tpath = HTTP::Cookie.normalize_path(uri.path)
|
|
194
|
+
|
|
195
|
+
@st_cookies_for_domain.execute({
|
|
196
|
+
:baseDomain => thost.domain_name.domain,
|
|
197
|
+
:appId => @app_id,
|
|
198
|
+
:inBrowserElement => @in_browser_element ? 1 : 0,
|
|
199
|
+
:expiry => now.to_i,
|
|
200
|
+
}).each { |row|
|
|
201
|
+
if secure = row['isSecure'] != 0
|
|
202
|
+
next unless URI::HTTPS === uri
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
cookie = HTTP::Cookie.new({}.tap { |attrs|
|
|
206
|
+
attrs[:name] = row['name']
|
|
207
|
+
attrs[:value] = row['value']
|
|
208
|
+
attrs[:domain] = row['host']
|
|
209
|
+
attrs[:path] = row['path']
|
|
210
|
+
attrs[:expires_at] = Time.at(row['expiry'])
|
|
211
|
+
attrs[:accessed_at] = Time.at(row['lastAccessed'])
|
|
212
|
+
attrs[:created_at] = Time.at(row['creationTime'])
|
|
213
|
+
attrs[:secure] = secure
|
|
214
|
+
attrs[:httponly] = row['isHttpOnly'] != 0
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
if cookie.valid_for_uri?(uri)
|
|
218
|
+
cookie.accessed_at = now
|
|
219
|
+
@st_update_lastaccessed.execute({
|
|
220
|
+
'lastAccessed' => now.to_i,
|
|
221
|
+
'id' => row['id'],
|
|
222
|
+
})
|
|
223
|
+
yield cookie
|
|
224
|
+
end
|
|
225
|
+
}
|
|
226
|
+
else
|
|
227
|
+
@st_all_cookies ||=
|
|
228
|
+
@db.prepare(<<-'SQL')
|
|
229
|
+
SELECT * FROM moz_cookies
|
|
230
|
+
WHERE appId = :appId AND
|
|
231
|
+
inBrowserElement = :inBrowserElement AND
|
|
232
|
+
expiry >= :expiry
|
|
233
|
+
SQL
|
|
234
|
+
|
|
235
|
+
@st_all_cookies.execute({
|
|
236
|
+
:appId => @app_id,
|
|
237
|
+
:inBrowserElement => @in_browser_element ? 1 : 0,
|
|
238
|
+
:expiry => now.to_i,
|
|
239
|
+
}).each { |row|
|
|
240
|
+
cookie = HTTP::Cookie.new({}.tap { |attrs|
|
|
241
|
+
attrs[:name] = row['name']
|
|
242
|
+
attrs[:value] = row['value']
|
|
243
|
+
attrs[:domain] = row['host']
|
|
244
|
+
attrs[:path] = row['path']
|
|
245
|
+
attrs[:expires_at] = Time.at(row['expiry'])
|
|
246
|
+
attrs[:accessed_at] = Time.at(row['lastAccessed'])
|
|
247
|
+
attrs[:created_at] = Time.at(row['creationTime'])
|
|
248
|
+
attrs[:secure] = row['isSecure'] != 0
|
|
249
|
+
attrs[:httponly] = row['isHttpOnly'] != 0
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
yield cookie
|
|
253
|
+
}
|
|
254
|
+
end
|
|
255
|
+
self
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def clear
|
|
259
|
+
@db.execute("DELETE FROM moz_cookies")
|
|
260
|
+
self
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def count
|
|
264
|
+
@st_count ||=
|
|
265
|
+
@db.prepare("SELECT COUNT(id) FROM moz_cookies")
|
|
266
|
+
|
|
267
|
+
@st_count.execute.first[0]
|
|
268
|
+
end
|
|
269
|
+
protected :count
|
|
270
|
+
|
|
271
|
+
def empty?
|
|
272
|
+
count == 0
|
|
273
|
+
end
|
|
274
|
+
|
|
275
|
+
def cleanup(session = false)
|
|
276
|
+
now = Time.now
|
|
277
|
+
all_cookies = []
|
|
278
|
+
|
|
279
|
+
@st_delete_expired ||=
|
|
280
|
+
@db.prepare("DELETE FROM moz_cookies WHERE expiry < :expiry")
|
|
281
|
+
|
|
282
|
+
@st_overusing_domains ||=
|
|
283
|
+
@db.prepare(<<-'SQL')
|
|
284
|
+
SELECT LTRIM(host, '.') domain, COUNT(*) count
|
|
285
|
+
FROM moz_cookies
|
|
286
|
+
GROUP BY domain
|
|
287
|
+
HAVING count > :count
|
|
288
|
+
SQL
|
|
289
|
+
|
|
290
|
+
@st_delete_per_domain_overuse ||=
|
|
291
|
+
@db.prepare(<<-'SQL')
|
|
292
|
+
DELETE FROM moz_cookies WHERE id IN (
|
|
293
|
+
SELECT id FROM moz_cookies
|
|
294
|
+
WHERE LTRIM(host, '.') = :domain
|
|
295
|
+
ORDER BY creationtime
|
|
296
|
+
LIMIT :limit)
|
|
297
|
+
SQL
|
|
298
|
+
@st_delete_total_overuse ||=
|
|
299
|
+
@db.prepare(<<-'SQL')
|
|
300
|
+
DELETE FROM moz_cookies WHERE id IN (
|
|
301
|
+
SELECT id FROM moz_cookies ORDER BY creationTime ASC LIMIT :limit
|
|
302
|
+
)
|
|
303
|
+
SQL
|
|
304
|
+
|
|
305
|
+
@st_delete_expired.execute({ 'expiry' => now.to_i })
|
|
306
|
+
|
|
307
|
+
@st_overusing_domains.execute({
|
|
308
|
+
'count' => HTTP::Cookie::MAX_COOKIES_PER_DOMAIN
|
|
309
|
+
}).each { |row|
|
|
310
|
+
domain, count = row['domain'], row['count']
|
|
311
|
+
|
|
312
|
+
@st_delete_per_domain_overuse.execute({
|
|
313
|
+
'domain' => domain,
|
|
314
|
+
'limit' => count - HTTP::Cookie::MAX_COOKIES_PER_DOMAIN,
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
overrun = count - HTTP::Cookie::MAX_COOKIES_TOTAL
|
|
319
|
+
|
|
320
|
+
if overrun > 0
|
|
321
|
+
@st_delete_total_overuse.execute({ 'limit' => overrun })
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
@gc_index = 0
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
end
|
data/test/test_http_cookie.rb
CHANGED
|
@@ -162,6 +162,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|
|
162
162
|
|
|
163
163
|
assert_equal 'example.com', cookie.domain
|
|
164
164
|
assert cookie.for_domain?
|
|
165
|
+
assert_equal '.example.com', cookie.dot_domain
|
|
165
166
|
end
|
|
166
167
|
|
|
167
168
|
def test_parse_domain_no_dot
|
|
@@ -173,6 +174,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|
|
173
174
|
|
|
174
175
|
assert_equal 'example.com', cookie.domain
|
|
175
176
|
assert cookie.for_domain?
|
|
177
|
+
assert_equal '.example.com', cookie.dot_domain
|
|
176
178
|
end
|
|
177
179
|
|
|
178
180
|
def test_parse_domain_none
|
|
@@ -184,6 +186,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
|
|
184
186
|
|
|
185
187
|
assert_equal 'example.com', cookie.domain
|
|
186
188
|
assert !cookie.for_domain?
|
|
189
|
+
assert_equal 'example.com', cookie.dot_domain
|
|
187
190
|
end
|
|
188
191
|
|
|
189
192
|
def test_parse_max_age
|
|
@@ -360,20 +360,31 @@ class TestHTTPCookieJar < Test::Unit::TestCase
|
|
|
360
360
|
:value => 'Foo#Baz',
|
|
361
361
|
:path => '/foo/',
|
|
362
362
|
:for_domain => false))
|
|
363
|
+
h_cookie = HTTP::Cookie.new(cookie_values(:name => 'Quux',
|
|
364
|
+
:value => 'Foo#Quux',
|
|
365
|
+
:httponly => true))
|
|
363
366
|
|
|
364
367
|
@jar.add(cookie)
|
|
365
368
|
@jar.add(s_cookie)
|
|
366
369
|
@jar.add(cookie2)
|
|
370
|
+
@jar.add(h_cookie)
|
|
367
371
|
|
|
368
|
-
assert_equal(
|
|
372
|
+
assert_equal(4, @jar.cookies(url).length)
|
|
369
373
|
|
|
370
374
|
Dir.mktmpdir do |dir|
|
|
371
|
-
|
|
375
|
+
filename = File.join(dir, "cookies.txt")
|
|
376
|
+
@jar.save(filename, :cookiestxt)
|
|
377
|
+
|
|
378
|
+
content = File.read(filename)
|
|
379
|
+
|
|
380
|
+
assert_match(/^\.rubyforge\.org\t.*\tFoo\t/, content)
|
|
381
|
+
assert_match(/^rubyforge\.org\t.*\tBaz\t/, content)
|
|
382
|
+
assert_match(/^#HttpOnly_\.rubyforge\.org\t/, content)
|
|
372
383
|
|
|
373
384
|
jar = HTTP::CookieJar.new
|
|
374
|
-
jar.load(
|
|
385
|
+
jar.load(filename, :cookiestxt) # HACK test the format
|
|
375
386
|
cookies = jar.cookies(url)
|
|
376
|
-
assert_equal(
|
|
387
|
+
assert_equal(3, cookies.length)
|
|
377
388
|
cookies.each { |cookie|
|
|
378
389
|
case cookie.name
|
|
379
390
|
when 'Foo'
|
|
@@ -382,18 +393,27 @@ class TestHTTPCookieJar < Test::Unit::TestCase
|
|
|
382
393
|
assert_equal 'rubyforge.org', cookie.domain
|
|
383
394
|
assert_equal true, cookie.for_domain
|
|
384
395
|
assert_equal '/', cookie.path
|
|
396
|
+
assert_equal false, cookie.httponly?
|
|
385
397
|
when 'Baz'
|
|
386
398
|
assert_equal 'Foo#Baz', cookie.value
|
|
387
399
|
assert_equal 'rubyforge.org', cookie.domain
|
|
388
400
|
assert_equal false, cookie.for_domain
|
|
389
401
|
assert_equal '/foo/', cookie.path
|
|
402
|
+
assert_equal false, cookie.httponly?
|
|
403
|
+
when 'Quux'
|
|
404
|
+
assert_equal 'Foo#Quux', cookie.value
|
|
405
|
+
assert_equal expires, cookie.expires
|
|
406
|
+
assert_equal 'rubyforge.org', cookie.domain
|
|
407
|
+
assert_equal true, cookie.for_domain
|
|
408
|
+
assert_equal '/', cookie.path
|
|
409
|
+
assert_equal true, cookie.httponly?
|
|
390
410
|
else
|
|
391
411
|
raise
|
|
392
412
|
end
|
|
393
413
|
}
|
|
394
414
|
end
|
|
395
415
|
|
|
396
|
-
assert_equal(
|
|
416
|
+
assert_equal(4, @jar.cookies(url).length)
|
|
397
417
|
end
|
|
398
418
|
|
|
399
419
|
def test_expire_cookies
|
|
@@ -514,8 +534,7 @@ class TestHTTPCookieJar < Test::Unit::TestCase
|
|
|
514
534
|
assert_equal('Foo1 Foo2', @jar.cookies(surl).map { |c| c.name }.sort.join(' ') )
|
|
515
535
|
end
|
|
516
536
|
|
|
517
|
-
def
|
|
518
|
-
jar = HTTP::CookieJar.new
|
|
537
|
+
def h_test_max_cookies(jar, slimit)
|
|
519
538
|
limit_per_domain = HTTP::Cookie::MAX_COOKIES_PER_DOMAIN
|
|
520
539
|
uri = URI('http://www.example.org/')
|
|
521
540
|
date = Time.at(Time.now.to_i + 86400)
|
|
@@ -540,7 +559,6 @@ class TestHTTPCookieJar < Test::Unit::TestCase
|
|
|
540
559
|
}.sort
|
|
541
560
|
|
|
542
561
|
hlimit = HTTP::Cookie::MAX_COOKIES_TOTAL
|
|
543
|
-
slimit = hlimit + HTTP::CookieJar::HashStore::GC_THRESHOLD
|
|
544
562
|
|
|
545
563
|
n = hlimit / limit_per_domain * 2
|
|
546
564
|
|
|
@@ -569,4 +587,23 @@ class TestHTTPCookieJar < Test::Unit::TestCase
|
|
|
569
587
|
cookie.domain == cookie.value
|
|
570
588
|
}
|
|
571
589
|
end
|
|
590
|
+
|
|
591
|
+
def test_max_cookies_hashstore
|
|
592
|
+
gc_threshold = 150
|
|
593
|
+
h_test_max_cookies(
|
|
594
|
+
HTTP::CookieJar.new(:hash,
|
|
595
|
+
:gc_threshold => gc_threshold),
|
|
596
|
+
HTTP::Cookie::MAX_COOKIES_TOTAL + gc_threshold)
|
|
597
|
+
end
|
|
598
|
+
|
|
599
|
+
def test_max_cookies_mozillastore
|
|
600
|
+
gc_threshold = 150
|
|
601
|
+
Dir.mktmpdir { |dir|
|
|
602
|
+
h_test_max_cookies(
|
|
603
|
+
HTTP::CookieJar.new(:mozilla,
|
|
604
|
+
:gc_threshold => gc_threshold,
|
|
605
|
+
:filename => File.join(dir, "cookies.sqlite")),
|
|
606
|
+
HTTP::Cookie::MAX_COOKIES_TOTAL + gc_threshold)
|
|
607
|
+
}
|
|
608
|
+
end
|
|
572
609
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: http-cookie
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.0.
|
|
4
|
+
version: 1.0.0.pre3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Akinori MUSHA
|
|
@@ -11,7 +11,7 @@ authors:
|
|
|
11
11
|
autorequire:
|
|
12
12
|
bindir: bin
|
|
13
13
|
cert_chain: []
|
|
14
|
-
date: 2013-03-
|
|
14
|
+
date: 2013-03-27 00:00:00.000000000 Z
|
|
15
15
|
dependencies:
|
|
16
16
|
- !ruby/object:Gem::Dependency
|
|
17
17
|
name: domain_name
|
|
@@ -27,6 +27,20 @@ dependencies:
|
|
|
27
27
|
- - ~>
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
29
|
version: '0.5'
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
name: sqlite3
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
requirements:
|
|
34
|
+
- - ~>
|
|
35
|
+
- !ruby/object:Gem::Version
|
|
36
|
+
version: 1.3.3
|
|
37
|
+
type: :runtime
|
|
38
|
+
prerelease: false
|
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
40
|
+
requirements:
|
|
41
|
+
- - ~>
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: 1.3.3
|
|
30
44
|
- !ruby/object:Gem::Dependency
|
|
31
45
|
name: bundler
|
|
32
46
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -125,6 +139,7 @@ files:
|
|
|
125
139
|
- lib/http/cookie_jar/abstract_store.rb
|
|
126
140
|
- lib/http/cookie_jar/cookiestxt_saver.rb
|
|
127
141
|
- lib/http/cookie_jar/hash_store.rb
|
|
142
|
+
- lib/http/cookie_jar/mozilla_store.rb
|
|
128
143
|
- lib/http/cookie_jar/yaml_saver.rb
|
|
129
144
|
- test/helper.rb
|
|
130
145
|
- test/simplecov_start.rb
|