cookiejar 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 97cc2e91638c2a60db2cc19a4ccf0ad0e2d97954
4
+ data.tar.gz: 8f4800820d1c7430437e4105dec116324f3db4ee
5
+ SHA512:
6
+ metadata.gz: 8f42ea0a868ff3c9d17c141756b3f8596f23d0e00a233ca9c13090de983b97e0093e4a59aba121e875113dfef635c9994d0d8ba5d852fd3a4e2ff2dca82025d7
7
+ data.tar.gz: bb6215c18b5c5ac93523f6fa208ea8939904a75da4eeaa95c868022af83242998af70e627e3ffc2977b8869d742651b385e1ee47bdc12f7373dd34cce46a5592
data/LICENSE ADDED
@@ -0,0 +1,10 @@
1
+ Copyright (c) 2009 - 2014, David Waite and Other Contributors
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
5
+
6
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
7
+
8
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
9
+
10
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,17 @@
1
+ Ruby CookieJar
2
+ ==============
3
+
4
+ **Git**: [http://github.com/dwaite/cookiejar](http://github.com/dwaite/cookiejar)
5
+
6
+ **Author**: David Waite
7
+
8
+ Synopsis
9
+ --------
10
+
11
+ The Ruby CookieJar is a library to help manage client-side cookies in pure Ruby. It enables parsing and setting of cookie headers, alternating between multiple 'jars' of cookies at one time (such as having a set of cookies for each browser or thread), and supports persistence of the cookies in a JSON string.
12
+
13
+ Both Netscape/RFC 2109 cookies and RFC 2965 cookies are supported.
14
+
15
+ COPYRIGHT
16
+ ---------
17
+ The Ruby CookieJar is Copyright © 2009 David Waite. Licensing terms are given within the LICENSE file.
@@ -0,0 +1,31 @@
1
+ require 'rake'
2
+
3
+ require 'rake/clean'
4
+ require 'rake/packagetask'
5
+ require 'yard'
6
+ require 'yard/rake/yardoc_task'
7
+
8
+ require 'fileutils'
9
+ include FileUtils
10
+
11
+ # Default Rake task is to run all tests
12
+ task :default => :test
13
+ CLEAN << Rake::FileList['doc/**', '.yardoc']
14
+ #Yard
15
+ YARD::Rake::YardocTask.new do |t|
16
+ t.files = ['lib/**/*.rb'] # optional
17
+ t.options = ['--title', 'CookieJar, a HTTP Client Cookie Parsing Library',
18
+ '--main', 'README.markdown', '--files', 'LICENSE']
19
+ end
20
+
21
+ begin
22
+ require 'rspec/core/rake_task'
23
+
24
+ RSpec::Core::RakeTask.new do |t|
25
+ # t.libs << 'lib'
26
+ # t.pattern = 'test/**/*_test.rb'
27
+ end
28
+ task :test => :spec
29
+ rescue LoadError
30
+ puts "Warning: unable to load rspec tasks"
31
+ end
@@ -0,0 +1,14 @@
1
+ [
2
+ {
3
+ "author": {
4
+ "github": "http://github.com/secobarbital",
5
+ "name": "Seggy Umboh"
6
+ },
7
+ "contributions": [
8
+ "widen supported IP addresses",
9
+ "fix case-sensitivity issue with HTTP headers",
10
+ "correct issue when running under Ruby 1.8.x",
11
+ "made Jar act more like a browser (dropping cookies w/o exception)"
12
+ ]
13
+ }
14
+ ]
@@ -3,25 +3,25 @@ require 'uri'
3
3
  require 'cookiejar/cookie_validation'
4
4
 
5
5
  module CookieJar
6
-
6
+
7
7
  # Cookie is an immutable object which defines the data model of a HTTP Cookie.
8
8
  # The data values within the cookie may be different from the
9
9
  # values described in the literal cookie declaration.
10
- # Specifically, the 'domain' and 'path' values may be set to defaults
10
+ # Specifically, the 'domain' and 'path' values may be set to defaults
11
11
  # based on the requested resource that resulted in the cookie being set.
12
12
  class Cookie
13
-
13
+
14
14
  # [String] The name of the cookie.
15
15
  attr_reader :name
16
16
  # [String] The value of the cookie, without any attempts at decoding.
17
17
  attr_reader :value
18
-
19
- # [String] The domain scope of the cookie. Follows the RFC 2965
18
+
19
+ # [String] The domain scope of the cookie. Follows the RFC 2965
20
20
  # 'effective host' rules. A 'dot' prefix indicates that it applies both
21
21
  # to the non-dotted domain and child domains, while no prefix indicates
22
22
  # that only exact matches of the domain are in scope.
23
23
  attr_reader :domain
24
-
24
+
25
25
  # [String] The path scope of the cookie. The cookie applies to URI paths
26
26
  # that prefix match this value.
27
27
  attr_reader :path
@@ -35,7 +35,7 @@ module CookieJar
35
35
  # [Boolean] Popular browser extension to mark a cookie as invisible
36
36
  # to code running within the browser, such as JavaScript
37
37
  attr_reader :http_only
38
-
38
+
39
39
  # [Fixnum] Version indicator, currently either
40
40
  # * 0 for netscape cookies
41
41
  # * 1 for RFC 2965 cookies
@@ -43,11 +43,11 @@ module CookieJar
43
43
  # [String] RFC 2965 field for indicating comment (or a location)
44
44
  # describing the cookie to a usesr agent.
45
45
  attr_reader :comment, :comment_url
46
- # [Boolean] RFC 2965 field for indicating session lifetime for a cookie
46
+ # [Boolean] RFC 2965 field for indicating session lifetime for a cookie
47
47
  attr_reader :discard
48
48
  # [Array<FixNum>, nil] RFC 2965 port scope for the cookie. If not nil,
49
49
  # indicates specific ports on the HTTP server which should receive this
50
- # cookie if contacted.
50
+ # cookie if contacted.
51
51
  attr_reader :ports
52
52
  # [Time] Time when this cookie was first evaluated and created.
53
53
  attr_reader :created_at
@@ -63,7 +63,7 @@ module CookieJar
63
63
  @created_at + @expiry
64
64
  end
65
65
  end
66
-
66
+
67
67
  # Indicates whether the cookie is currently considered valid
68
68
  #
69
69
  # @param [Time] time to compare against, or 'now' if omitted
@@ -74,11 +74,11 @@ module CookieJar
74
74
 
75
75
  # Indicates whether the cookie will be considered invalid after the end
76
76
  # of the current user session
77
- # @return [Boolean]
77
+ # @return [Boolean]
78
78
  def session?
79
79
  @expiry == nil || @discard
80
80
  end
81
-
81
+
82
82
  # Create a cookie based on an absolute URI and the string value of a
83
83
  # 'Set-Cookie' header.
84
84
  #
@@ -88,10 +88,12 @@ module CookieJar
88
88
  # @param set_cookie_value [String] HTTP value for the Set-Cookie header.
89
89
  # @return [Cookie] created from the header string and request URI
90
90
  # @raise [InvalidCookieError] on validation failure(s)
91
- def self.from_set_cookie request_uri, set_cookie_value
91
+ def self.from_set_cookie request_uri, set_cookie_value
92
92
  args = CookieJar::CookieValidation.parse_set_cookie set_cookie_value
93
- args[:domain] = CookieJar::CookieValidation.determine_cookie_domain request_uri, args[:domain]
94
- args[:path] = CookieJar::CookieValidation.determine_cookie_path request_uri, args[:path]
93
+ args[:domain] = CookieJar::CookieValidation.
94
+ determine_cookie_domain request_uri, args[:domain]
95
+ args[:path] = CookieJar::CookieValidation.
96
+ determine_cookie_path request_uri, args[:path]
95
97
  cookie = Cookie.new args
96
98
  CookieJar::CookieValidation.validate_cookie request_uri, cookie
97
99
  cookie
@@ -106,18 +108,20 @@ module CookieJar
106
108
  # @param set_cookie_value [String] HTTP value for the Set-Cookie2 header.
107
109
  # @return [Cookie] created from the header string and request URI
108
110
  # @raise [InvalidCookieError] on validation failure(s)
109
- def self.from_set_cookie2 request_uri, set_cookie_value
111
+ def self.from_set_cookie2 request_uri, set_cookie_value
110
112
  args = CookieJar::CookieValidation.parse_set_cookie2 set_cookie_value
111
- args[:domain] = CookieJar::CookieValidation.determine_cookie_domain request_uri, args[:domain]
112
- args[:path] = CookieJar::CookieValidation.determine_cookie_path request_uri, args[:path]
113
+ args[:domain] = CookieJar::CookieValidation.
114
+ determine_cookie_domain request_uri, args[:domain]
115
+ args[:path] = CookieJar::CookieValidation.
116
+ determine_cookie_path request_uri, args[:path]
113
117
  cookie = Cookie.new args
114
118
  CookieJar::CookieValidation.validate_cookie request_uri, cookie
115
119
  cookie
116
120
  end
117
-
121
+
118
122
  # Returns cookie in a format appropriate to send to a server.
119
123
  #
120
- # @param [FixNum] 0 version, 0 for Netscape-style cookies, 1 for
124
+ # @param [FixNum] 0 version, 0 for Netscape-style cookies, 1 for
121
125
  # RFC2965-style.
122
126
  # @param [Boolean] true prefix, for RFC2965, whether to prefix with
123
127
  # "$Version=<version>;". Ignored for Netscape-style cookies
@@ -140,6 +144,33 @@ module CookieJar
140
144
  end
141
145
  end
142
146
 
147
+
148
+ # Return a hash representation of the cookie.
149
+
150
+ def to_hash
151
+ result = {
152
+ :name => @name,
153
+ :value => @value,
154
+ :domain => @domain,
155
+ :path => @path,
156
+ :created_at => @created_at
157
+ }
158
+ {
159
+ :expiry => @expiry,
160
+ :secure => (true if @secure),
161
+ :http_only => (true if @http_only),
162
+ :version => (@version if version != 0),
163
+ :comment => @comment,
164
+ :comment_url => @comment_url,
165
+ :discard => (true if @discard),
166
+ :ports => @ports
167
+ }.each do |name, value|
168
+ result[name] = value if value
169
+ end
170
+
171
+ result
172
+ end
173
+
143
174
  # Determine if a cookie should be sent given a request URI along with
144
175
  # other options.
145
176
  #
@@ -156,47 +187,27 @@ module CookieJar
156
187
  # being sent over http, and it must not be a http_only cookie sent to
157
188
  # a script
158
189
  path_match = uri.path.start_with? @path
159
- secure_match = !(@secure && uri.scheme == 'http')
190
+ secure_match = !(@secure && uri.scheme == 'http')
160
191
  script_match = !(script && @http_only)
161
192
  expiry_match = !expired?
162
193
  ports_match = ports.nil? || (ports.include? uri.port)
163
194
  path_match && secure_match && script_match && expiry_match && ports_match
164
195
  end
165
-
196
+
166
197
  def decoded_value
167
198
  CookieJar::CookieValidation::decode_value value
168
199
  end
169
-
200
+
170
201
  # Return a JSON 'object' for the various data values. Allows for
171
202
  # persistence of the cookie information
172
203
  #
173
- # @param [Array] a options controlling output JSON text
204
+ # @param [Array] a options controlling output JSON text
174
205
  # (usually a State and a depth)
175
206
  # @return [String] JSON representation of object data
176
207
  def to_json *a
177
- result = {
178
- :json_class => self.class.name,
179
- :name => @name,
180
- :value => @value,
181
- :domain => @domain,
182
- :path => @path,
183
- :created_at => @created_at
184
- }
185
- {
186
- :expiry => @expiry,
187
- :secure => (true if @secure),
188
- :http_only => (true if @http_only),
189
- :version => (@version if version != 0),
190
- :comment => @comment,
191
- :comment_url => @comment_url,
192
- :discard => (true if @discard),
193
- :ports => @ports
194
- }.each do |name, value|
195
- result[name] = value if value
196
- end
197
- result.to_json(*a)
208
+ to_hash.merge(:json_class => self.class.name).to_json(*a)
198
209
  end
199
-
210
+
200
211
  # Given a Hash representation of a JSON document, create a local cookie
201
212
  # from the included data.
202
213
  #
@@ -218,7 +229,7 @@ module CookieJar
218
229
 
219
230
  self.new params
220
231
  end
221
-
232
+
222
233
  # Compute the cookie search domains for a given request URI
223
234
  # This will be the effective host of the request uri, along with any
224
235
  # possibly matching dot-prefixed domains
@@ -231,22 +242,22 @@ module CookieJar
231
242
  protected
232
243
  # Call {from_set_cookie} to create a new Cookie instance
233
244
  def initialize args
234
-
235
- @created_at, @name, @value, @domain, @path, @secure,
245
+
246
+ @created_at, @name, @value, @domain, @path, @secure,
236
247
  @http_only, @version, @comment, @comment_url, @discard, @ports \
237
248
  = args.values_at \
238
- :created_at, :name, :value, :domain, :path, :secure,
249
+ :created_at, :name, :value, :domain, :path, :secure,
239
250
  :http_only, :version, :comment, :comment_url, :discard, :ports
240
251
 
241
252
  @created_at ||= Time.now
242
- @expiry = args[:max_age] || args[:expires_at]
253
+ @expiry = args[:max_age] || args[:expires_at]
243
254
  @secure ||= false
244
255
  @http_only ||= false
245
256
  @discard ||= false
246
-
257
+
247
258
  if @ports.is_a? Integer
248
259
  @ports = [@ports]
249
260
  end
250
261
  end
251
262
  end
252
- end
263
+ end
@@ -5,7 +5,7 @@ module CookieJar
5
5
  class InvalidCookieError < StandardError
6
6
  # [Array<String>] the specific validation issues encountered
7
7
  attr_reader :messages
8
-
8
+
9
9
  # Create a new instance
10
10
  # @param [String, Array<String>] the validation issue(s) encountered
11
11
  def initialize message
@@ -18,7 +18,7 @@ module CookieJar
18
18
  super message
19
19
  end
20
20
  end
21
-
21
+
22
22
  # Contains logic to parse and validate cookie headers
23
23
  module CookieValidation
24
24
  module PATTERN
@@ -45,7 +45,7 @@ module CookieJar
45
45
  PARAM1 = /\A(#{PATTERN::TOKEN})(?:=#{PATTERN::VALUE1})?\Z/
46
46
  PARAM2 = Regexp.new "(#{PATTERN::TOKEN})(?:=(#{PATTERN::VALUE2}))?(?:\\Z|;)", '', 'n'
47
47
  # TWO_DOT_DOMAINS = /\A\.(com|edu|net|mil|gov|int|org)\Z/
48
-
48
+
49
49
  # Converts the input object to a URI (if not already a URI)
50
50
  #
51
51
  # @param [String, URI] request_uri URI we are normalizing
@@ -53,7 +53,7 @@ module CookieJar
53
53
  def self.to_uri request_uri
54
54
  (request_uri.is_a? URI)? request_uri : (URI.parse request_uri)
55
55
  end
56
-
56
+
57
57
  # Converts an input cookie or uri to a string representing the path.
58
58
  # Assume strings are already paths
59
59
  #
@@ -66,10 +66,10 @@ module CookieJar
66
66
  uri_or_path
67
67
  end
68
68
  end
69
-
69
+
70
70
  # Converts an input cookie or uri to a string representing the domain.
71
71
  # Assume strings are already domains. Value may not be an effective host.
72
- #
72
+ #
73
73
  # @param [String, URI, Cookie] object containing the domain
74
74
  # @return [String] domain information.
75
75
  def self.to_domain uri_or_domain
@@ -81,7 +81,7 @@ module CookieJar
81
81
  uri_or_domain
82
82
  end
83
83
  end
84
-
84
+
85
85
  # Compare a tested domain against the base domain to see if they match, or
86
86
  # if the base domain is reachable.
87
87
  #
@@ -91,11 +91,11 @@ module CookieJar
91
91
  def self.domains_match tested_domain, base_domain
92
92
  base = effective_host base_domain
93
93
  search_domains = compute_search_domains_for_host base
94
- result = search_domains.find do |domain|
95
- domain == tested_domain
94
+ result = search_domains.find do |domain|
95
+ domain == tested_domain
96
96
  end
97
97
  end
98
-
98
+
99
99
  # Compute the reach of a hostname (RFC 2965, section 1)
100
100
  # Determines the next highest superdomain
101
101
  #
@@ -109,7 +109,7 @@ module CookieJar
109
109
  match[1]
110
110
  end
111
111
  end
112
-
112
+
113
113
  # Compute the base of a path, for default cookie path assignment
114
114
  #
115
115
  # @param [String, URI, Cookie] path, or object holding path
@@ -117,7 +117,7 @@ module CookieJar
117
117
  def self.cookie_base_path path
118
118
  BASE_PATH.match(to_path path)[1]
119
119
  end
120
-
120
+
121
121
  # Processes cookie path data using the following rules:
122
122
  # Paths are separated by '/' characters, and accepted values are truncated
123
123
  # to the last '/' character. If no path is specified in the cookie, a path
@@ -131,17 +131,17 @@ module CookieJar
131
131
  def self.determine_cookie_path request_uri, cookie_path
132
132
  uri = to_uri request_uri
133
133
  cookie_path = to_path cookie_path
134
-
134
+
135
135
  if cookie_path == nil || cookie_path.empty?
136
136
  cookie_path = cookie_base_path uri.path
137
137
  end
138
138
  cookie_path
139
139
  end
140
-
140
+
141
141
  # Given a URI, compute the relevant search domains for pre-existing
142
142
  # cookies. This includes all the valid dotted forms for a named or IP
143
143
  # domains.
144
- #
144
+ #
145
145
  # @param [String, URI] request_uri requested uri
146
146
  # @return [Array<String>] all cookie domain values which would match the
147
147
  # requested uri
@@ -150,7 +150,7 @@ module CookieJar
150
150
  host = uri.host
151
151
  compute_search_domains_for_host host
152
152
  end
153
-
153
+
154
154
  # Given a host, compute the relevant search domains for pre-existing
155
155
  # cookies
156
156
  #
@@ -169,7 +169,7 @@ module CookieJar
169
169
  end
170
170
  result
171
171
  end
172
-
172
+
173
173
  # Processes cookie domain data using the following rules:
174
174
  # Domains strings of the form .foo.com match 'foo.com' and all immediate
175
175
  # subdomains of 'foo.com'. Domain strings specified of the form 'foo.com' are
@@ -189,7 +189,7 @@ module CookieJar
189
189
  def self.determine_cookie_domain request_uri, cookie_domain
190
190
  uri = to_uri request_uri
191
191
  domain = to_domain cookie_domain
192
-
192
+
193
193
  if domain == nil || domain.empty?
194
194
  domain = effective_host uri.host
195
195
  else
@@ -197,11 +197,11 @@ module CookieJar
197
197
  if domain =~ IPADDR || domain.start_with?('.')
198
198
  domain
199
199
  else
200
- ".#{domain}"
200
+ ".#{domain}"
201
201
  end
202
202
  end
203
203
  end
204
-
204
+
205
205
  # Compute the effective host (RFC 2965, section 1)
206
206
  #
207
207
  # Has the added additional logic of searching for interior dots specifically, and
@@ -212,15 +212,15 @@ module CookieJar
212
212
  def self.effective_host host_or_uri
213
213
  hostname = to_domain host_or_uri
214
214
  hostname = hostname.downcase
215
-
215
+
216
216
  if /.[\.:]./.match(hostname) || hostname == '.local'
217
217
  hostname
218
218
  else
219
219
  hostname + '.local'
220
220
  end
221
221
  end
222
-
223
- # Check whether a cookie meets all of the rules to be created, based on
222
+
223
+ # Check whether a cookie meets all of the rules to be created, based on
224
224
  # its internal settings and the URI it came from.
225
225
  #
226
226
  # @param [String,URI] request_uri originally requested URI
@@ -234,12 +234,12 @@ module CookieJar
234
234
  request_secure = (uri.scheme == 'https')
235
235
  cookie_host = cookie.domain
236
236
  cookie_path = cookie.path
237
-
237
+
238
238
  errors = []
239
-
239
+
240
240
  # From RFC 2965, Section 3.3.2 Rejecting Cookies
241
-
242
- # A user agent rejects (SHALL NOT store its information) if the
241
+
242
+ # A user agent rejects (SHALL NOT store its information) if the
243
243
  # Version attribute is missing. Note that the legacy Set-Cookie
244
244
  # directive will result in an implicit version 0.
245
245
  unless cookie.version
@@ -247,7 +247,7 @@ module CookieJar
247
247
  end
248
248
 
249
249
  # The value for the Path attribute is not a prefix of the request-URI
250
- unless request_path.start_with? cookie_path
250
+ unless request_path.start_with? cookie_path
251
251
  errors << "Path is not a prefix of the request uri path"
252
252
  end
253
253
 
@@ -256,7 +256,7 @@ module CookieJar
256
256
  cookie_host == '.local' #is the domain cookie for local addresses
257
257
  errors << "Domain format is illegal"
258
258
  end
259
-
259
+
260
260
  # The effective host name that derives from the request-host does
261
261
  # not domain-match the Domain attribute.
262
262
  #
@@ -266,7 +266,7 @@ module CookieJar
266
266
  unless domains_match cookie_host, uri
267
267
  errors << "Domain is inappropriate based on request URI hostname"
268
268
  end
269
-
269
+
270
270
  # The Port attribute has a "port-list", and the request-port was
271
271
  # not in the list.
272
272
  unless cookie.ports.nil? || cookie.ports.length != 0
@@ -275,24 +275,24 @@ module CookieJar
275
275
  end
276
276
  end
277
277
 
278
- raise (InvalidCookieError.new errors) unless errors.empty?
278
+ raise InvalidCookieError.new(errors) unless errors.empty?
279
279
 
280
280
  # Note: 'secure' is not explicitly defined as an SSL channel, and no
281
281
  # test is defined around validity and the 'secure' attribute
282
282
  true
283
283
  end
284
-
284
+
285
285
  # Break apart a traditional (non RFC 2965) cookie value into its core
286
286
  # components. This does not do any validation, or defaulting of values
287
287
  # based on requested URI
288
- #
288
+ #
289
289
  # @param [String] set_cookie_value a Set-Cookie header formatted cookie
290
290
  # definition
291
291
  # @return [Hash] Contains the parsed values of the cookie
292
292
  def self.parse_set_cookie set_cookie_value
293
293
  args = { }
294
- params=set_cookie_value.split /;\s*/
295
-
294
+ params=set_cookie_value.split(/;\s*/)
295
+
296
296
  first=true
297
297
  params.each do |param|
298
298
  result = PARAM1.match param
@@ -308,7 +308,12 @@ module CookieJar
308
308
  else
309
309
  case key
310
310
  when :expires
311
- args[:expires_at] = Time.parse keyvalue
311
+ begin
312
+ args[:expires_at] = Time.parse keyvalue
313
+ rescue ArgumentError
314
+ raise unless $!.message == "time out of range"
315
+ args[:expires_at] = Time.at(0x7FFFFFFF)
316
+ end
312
317
  when *[:domain, :path]
313
318
  args[key] = keyvalue
314
319
  when :secure
@@ -323,7 +328,7 @@ module CookieJar
323
328
  args[:version] = 0
324
329
  args
325
330
  end
326
-
331
+
327
332
  # Parse a RFC 2965 value and convert to a literal string
328
333
  def self.value_to_string value
329
334
  if /\A"(.*)"\Z/.match value
@@ -333,7 +338,7 @@ module CookieJar
333
338
  value
334
339
  end
335
340
  end
336
-
341
+
337
342
  # Attempt to decipher a partially decoded version of text cookie values
338
343
  def self.decode_value value
339
344
  if /\A"(.*)"\Z/.match value
@@ -342,11 +347,11 @@ module CookieJar
342
347
  CGI.unescape value
343
348
  end
344
349
  end
345
-
346
- # Break apart a RFC 2965 cookie value into its core components.
350
+
351
+ # Break apart a RFC 2965 cookie value into its core components.
347
352
  # This does not do any validation, or defaulting of values
348
353
  # based on requested URI
349
- #
354
+ #
350
355
  # @param [String] set_cookie_value a Set-Cookie2 header formatted cookie
351
356
  # definition
352
357
  # @return [Hash] Contains the parsed values of the cookie
@@ -360,7 +365,7 @@ module CookieJar
360
365
  raise InvalidCookieError.new "Invalid Set-Cookie2 header '#{set_cookie_value}'"
361
366
  end
362
367
  index+=md.offset(0)[1]
363
-
368
+
364
369
  key = md[1].downcase.to_sym
365
370
  keyvalue = md[2] || md[3]
366
371
  if first
@@ -382,7 +387,7 @@ module CookieJar
382
387
  args[:version] = keyvalue.to_i
383
388
  when :port
384
389
  # must be in format '"port,port"'
385
- ports = keyvalue.split /,\s*/
390
+ ports = keyvalue.split(/,\s*/)
386
391
  args[:ports] = ports.map do |portstr| portstr.to_i end
387
392
  else
388
393
  raise InvalidCookieError.new "Unknown cookie parameter '#{key}'"
@@ -393,8 +398,8 @@ module CookieJar
393
398
  if args[:version] != 1
394
399
  raise InvalidCookieError.new "Set-Cookie2 declares a non RFC2965 version cookie"
395
400
  end
396
-
401
+
397
402
  args
398
- end
403
+ end
399
404
  end
400
405
  end
@@ -1,23 +1,23 @@
1
1
  require 'cookiejar/cookie'
2
-
2
+
3
3
  module CookieJar
4
- # A cookie store for client side usage.
4
+ # A cookie store for client side usage.
5
5
  # - Enforces cookie validity rules
6
6
  # - Returns just the cookies valid for a given URI
7
7
  # - Handles expiration of cookies
8
- # - Allows for persistence of cookie data (with or without session)
8
+ # - Allows for persistence of cookie data (with or without session)
9
9
  #
10
10
  #--
11
11
  #
12
12
  # Internal format:
13
- #
13
+ #
14
14
  # Internally, the data structure is a set of nested hashes.
15
15
  # Domain Level:
16
16
  # At the domain level, the hashes are of individual domains,
17
17
  # down-cased and without any leading period. For instance, imagine cookies
18
18
  # for .foo.com, .bar.com, and .auth.bar.com:
19
19
  #
20
- # {
20
+ # {
21
21
  # "foo.com" : (host data),
22
22
  # "bar.com" : (host data),
23
23
  # "auth.bar.com" : (host data)
@@ -25,7 +25,7 @@ module CookieJar
25
25
  #
26
26
  # Lookups are done both for the matching entry, and for an entry without
27
27
  # the first segment up to the dot, ie. for /^\.?[^\.]+\.(.*)$/.
28
- # A lookup of auth.bar.com would match both bar.com and
28
+ # A lookup of auth.bar.com would match both bar.com and
29
29
  # auth.bar.com, but not entries for com or www.auth.bar.com.
30
30
  #
31
31
  # Host Level:
@@ -39,47 +39,49 @@ module CookieJar
39
39
  #
40
40
  # Paths are given a straight prefix string comparison to match.
41
41
  # Further filters <secure, http only, ports> are not represented in this
42
- # heirarchy.
42
+ # heirarchy.
43
43
  #
44
44
  # Cookies returned are ordered solely by specificity (length) of the
45
45
  # path.
46
46
  class Jar
47
47
  # Create a new empty Jar
48
48
  def initialize
49
- @domains = {}
49
+ @domains = {}
50
50
  end
51
-
51
+
52
52
  # Given a request URI and a literal Set-Cookie header value, attempt to
53
- # add the cookie to the cookie store.
54
- #
53
+ # add the cookie(s) to the cookie store.
54
+ #
55
55
  # @param [String, URI] request_uri the resource returning the header
56
56
  # @param [String] cookie_header_value the contents of the Set-Cookie
57
57
  # @return [Cookie] which was created and stored
58
58
  # @raise [InvalidCookieError] if the cookie header did not validate
59
- def set_cookie request_uri, cookie_header_value
60
- cookie = Cookie.from_set_cookie request_uri, cookie_header_value
61
- add_cookie cookie
59
+ def set_cookie request_uri, cookie_header_values
60
+ cookie_header_values.split(/, (?=[\w]+=)/).each do |cookie_header_value|
61
+ cookie = Cookie.from_set_cookie request_uri, cookie_header_value
62
+ add_cookie cookie
63
+ end
62
64
  end
63
65
 
64
66
  # Given a request URI and a literal Set-Cookie2 header value, attempt to
65
67
  # add the cookie to the cookie store.
66
- #
68
+ #
67
69
  # @param [String, URI] request_uri the resource returning the header
68
70
  # @param [String] cookie_header_value the contents of the Set-Cookie2
69
71
  # @return [Cookie] which was created and stored
70
72
  # @raise [InvalidCookieError] if the cookie header did not validate
71
73
  def set_cookie2 request_uri, cookie_header_value
72
- cookie = Cookie.from_set_cookie2 request_uri, cookie_header_value
73
- add_cookie cookie
74
+ cookie = Cookie.from_set_cookie2 request_uri, cookie_header_value
75
+ add_cookie cookie
74
76
  end
75
-
77
+
76
78
  # Given a request URI and some HTTP headers, attempt to add the cookie(s)
77
79
  # (from Set-Cookie or Set-Cookie2 headers) to the cookie store. If a
78
- # cookie is defined (by equivalent name, domain, and path) via Set-Cookie
80
+ # cookie is defined (by equivalent name, domain, and path) via Set-Cookie
79
81
  # and Set-Cookie2, the Set-Cookie version is ignored.
80
82
  #
81
83
  # @param [String, URI] request_uri the resource returning the header
82
- # @param [Hash<String,[String,Array<String>]>] http_headers a Hash
84
+ # @param [Hash<String,[String,Array<String>]>] http_headers a Hash
83
85
  # which may have a key of "Set-Cookie" or "Set-Cookie2", and values of
84
86
  # either strings or arrays of strings
85
87
  # @return [Array<Cookie>,nil] the cookies created, or nil if none found.
@@ -93,7 +95,7 @@ module CookieJar
93
95
  rescue InvalidCookieError
94
96
  end
95
97
  end
96
-
98
+
97
99
  set_cookie2_key = http_headers.keys.detect { |k| /\ASet-Cookie2\Z/i.match k }
98
100
  cookies += gather_header_values(http_headers[set_cookie2_key]) do |value|
99
101
  begin
@@ -101,7 +103,7 @@ module CookieJar
101
103
  rescue InvalidCookieError
102
104
  end
103
105
  end
104
-
106
+
105
107
  # build the list of cookies, using a Jar. Since Set-Cookie2 values
106
108
  # come second, they will replace the Set-Cookie versions.
107
109
  jar = Jar.new
@@ -109,65 +111,68 @@ module CookieJar
109
111
  jar.add_cookie cookie
110
112
  end
111
113
  cookies = jar.to_a
112
-
114
+
113
115
  # now add them all to our own store.
114
116
  cookies.each do |cookie|
115
117
  add_cookie cookie
116
118
  end
117
119
  cookies
118
120
  end
119
-
121
+
120
122
  # Add a pre-existing cookie object to the jar.
121
123
  #
122
124
  # @param [Cookie] cookie a pre-existing cookie object
123
125
  # @return [Cookie] the cookie added to the store
124
126
  def add_cookie cookie
125
127
  domain_paths = find_or_add_domain_for_cookie cookie
126
- add_cookie_to_path domain_paths, cookie
127
- cookie
128
- end
129
-
130
- # Return an array of all cookie objects in the jar
131
- #
132
- # @return [Array<Cookie>] all cookies. Includes any expired cookies
133
- # which have not yet been removed with expire_cookies
134
- def to_a
135
- result = []
136
- @domains.values.each do |paths|
137
- paths.values.each do |cookies|
138
- cookies.values.inject result, :<<
139
- end
128
+ add_cookie_to_path domain_paths, cookie
129
+ cookie
130
+ end
131
+
132
+ # Return an array of all cookie objects in the jar
133
+ #
134
+ # @return [Array<Cookie>] all cookies. Includes any expired cookies
135
+ # which have not yet been removed with expire_cookies
136
+ def to_a
137
+ result = []
138
+ @domains.values.each do |paths|
139
+ paths.values.each do |cookies|
140
+ cookies.values.inject result, :<<
141
+ end
140
142
  end
141
143
  result
142
- end
143
-
144
- # Return a JSON 'object' for the various data values. Allows for
144
+ end
145
+
146
+ # Return a JSON 'object' for the various data values. Allows for
145
147
  # persistence of the cookie information
146
148
  #
147
- # @param [Array] a options controlling output JSON text
149
+ # @param [Array] a options controlling output JSON text
148
150
  # (usually a State and a depth)
149
- # @return [String] JSON representation of object data
150
- def to_json *a
151
- {
152
- 'json_class' => self.class.name,
153
- 'cookies' => (to_a.to_json *a)
154
- }.to_json *a
151
+ # @return [String] JSON representation of object data
152
+ def to_json *a
153
+ {
154
+ 'json_class' => self.class.name,
155
+ 'cookies' => to_a.to_json(*a)
156
+ }.to_json(*a)
155
157
  end
156
-
158
+
157
159
  # Create a new Jar from a JSON-backed hash
158
160
  #
159
161
  # @param o [Hash] the expanded JSON object
160
162
  # @return [CookieJar] a new CookieJar instance
161
163
  def self.json_create o
164
+ if o.is_a? String
165
+ o = JSON.parse(o)
166
+ end
162
167
  if o.is_a? Hash
163
168
  o = o['cookies']
164
169
  end
165
- cookies = o.inject [] do |result, cookie_json|
170
+ cookies = o.inject([]) do |result, cookie_json|
166
171
  result << (Cookie.json_create cookie_json)
167
172
  end
168
173
  self.from_a cookies
169
174
  end
170
-
175
+
171
176
  # Create a new Jar from an array of Cookie objects. Expired cookies
172
177
  # will still be added to the archive, and conflicting cookies will
173
178
  # be overwritten by the last cookie in the array.
@@ -198,7 +203,7 @@ module CookieJar
198
203
  paths.empty?
199
204
  end
200
205
  end
201
-
206
+
202
207
  # Given a request URI, return a sorted list of Cookie objects. Cookies
203
208
  # will be in order per RFC 2965 - sorted by longest path length, but
204
209
  # otherwise unordered.
@@ -210,26 +215,26 @@ module CookieJar
210
215
  # if true
211
216
  # @return [Array<Cookie>] cookies which should be sent in the HTTP request
212
217
  def get_cookies request_uri, opts = { }
213
- uri = to_uri request_uri
214
- hosts = Cookie.compute_search_domains uri
215
-
216
- results = []
217
- hosts.each do |host|
218
- domain = find_domain host
219
- domain.each do |path, cookies|
220
- if uri.path.start_with? path
221
- results += cookies.values.select do |cookie|
222
- cookie.should_send? uri, opts[:script]
223
- end
224
- end
225
- end
218
+ uri = to_uri request_uri
219
+ hosts = Cookie.compute_search_domains uri
220
+
221
+ results = []
222
+ hosts.each do |host|
223
+ domain = find_domain host
224
+ domain.each do |path, cookies|
225
+ if uri.path.start_with? path
226
+ results += cookies.values.select do |cookie|
227
+ cookie.should_send? uri, opts[:script]
228
+ end
229
+ end
230
+ end
231
+ end
232
+ #Sort by path length, longest first
233
+ results.sort do |lhs, rhs|
234
+ rhs.path.length <=> lhs.path.length
226
235
  end
227
- #Sort by path length, longest first
228
- results.sort do |lhs, rhs|
229
- rhs.path.length <=> lhs.path.length
230
- end
231
236
  end
232
-
237
+
233
238
  # Given a request URI, return a string Cookie header.Cookies will be in
234
239
  # order per RFC 2965 - sorted by longest path length, but otherwise
235
240
  # unordered.
@@ -242,44 +247,43 @@ module CookieJar
242
247
  # @return String value of the Cookie header which should be sent on the
243
248
  # HTTP request
244
249
  def get_cookie_header request_uri, opts = { }
245
- cookies = get_cookies request_uri, opts
246
- version = 0
247
- ver = [[],[]]
248
- cookies.each do |cookie|
249
- ver[cookie.version] << cookie
250
- end
251
- if (ver[1].empty?)
252
- # can do a netscape-style cookie header, relish the opportunity
253
- cookies.map do |cookie|
254
- cookie.to_s
255
- end.join ";"
256
- else
257
- # build a RFC 2965-style cookie header. Split the cookies into
258
- # version 0 and 1 groups so that we can reuse the '$Version' header
259
- result = ''
260
- unless ver[0].empty?
261
- result << '$Version=0;'
262
- result << ver[0].map do |cookie|
263
- (cookie.to_s 1,false)
264
- end.join(';')
265
- # separate version 0 and 1 with a comma
266
- result << ','
250
+ cookies = get_cookies request_uri, opts
251
+ version = 0
252
+ ver = [[],[]]
253
+ cookies.each do |cookie|
254
+ ver[cookie.version] << cookie
255
+ end
256
+ if (ver[1].empty?)
257
+ # can do a netscape-style cookie header, relish the opportunity
258
+ cookies.map do |cookie|
259
+ cookie.to_s
260
+ end.join ";"
261
+ else
262
+ # build a RFC 2965-style cookie header. Split the cookies into
263
+ # version 0 and 1 groups so that we can reuse the '$Version' header
264
+ result = ''
265
+ unless ver[0].empty?
266
+ result << '$Version=0;'
267
+ result << ver[0].map do |cookie|
268
+ (cookie.to_s 1,false)
269
+ end.join(';')
270
+ # separate version 0 and 1 with a comma
271
+ result << ','
267
272
  end
268
273
  result << '$Version=1;'
269
- ver[1].map do |cookie|
270
- result << (cookie.to_s 1,false)
274
+ ver[1].map do |cookie|
275
+ result << (cookie.to_s 1,false)
271
276
  end
272
- result
277
+ result
273
278
  end
274
279
  end
275
280
 
276
- protected
281
+ protected
277
282
 
278
283
  def gather_header_values http_header_value, &block
279
284
  result = []
280
- http_header_value
281
285
  if http_header_value.is_a? Array
282
- http_header_value.each do |value|
286
+ http_header_value.each do |value|
283
287
  result << block.call(value)
284
288
  end
285
289
  elsif http_header_value.is_a? String
@@ -287,22 +291,22 @@ module CookieJar
287
291
  end
288
292
  result.compact
289
293
  end
290
-
294
+
291
295
  def to_uri request_uri
292
296
  (request_uri.is_a? URI)? request_uri : (URI.parse request_uri)
293
297
  end
294
-
298
+
295
299
  def find_domain host
296
- @domains[host] || {}
300
+ @domains[host] || {}
297
301
  end
298
302
 
299
303
  def find_or_add_domain_for_cookie cookie
300
- @domains[cookie.domain] ||= {}
304
+ @domains[cookie.domain] ||= {}
301
305
  end
302
-
306
+
303
307
  def add_cookie_to_path paths, cookie
304
- path_entry = (paths[cookie.path] ||= {})
305
- path_entry[cookie.name] = cookie
308
+ path_entry = (paths[cookie.path] ||= {})
309
+ path_entry[cookie.name] = cookie
306
310
  end
307
311
  end
308
- end
312
+ end
@@ -163,7 +163,7 @@ describe Cookie do
163
163
  end
164
164
  it "should automatically deserialize to a cookie" do
165
165
  json = "{\"json_class\":\"CookieJar::Cookie\",\"name\":\"foo\",\"value\":\"bar\",\"domain\":\"localhost.local\",\"path\":\"\\/\",\"created_at\":\"2009-09-11 12:51:03 -0600\",\"expiry\":\"2009-09-11 19:10:00 -0600\",\"secure\":true}"
166
- c = JSON.parse json
166
+ c = JSON.parse json, :create_additions => true
167
167
  c.should be_a Cookie
168
168
  CookieValidation.validate_cookie 'https://localhost/', c
169
169
  end
@@ -236,4 +236,9 @@ describe CookieValidation do
236
236
  end
237
237
  end
238
238
  end
239
+ describe '#parse_set_cookie' do
240
+ it "should max out at 2038 on 32bit systems" do
241
+ CookieValidation.parse_set_cookie("TRACK_USER_P=98237480810003948000782774;expires=Sat, 30-Jun-2040 05:39:49 GMT;path=/")[:expires_at].to_i.should >= 0x7FFFFFFF
242
+ end
243
+ end
239
244
  end
@@ -17,6 +17,10 @@ describe Jar do
17
17
  jar.set_cookie 'http://auth.foo.com/', 'foo=bar'
18
18
  jar.set_cookie 'http://auth.foo.com/', 'auth=135121...;domain=foo.com'
19
19
  end
20
+ it "should allow me to set multiple cookies in 1 header" do
21
+ jar = Jar.new
22
+ jar.set_cookie 'http://foo.com/', 'my_cookie=123456; Domain=foo.com; expires=Thu, 31 Dec 2037 23:59:59 GMT; Path=/, other_cookie=helloworld; Domain=foo.com; expires=Thu, 31 Dec 2037 23:59:59 GMT, last_cookie=098765'
23
+ end
20
24
  end
21
25
  describe '.get_cookies' do
22
26
  it "should let me read back cookies which are set" do
@@ -27,6 +31,11 @@ describe Jar do
27
31
  jar.set_cookie 'http://auth.foo.com/', 'auth=135121...;domain=foo.com'
28
32
  jar.get_cookies('http://foo.com/').should have(3).items
29
33
  end
34
+ it "should let me read back a multiple cookies from 1 header" do
35
+ jar = Jar.new
36
+ jar.set_cookie 'http://foo.com/', 'my_cookie=123456; Domain=foo.com; expires=Thu, 31 Dec 2037 23:59:59 GMT; Path=/, other_cookie=helloworld; Domain=foo.com; expires=Thu, 31 Dec 2037 23:59:59 GMT, last_cookie=098765'
37
+ jar.get_cookie_header('http://foo.com/').should == 'last_cookie=098765;my_cookie=123456;other_cookie=helloworld'
38
+ end
30
39
  it "should return cookies longest path first" do
31
40
  jar = Jar.new
32
41
  uri = 'http://foo.com/a/b/c/d'
@@ -200,7 +209,7 @@ describe Jar do
200
209
  it "should deserialize a JSON array to a jar" do
201
210
  json = "[{\"name\":\"foo\",\"value\":\"bar\",\"domain\":\"localhost.local\",\"path\":\"\\/\",\"created_at\":\"2009-09-11 12:51:03 -0600\",\"expiry\":\"2028-11-01 12:00:00 GMT\",\"secure\":true}]"
202
211
  array = JSON.parse json
203
-
212
+
204
213
  jar = Jar.json_create array
205
214
  jar.get_cookies('https://localhost/').should have(1).items
206
215
  end
@@ -214,7 +223,7 @@ describe Jar do
214
223
 
215
224
  it "should automatically deserialize to a jar" do
216
225
  json = "{\"json_class\":\"CookieJar::Jar\",\"cookies\":[{\"name\":\"foo\",\"value\":\"bar\",\"domain\":\"localhost.local\",\"path\":\"\\/\",\"created_at\":\"2009-09-11 12:51:03 -0600\",\"expiry\":\"2028-11-01 12:00:00 GMT\",\"secure\":true}]}"
217
- jar = JSON.parse json
226
+ jar = JSON.parse json, :create_additions => true
218
227
  jar.get_cookies('https://localhost/').should have(1).items
219
228
  end
220
229
  end
metadata CHANGED
@@ -1,72 +1,99 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: cookiejar
3
- version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 3
8
- - 0
9
- version: 0.3.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.1
10
5
  platform: ruby
11
- authors:
6
+ authors:
12
7
  - David Waite
13
8
  autorequire:
14
9
  bindir: bin
15
10
  cert_chain: []
16
-
17
- date: 2010-06-14 00:00:00 -06:00
18
- default_executable:
19
- dependencies: []
20
-
11
+ date: 2014-02-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '2.14'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '2.14'
41
+ - !ruby/object:Gem::Dependency
42
+ name: yard
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 0.8.7
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 0.8.7
21
55
  description: Allows for parsing and returning cookies in Ruby HTTP client code
22
56
  email: david@alkaline-solutions.com
23
57
  executables: []
24
-
25
58
  extensions: []
26
-
27
59
  extra_rdoc_files: []
28
-
29
- files:
60
+ files:
61
+ - LICENSE
62
+ - README.markdown
63
+ - Rakefile
64
+ - contributors.json
65
+ - lib/cookiejar.rb
30
66
  - lib/cookiejar/cookie.rb
31
67
  - lib/cookiejar/cookie_validation.rb
32
68
  - lib/cookiejar/jar.rb
33
- - lib/cookiejar.rb
34
- - test/cookie_test.rb
35
- - test/cookie_validation_test.rb
36
- - test/jar_test.rb
37
- has_rdoc: true
38
- homepage: http://alkaline-solutions.com
39
- licenses: []
40
-
69
+ - spec/cookie_spec.rb
70
+ - spec/cookie_validation_spec.rb
71
+ - spec/jar_spec.rb
72
+ homepage: https://alkaline-solutions.com
73
+ licenses:
74
+ - BSD-2-Clause
75
+ metadata: {}
41
76
  post_install_message:
42
- rdoc_options:
43
- - --title
77
+ rdoc_options:
78
+ - "--title"
44
79
  - CookieJar -- Client-side HTTP Cookies
45
- require_paths:
80
+ require_paths:
46
81
  - lib
47
- required_ruby_version: !ruby/object:Gem::Requirement
48
- none: false
49
- requirements:
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
50
84
  - - ">="
51
- - !ruby/object:Gem::Version
52
- hash: -1050474939376950864
53
- segments:
54
- - 0
55
- version: "0"
56
- required_rubygems_version: !ruby/object:Gem::Requirement
57
- none: false
58
- requirements:
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
59
89
  - - ">="
60
- - !ruby/object:Gem::Version
61
- segments:
62
- - 0
63
- version: "0"
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
64
92
  requirements: []
65
-
66
93
  rubyforge_project:
67
- rubygems_version: 1.3.7
94
+ rubygems_version: 2.2.0
68
95
  signing_key:
69
96
  specification_version: 3
70
97
  summary: Client-side HTTP Cookie library
71
98
  test_files: []
72
-
99
+ has_rdoc: