dwaite-cookiejar 0.1.3 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/cookiejar.rb CHANGED
@@ -1,3 +1,2 @@
1
- require 'cookiejar/cookie_common'
2
1
  require 'cookiejar/cookie'
3
2
  require 'cookiejar/jar'
@@ -1,6 +1,6 @@
1
1
  require 'time'
2
2
  require 'uri'
3
- require 'cookiejar/cookie_common'
3
+ require 'cookiejar/cookie_validation'
4
4
 
5
5
  module CookieJar
6
6
 
@@ -10,28 +10,6 @@ module CookieJar
10
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
- module PATTERN
14
- include URI::REGEXP::PATTERN
15
-
16
- TOKEN = '[^(),\/<>@;:\\\"\[\]?={}\s]*'
17
- VALUE1 = "([^;]*)"
18
- IPADDR = "#{IPV4ADDR}|#{IPV6ADDR}"
19
- BASE_HOSTNAME = "(?:#{DOMLABEL}\\.)(?:((?:(?:#{DOMLABEL}\\.)+(?:#{TOPLABEL}\\.?))|local))"
20
-
21
- # QUOTED_PAIR = "\\\\[\\x00-\\x7F]"
22
- # LWS = "\\r\\n(?:[ \\t]+)"
23
- # TEXT="[\\t\\x20-\\x7E\\x80-\\xFF]|(?:#{LWS})"
24
- # QDTEXT="[\\t\\x20-\\x21\\x23-\\x7E\\x80-\\xFF]|(?:#{LWS})"
25
- # QUOTED_TEXT = "\\\"((?:#{QDTEXT}|#{QUOTED_PAIR})*)\\\""
26
- # VALUE2 = "(#{TOKEN})|#{QUOTED_TEXT}"
27
-
28
- end
29
- BASE_HOSTNAME = /#{PATTERN::BASE_HOSTNAME}/
30
- BASE_PATH = /\A((?:[^\/?#]*\/)*)/
31
- IPADDR = /\A#{PATTERN::IPADDR}\Z/
32
- # HDN = /\A#{PATTERN::HOSTNAME}\Z/
33
- # TOKEN = /\A#{PATTERN::TOKEN}\Z/
34
- # TWO_DOT_DOMAINS = /\A\.(com|edu|net|mil|gov|int|org)\Z/
35
13
 
36
14
  # The mandatory name and value of the cookie
37
15
  attr_reader :name, :value
@@ -74,111 +52,29 @@ module CookieJar
74
52
  end
75
53
  end
76
54
 
77
- def expired?
55
+ def is_expired?
78
56
  expires_at != nil && Time.now > expires_at
79
57
  end
80
-
58
+
59
+ def is_session?
60
+ @expiry == nil
61
+ end
62
+
81
63
  # Create a cookie based on an absolute URI and the string value of a
82
64
  # 'Set-Cookie' header.
83
65
  def self.from_set_cookie request_uri, set_cookie_value
84
- args = { }
85
- params=set_cookie_value.split /;\s*/
86
- params.each do |param|
87
- result = PARAM1.match param
88
- if !result
89
- raise InvalidCookieError.new "Invalid cookie parameter in cookie '#{set_cookie_value}'"
90
- end
91
- key = result[1].downcase.to_sym
92
- keyvalue = result[2]
93
- case key
94
- when :expires
95
- args[:expires_at] = Time.parse keyvalue
96
- when :domain
97
- args[:domain] = keyvalue
98
- when :path
99
- args[:path] = keyvalue
100
- when :secure
101
- args[:secure] = true
102
- when :httponly
103
- args[:http_only] = true
104
- else
105
- args[:name] = result[1]
106
- args[:value] = keyvalue
107
- end
108
- end
109
- args[:domain] = determine_cookie_domain request_uri, args[:domain]
110
- args[:path] = determine_cookie_path request_uri, args[:path]
111
- args[:version] = 0
66
+ args = CookieJar::CookieValidation.parse_set_cookie set_cookie_value
67
+ args[:domain] = CookieJar::CookieValidation.determine_cookie_domain request_uri, args[:domain]
68
+ args[:path] = CookieJar::CookieValidation.determine_cookie_path request_uri, args[:path]
112
69
  cookie = Cookie.new args
113
- validate_cookie request_uri, cookie
70
+ CookieJar::CookieValidation.validate_cookie request_uri, cookie
114
71
  cookie
115
72
  end
116
73
 
117
74
  def to_s
118
- %^"#{name}=#{value}#{if domain then "; domain=#{domain}" end}#{if expiry then "; expiry=#{expiry}" end}#{if path then "; path=#{path}" end}#{if secure then "; secure" end }#{if http_only then "; HTTPOnly" end}^
75
+ "#{name}=#{value}"
119
76
  end
120
77
 
121
- # Check whether a cookie meets all of the rules to be created, based on
122
- # its internal settings and the URI it came from.
123
- #
124
- # returns true on success, but will raise an InvalidCookieError on failure
125
- # with an appropriate error message
126
- def self.validate_cookie request_uri, cookie
127
- uri = request_uri.is_a?(URI) ? request_uri : URI.parse(request_uri)
128
-
129
- request_host = effective_host uri.host
130
- request_path = uri.path
131
- request_secure = (uri.scheme == 'https')
132
- cookie_host = cookie.domain
133
- cookie_path = cookie.path
134
-
135
- errors = []
136
-
137
- # From RFC 2965, Section 3.3.2 Rejecting Cookies
138
-
139
- # A user agent rejects (SHALL NOT store its information) if the
140
- # Version attribute is missing. Note that the legacy Set-Cookie
141
- # directive will result in an implicit version 0.
142
- unless cookie.version
143
- errors << "Version missing"
144
- end
145
-
146
- # The value for the Path attribute is not a prefix of the request-URI
147
- unless request_path.start_with? cookie_path
148
- errors << "Path is not a prefix of the request uri path"
149
- end
150
-
151
- unless cookie_host =~ IPADDR || #is an IPv4 or IPv6 address
152
- cookie_host =~ /.\../ || #contains an embedded dot
153
- cookie_host == '.local' #is the domain cookie for local addresses
154
- errors << "Domain format is illegal"
155
- end
156
-
157
- # The effective host name that derives from the request-host does
158
- # not domain-match the Domain attribute.
159
- #
160
- # The request-host is a HDN (not IP address) and has the form HD,
161
- # where D is the value of the Domain attribute, and H is a string
162
- # that contains one or more dots.
163
- effective_host = effective_host uri
164
- unless domains_match effective_host, cookie_host
165
- errors << "Domain is inappropriate based on request URI hostname"
166
- end
167
-
168
- # The Port attribute has a "port-list", and the request-port was
169
- # not in the list.
170
- unless cookie.ports.nil? || cookie.ports.length != 0
171
- unless cookie.ports.find_index uri.port
172
- errors << "Ports list does not contain request URI port"
173
- end
174
- end
175
-
176
- raise InvalidCookieError.new errors unless errors.empty?
177
-
178
- # Note: 'secure' is not explicitly defined as an SSL channel, and no
179
- # test is defined around validity and the 'secure' attribute
180
- true
181
- end
182
78
  # Return true if (given a URI, a cookie object and other options) a cookie
183
79
  # should be sent to a host. Note that this currently ignores domain.
184
80
  #
@@ -190,127 +86,58 @@ module CookieJar
190
86
  path_match = uri.path.start_with? @path
191
87
  secure_match = !(@secure && uri.scheme == 'http')
192
88
  script_match = !(script && @http_only)
193
- expiry_match = !expired?
89
+ expiry_match = !is_expired?
194
90
  path_match && secure_match && script_match && expiry_match
195
91
  end
196
92
 
197
- # Compute the base of a path.
198
- def self.cookie_base_path path
199
- path = path.is_a?(URI) ? path.path : path
200
- BASE_PATH.match(path)[1]
201
- end
202
-
203
- # Processes cookie path data using the following rules:
204
- # Paths are separated by '/' characters, and accepted values are truncated
205
- # to the last '/' character. If no path is specified in the cookie, a path
206
- # value will be taken from the request URI which was used for the site.
207
- #
208
- # Note that this will not attempt to detect a mismatch of the request uri domain
209
- # and explicitly specified cookie path
210
- def self.determine_cookie_path request_uri, cookie_path
211
- uri = request_uri.is_a?(URI) ? request_uri : URI.parse(request_uri)
212
- cookie_path = cookie_path.is_a?(Cookie) ? cookie_path.path : cookie_path
213
-
214
- if cookie_path == nil || cookie_path.empty?
215
- cookie_path = cookie_base_path uri.path
216
- end
217
- cookie_path
218
- end
219
-
220
- # Given a URI, compute the relevant search domains for pre-existing
221
- # cookies. This includes all the valid dotted forms for a named or IP
222
- # domains.
223
- def self.compute_search_domains request_uri
224
- uri = request_uri.is_a?(URI) ? request_uri : URI.parse(request_uri)
225
- host = effective_host uri
226
- result = [host]
227
- if host !~ IPADDR
228
- result << ".#{host}"
93
+ def to_json *a
94
+ result = {
95
+ :json_class => self.class.name,
96
+ :name => @name,
97
+ :value => @value,
98
+ :domain => @domain,
99
+ :path => @path,
100
+ :created_at => @created_at
101
+ }
102
+ {
103
+ :expiry => @expiry,
104
+ :secure => (true if @secure),
105
+ :http_only => (true if @http_only),
106
+ :version => (@version if version != 0),
107
+ :comment => @comment,
108
+ :comment_url => @comment_url,
109
+ :discard => (true if @discard),
110
+ :ports => @ports
111
+ }.each do |name, value|
112
+ result[name] = value if value
229
113
  end
230
- base = hostname_reach host
231
- if base
232
- result << ".#{base}"
233
- end
234
- result
114
+ result.to_json(*a)
235
115
  end
236
-
237
- # Processes cookie domain data using the following rules:
238
- # Domains strings of the form .foo.com match 'foo.com' and all immediate
239
- # subdomains of 'foo.com'. Domain strings specified of the form 'foo.com' are
240
- # modified to '.foo.com', and as such will still apply to subdomains.
241
- #
242
- # Cookies without an explicit domain will have their domain value taken directly
243
- # from the URL, and will _NOT_ have any leading dot applied. For example, a request
244
- # to http://foo.com/ will cause an entry for 'foo.com' to be created - which applies
245
- # to foo.com but no subdomain.
246
- #
247
- # Note that this will not attempt to detect a mismatch of the request uri domain
248
- # and explicitly specified cookie domain
249
- def self.determine_cookie_domain request_uri, cookie_domain
250
- uri = request_uri.is_a?(URI) ? request_uri : URI.parse(request_uri)
251
- domain = cookie_domain.is_a?(Cookie) ? cookie_domain.domain : cookie_domain
252
-
253
- if domain == nil || domain.empty?
254
- domain = effective_host uri.host
255
- else
256
- domain = domain.downcase
257
- if domain =~ IPADDR || domain.start_with?('.')
258
- domain
259
- else
260
- ".#{domain}"
261
- end
116
+ def self.json_create o
117
+ params = o.inject({}) do |hash, (key, value)|
118
+ hash[key.to_sym] = value
119
+ hash
262
120
  end
263
- end
264
- # Compute the effective host (RFC 2965, section 1)
265
- # [host] a string or URI.
266
- #
267
- # Has the added additional logic of searching for interior dots specifically, and
268
- # matches colons to prevent .local being suffixed on IPv6 addresses
269
- def self.effective_host host
270
- hostname = host.is_a?(URI) ? host.host : host
271
- hostname = hostname.downcase
272
-
273
- if /.[\.:]./.match(hostname) || hostname == '.local'
274
- hostname
121
+ params[:version] ||= 0
122
+ params[:created_at] = Time.parse params[:created_at]
123
+ if params[:expiry].is_a? String
124
+ params[:expires_at] = Time.parse params[:expiry]
275
125
  else
276
- hostname + '.local'
126
+ params[:max_age] = params[:expiry]
277
127
  end
278
- end
279
- # Compare a base domain against the base domain to see if they match, or
280
- # if the base domain is reachable
281
- def self.domains_match tested_domain,base_domain
282
- return true if (tested_domain == base_domain || ".#{tested_domain}" == base_domain)
283
- lhs = effective_host tested_domain
284
- rhs = effective_host base_domain
285
- lhs == rhs || ".#{lhs}" == rhs || hostname_reach(lhs) == rhs || ".#{hostname_reach lhs}" == rhs
128
+ params.delete :expiry
129
+
130
+ self.new params
286
131
  end
287
- # Compute the reach of a hostname (RFC 2965, section 1)
288
- # Determines the next highest superdomain, or nil if none valid
289
- def self.hostname_reach hostname
290
- host = hostname.is_a?(URI) ? hostname.host : hostname
291
- host = host.downcase
292
- match = BASE_HOSTNAME.match host
293
- if match
294
- match[1]
295
- end
132
+ def self.compute_search_domains request_uri
133
+ CookieValidation.compute_search_domains request_uri
296
134
  end
297
135
  protected
298
- PARAM1 = /\A(#{PATTERN::TOKEN})(?:=#{PATTERN::VALUE1})?\Z/
299
- # PARAM2 = /\A(#{PATTERN::TOKEN})(?:=#{PATTERN::VALUE2})?\Z/
300
-
301
- def initialize *params
302
- case params.length
303
- when 1
304
- args = params[0]
305
- when 2
306
- args = {:name => params[0], :value => params[1], :version => 0}
307
- else
308
- raise ArgumentError.new "wrong number of arguments (expected 1 or 2)"
309
- end
136
+ def initialize args
310
137
 
311
- @created_at = Time.now
138
+ @created_at = args[:created_at] || Time.now
312
139
  @domain = args[:domain]
313
- @expiry = args[:max_age] || args[:expires_at] || nil
140
+ @expiry = args[:max_age] || args[:expires_at]
314
141
  @path = args[:path]
315
142
  @secure = args[:secure] || false
316
143
  @http_only = args[:http_only] || false
@@ -327,4 +154,4 @@ module CookieJar
327
154
  end
328
155
  end
329
156
  end
330
- end
157
+ end
@@ -0,0 +1,271 @@
1
+
2
+ module CookieJar
3
+ # Represents all cookie validation errors
4
+ class InvalidCookieError < StandardError
5
+ attr_reader :messages
6
+ def initialize message
7
+ if message.is_a? Array
8
+ @messages = message
9
+ message = message.join ', '
10
+ end
11
+ super(message)
12
+ end
13
+ end
14
+
15
+ # Contains logic to parse and validate cookie headers
16
+ module CookieValidation
17
+ module PATTERN
18
+ include URI::REGEXP::PATTERN
19
+
20
+ TOKEN = '[^(),\/<>@;:\\\"\[\]?={}\s]*'
21
+ VALUE1 = "([^;]*)"
22
+ IPADDR = "#{IPV4ADDR}|#{IPV6ADDR}"
23
+ BASE_HOSTNAME = "(?:#{DOMLABEL}\\.)(?:((?:(?:#{DOMLABEL}\\.)+(?:#{TOPLABEL}\\.?))|local))"
24
+
25
+ # QUOTED_PAIR = "\\\\[\\x00-\\x7F]"
26
+ # LWS = "\\r\\n(?:[ \\t]+)"
27
+ # TEXT="[\\t\\x20-\\x7E\\x80-\\xFF]|(?:#{LWS})"
28
+ # QDTEXT="[\\t\\x20-\\x21\\x23-\\x7E\\x80-\\xFF]|(?:#{LWS})"
29
+ # QUOTED_TEXT = "\\\"((?:#{QDTEXT}|#{QUOTED_PAIR})*)\\\""
30
+ # VALUE2 = "(#{TOKEN})|#{QUOTED_TEXT}"
31
+
32
+ end
33
+ BASE_HOSTNAME = /#{PATTERN::BASE_HOSTNAME}/
34
+ BASE_PATH = /\A((?:[^\/?#]*\/)*)/
35
+ IPADDR = /\A#{PATTERN::IPADDR}\Z/
36
+ HDN = /\A#{PATTERN::HOSTNAME}\Z/
37
+ TOKEN = /\A#{PATTERN::TOKEN}\Z/
38
+ PARAM1 = /\A(#{PATTERN::TOKEN})(?:=#{PATTERN::VALUE1})?\Z/
39
+ # PARAM2 = /\A(#{PATTERN::TOKEN})(?:=#{PATTERN::VALUE2})?\Z/
40
+
41
+ # TWO_DOT_DOMAINS = /\A\.(com|edu|net|mil|gov|int|org)\Z/
42
+
43
+ # Converts the input object to a URI (if not already a URI)
44
+ def self.to_uri request_uri
45
+ (request_uri.is_a? URI)? request_uri : (URI.parse request_uri)
46
+ end
47
+
48
+ # Converts an input cookie or uri to a string representing the path.
49
+ # Assume strings are already paths
50
+ def self.to_path uri_or_path
51
+ if (uri_or_path.is_a? URI) || (uri_or_path.is_a? Cookie)
52
+ uri_or_path.path
53
+ else
54
+ uri_or_path
55
+ end
56
+ end
57
+
58
+ # Converts an input cookie or uri to a string representing the domain.
59
+ # Assume strings are already domains
60
+ def self.to_domain uri_or_domain
61
+ if uri_or_domain.is_a? URI
62
+ uri_or_domain.host
63
+ elsif uri_or_domain.is_a? Cookie
64
+ uri_or_domain.domain
65
+ else
66
+ uri_or_domain
67
+ end
68
+ end
69
+
70
+ # Compare a tested domain against the base domain to see if they match, or
71
+ # if the base domain is reachable.
72
+ #
73
+ # returns the effective_host on success, nil on failure
74
+ def self.domains_match tested_domain, base_domain
75
+ base = effective_host base_domain
76
+ search_domains = compute_search_domains_for_host base
77
+ result = search_domains.find do |domain|
78
+ domain == tested_domain
79
+ end
80
+ end
81
+
82
+ # Compute the reach of a hostname (RFC 2965, section 1)
83
+ # Determines the next highest superdomain, or nil if none valid
84
+ def self.hostname_reach hostname
85
+ host = to_domain hostname
86
+ host = host.downcase
87
+ match = BASE_HOSTNAME.match host
88
+ if match
89
+ match[1]
90
+ end
91
+ end
92
+
93
+ # Compute the base of a path.
94
+ def self.cookie_base_path path
95
+ BASE_PATH.match(to_path path)[1]
96
+ end
97
+
98
+ # Processes cookie path data using the following rules:
99
+ # Paths are separated by '/' characters, and accepted values are truncated
100
+ # to the last '/' character. If no path is specified in the cookie, a path
101
+ # value will be taken from the request URI which was used for the site.
102
+ #
103
+ # Note that this will not attempt to detect a mismatch of the request uri domain
104
+ # and explicitly specified cookie path
105
+ def self.determine_cookie_path request_uri, cookie_path
106
+ uri = to_uri request_uri
107
+ cookie_path = to_path cookie_path
108
+
109
+ if cookie_path == nil || cookie_path.empty?
110
+ cookie_path = cookie_base_path uri.path
111
+ end
112
+ cookie_path
113
+ end
114
+
115
+ # Given a URI, compute the relevant search domains for pre-existing
116
+ # cookies. This includes all the valid dotted forms for a named or IP
117
+ # domains.
118
+ def self.compute_search_domains request_uri
119
+ uri = to_uri request_uri
120
+ host = uri.host
121
+ compute_search_domains_for_host host
122
+ end
123
+
124
+ # Given a host, compute the relevant search domains for pre-existing
125
+ # cookies
126
+ def self.compute_search_domains_for_host host
127
+ host = effective_host host
128
+ result = [host]
129
+ unless host =~ IPADDR
130
+ result << ".#{host}"
131
+ base = hostname_reach host
132
+ if base
133
+ result << ".#{base}"
134
+ end
135
+ end
136
+ result
137
+ end
138
+
139
+ # Processes cookie domain data using the following rules:
140
+ # Domains strings of the form .foo.com match 'foo.com' and all immediate
141
+ # subdomains of 'foo.com'. Domain strings specified of the form 'foo.com' are
142
+ # modified to '.foo.com', and as such will still apply to subdomains.
143
+ #
144
+ # Cookies without an explicit domain will have their domain value taken directly
145
+ # from the URL, and will _NOT_ have any leading dot applied. For example, a request
146
+ # to http://foo.com/ will cause an entry for 'foo.com' to be created - which applies
147
+ # to foo.com but no subdomain.
148
+ #
149
+ # Note that this will not attempt to detect a mismatch of the request uri domain
150
+ # and explicitly specified cookie domain
151
+ def self.determine_cookie_domain request_uri, cookie_domain
152
+ uri = to_uri request_uri
153
+ domain = to_domain cookie_domain
154
+
155
+ if domain == nil || domain.empty?
156
+ domain = effective_host uri.host
157
+ else
158
+ domain = domain.downcase
159
+ if domain =~ IPADDR || domain.start_with?('.')
160
+ domain
161
+ else
162
+ ".#{domain}"
163
+ end
164
+ end
165
+ end
166
+
167
+ # Compute the effective host (RFC 2965, section 1)
168
+ # [host] a string or URI.
169
+ #
170
+ # Has the added additional logic of searching for interior dots specifically, and
171
+ # matches colons to prevent .local being suffixed on IPv6 addresses
172
+ def self.effective_host host_or_uri
173
+ hostname = to_domain host_or_uri
174
+ hostname = hostname.downcase
175
+
176
+ if /.[\.:]./.match(hostname) || hostname == '.local'
177
+ hostname
178
+ else
179
+ hostname + '.local'
180
+ end
181
+ end
182
+ # Check whether a cookie meets all of the rules to be created, based on
183
+ # its internal settings and the URI it came from.
184
+ #
185
+ # returns true on success, but will raise an InvalidCookieError on failure
186
+ # with an appropriate error message
187
+ def self.validate_cookie request_uri, cookie
188
+ uri = to_uri request_uri
189
+ request_host = effective_host uri.host
190
+ request_path = uri.path
191
+ request_secure = (uri.scheme == 'https')
192
+ cookie_host = cookie.domain
193
+ cookie_path = cookie.path
194
+
195
+ errors = []
196
+
197
+ # From RFC 2965, Section 3.3.2 Rejecting Cookies
198
+
199
+ # A user agent rejects (SHALL NOT store its information) if the
200
+ # Version attribute is missing. Note that the legacy Set-Cookie
201
+ # directive will result in an implicit version 0.
202
+ unless cookie.version
203
+ errors << "Version missing"
204
+ end
205
+
206
+ # The value for the Path attribute is not a prefix of the request-URI
207
+ unless request_path.start_with? cookie_path
208
+ errors << "Path is not a prefix of the request uri path"
209
+ end
210
+
211
+ unless cookie_host =~ IPADDR || #is an IPv4 or IPv6 address
212
+ cookie_host =~ /.\../ || #contains an embedded dot
213
+ cookie_host == '.local' #is the domain cookie for local addresses
214
+ errors << "Domain format is illegal"
215
+ end
216
+
217
+ # The effective host name that derives from the request-host does
218
+ # not domain-match the Domain attribute.
219
+ #
220
+ # The request-host is a HDN (not IP address) and has the form HD,
221
+ # where D is the value of the Domain attribute, and H is a string
222
+ # that contains one or more dots.
223
+ unless domains_match cookie_host, uri
224
+ errors << "Domain is inappropriate based on request URI hostname"
225
+ end
226
+
227
+ # The Port attribute has a "port-list", and the request-port was
228
+ # not in the list.
229
+ unless cookie.ports.nil? || cookie.ports.length != 0
230
+ unless cookie.ports.find_index uri.port
231
+ errors << "Ports list does not contain request URI port"
232
+ end
233
+ end
234
+
235
+ raise InvalidCookieError.new errors unless errors.empty?
236
+
237
+ # Note: 'secure' is not explicitly defined as an SSL channel, and no
238
+ # test is defined around validity and the 'secure' attribute
239
+ true
240
+ end
241
+ def self.parse_set_cookie set_cookie_value
242
+ args = { }
243
+ params=set_cookie_value.split /;\s*/
244
+ params.each do |param|
245
+ result = PARAM1.match param
246
+ if !result
247
+ raise InvalidCookieError.new "Invalid cookie parameter in cookie '#{set_cookie_value}'"
248
+ end
249
+ key = result[1].downcase.to_sym
250
+ keyvalue = result[2]
251
+ case key
252
+ when :expires
253
+ args[:expires_at] = Time.parse keyvalue
254
+ when :domain
255
+ args[:domain] = keyvalue
256
+ when :path
257
+ args[:path] = keyvalue
258
+ when :secure
259
+ args[:secure] = true
260
+ when :httponly
261
+ args[:http_only] = true
262
+ else
263
+ args[:name] = result[1]
264
+ args[:value] = keyvalue
265
+ end
266
+ end
267
+ args[:version] = 0
268
+ args
269
+ end
270
+ end
271
+ end