From f14c1786cd7a2eb7fcbb8736089791aa5058d451 Mon Sep 17 00:00:00 2001 From: Akinori MUSHA Date: Tue, 19 Mar 2013 21:42:35 +0900 Subject: [PATCH] Make expires and max_age affect each other and drop session=(). --- lib/http/cookie.rb | 69 ++++++++++++++++++++++++++++++---------- test/test_http_cookie.rb | 37 +++++++++++++++++++-- 2 files changed, 87 insertions(+), 19 deletions(-) diff --git a/lib/http/cookie.rb b/lib/http/cookie.rb index e32f827..0a6a498 100644 --- a/lib/http/cookie.rb +++ b/lib/http/cookie.rb @@ -55,10 +55,10 @@ class HTTP::Cookie attr_reader :name, :domain, :path, :origin attr_accessor :secure, :httponly, :value, :version - attr_reader :domain_name, :expires - attr_accessor :comment, :max_age + attr_reader :domain_name, :expires, :max_age + attr_accessor :comment - attr_accessor :session + attr_reader :session attr_accessor :created_at attr_accessor :accessed_at @@ -89,6 +89,7 @@ class HTTP::Cookie @secure = @httponly = @expires = @max_age = @comment = nil + @session = true @created_at = @accessed_at = Time.now case args.size @@ -157,6 +158,10 @@ class HTTP::Cookie # given origin is not allowed to issue is not included in the # resulted array. # + # Any Max-Age attribute value found is converted to an expires + # value computing from the current time so that expiration check + # (#expired?) can be performed. + # # If a block is given, each cookie object is passed to the block. # # Available option keywords are below: @@ -226,7 +231,10 @@ class HTTP::Cookie next unless value && !value.empty? cookie.path = value when 'expires' - next unless value && !value.empty? + # RFC 6265 4.1.2.2 + # The Max-Age attribute has precedence over the Expires + # attribute. + next unless value && !value.empty? && cookie.max_age.nil? begin cookie.expires = Time.parse(value) rescue @@ -260,11 +268,9 @@ class HTTP::Cookie cookie.secure ||= false cookie.httponly ||= false - # RFC 6265 4.1.2.2 - # The Max-Age attribute has precedence over the Expires - # attribute. - cookie.expires = date + cookie.max_age if cookie.max_age - cookie.session = !cookie.expires + # Have `expires` set instead of `max_age`, so that + # expiration check (`expired?`) can be performed. + cookie.expires = date + cookie.max_age if cookie.max_age if origin begin @@ -337,14 +343,43 @@ class HTTP::Cookie @origin = origin end - # Sets the expires attribute value. A `Time` object, a string - # representation of date/time, and `nil` are good values to set. + # Sets the Expires attribute value, accepting a `Time` object, a + # string representation of date/time, or `nil`. + # + # Note that max_age and expires are mutually exclusive. Setting + # `max_age` resets `expires` to nil, and vice versa. def expires=(t) case t when nil, Time - @expires = t else - @expires = Time.parse(t) + t = Time.parse(t) + end + @max_age = nil + @session = t.nil? + @expires = t + end + + attr_reader :max_age + + # Sets the Max-Age attribute, accepting an integer, or a string-like + # that represents an integer which will be stringified and then + # integerized using #to_i. + # + # Note that max_age and expires are mutually exclusive. Setting + # `max_age` resets `expires` to nil, and vice versa. + def max_age=(sec) + @expires = nil + case sec + when Integer, nil + else + str = check_string_type(sec) or + raise TypeError, "#{sec.class} is not an Integer or String" + sec = str.to_i + end + if @session = sec.nil? + @max_age = nil + else + @max_age = sec end end @@ -357,7 +392,7 @@ class HTTP::Cookie # Expires this cookie by setting the expires attribute value to a # past date. def expire - @expires = UNIX_EPOCH + self.expires = UNIX_EPOCH self end @@ -421,8 +456,10 @@ class HTTP::Cookie if (HTTP::Cookie.normalize_path(origin) + './').path != @path string << "; path=#{@path}" end - if @expires - string << "; expires=#{@expires.httpdate}" + if @max_age + string << "; Max-Age=#{@max_age}" + elsif @expires + string << "; Expires=#{@expires.httpdate}" end if @comment string << "; comment=#{@comment}" diff --git a/test/test_http_cookie.rb b/test/test_http_cookie.rb index 1f6e38b..5ff216b 100644 --- a/test/test_http_cookie.rb +++ b/test/test_http_cookie.rb @@ -364,13 +364,14 @@ class TestHTTPCookie < Test::Unit::TestCase def test_set_cookie_value url = URI.parse('http://rubyforge.org/') - cookie_params = @cookie_params.merge('secure' => 'secure') + cookie_params = @cookie_params.merge('secure' => 'secure', 'max-age' => 'Max-Age=1000') cookie_value = 'foo=bar' + date = Time.at(Time.now.to_i) cookie_params.keys.combine.each do |keys| cookie_text = [cookie_value, *keys.map { |key| cookie_params[key] }].join('; ') - cookie, = HTTP::Cookie.parse(cookie_text, :origin => url) - cookie2, = HTTP::Cookie.parse(cookie.set_cookie_value, :origin => url) + cookie, = HTTP::Cookie.parse(cookie_text, :origin => url, :date => date) + cookie2, = HTTP::Cookie.parse(cookie.set_cookie_value, :origin => url, :date => date) assert_equal(cookie.name, cookie2.name) assert_equal(cookie.value, cookie2.value) @@ -378,6 +379,13 @@ class TestHTTPCookie < Test::Unit::TestCase assert_equal(cookie.for_domain?, cookie2.for_domain?) assert_equal(cookie.path, cookie2.path) assert_equal(cookie.expires, cookie2.expires) + if keys.include?('max-age') + assert_equal(date + 1000, cookie2.expires) + elsif keys.include?('expires') + assert_equal(@expires, cookie2.expires) + else + assert_equal(nil, cookie2.expires) + end assert_equal(cookie.secure?, cookie2.secure?) assert_equal(cookie.httponly?, cookie2.httponly?) end @@ -470,6 +478,29 @@ class TestHTTPCookie < Test::Unit::TestCase assert_equal true, cookie.expired? end + def test_session + cookie = HTTP::Cookie.new(cookie_values) + + assert_equal false, cookie.session? + assert_equal nil, cookie.max_age + + cookie.expires = nil + assert_equal true, cookie.session? + assert_equal nil, cookie.max_age + + cookie.expires = Time.now + 3600 + assert_equal false, cookie.session? + assert_equal nil, cookie.max_age + + cookie.max_age = 3600 + assert_equal false, cookie.session? + assert_equal nil, cookie.expires + + cookie.max_age = nil + assert_equal true, cookie.session? + assert_equal nil, cookie.expires + end + def test_equal assert_not_equal(HTTP::Cookie.new(cookie_values), HTTP::Cookie.new(cookie_values(:value => 'bar')))