diff --git a/lib/http/cookie.rb b/lib/http/cookie.rb index 3df776a..2fbab7d 100644 --- a/lib/http/cookie.rb +++ b/lib/http/cookie.rb @@ -25,7 +25,7 @@ class HTTP::Cookie UNIX_EPOCH = Time.at(0) PERSISTENT_PROPERTIES = %w[ - name value + name value raw_value domain for_domain path secure httponly expires max_age @@ -139,6 +139,7 @@ def initialize(*args) args.pop else self.name, self.value = args # value is set to nil + adjust_raw_value return end when 2..3 @@ -149,6 +150,7 @@ def initialize(*args) argc == 2 or raise ArgumentError, "wrong number of arguments (#{argc} for 1-3)" self.name, self.value = args + adjust_raw_value return end else @@ -204,6 +206,7 @@ def initialize(*args) self.origin = origin if origin self.max_age = max_age if max_age self.value = value.nil? && (@expires || @max_age) ? '' : value + adjust_raw_value end autoload :Scanner, 'http/cookie/scanner' @@ -380,6 +383,17 @@ def value= value @value = value end + def adjust_raw_value + if m = @value.match(/^"(.*)"$/) + @raw_value = @value + @value = m[1] + else + @raw_value = nil + end + end + + attr_accessor :raw_value + attr_reader :domain # See #domain. @@ -594,7 +608,8 @@ def valid_for_uri?(uri) # Returns a string for use in the Cookie header, i.e. `name=value` # or `name="value"`. def cookie_value - "#{@name}=#{Scanner.quote(@value)}" + v = ( @raw_value.nil? ? Scanner.quote(@value) : @raw_value ) + "#{@name}=#{v}" end alias to_s cookie_value diff --git a/lib/http/cookie/scanner.rb b/lib/http/cookie/scanner.rb index cb0d4e5..15a8bc3 100644 --- a/lib/http/cookie/scanner.rb +++ b/lib/http/cookie/scanner.rb @@ -55,10 +55,14 @@ def scan_value case when scan(/[^,;"]+/) s << matched - when skip(/"/) - # RFC 6265 2.2 - # A cookie-value may be DQUOTE'd. - s << scan_dquoted + when scan(/"/) + if s.length == 0 + # RFC 6265 2.2 + # A cookie-value may be DQUOTE'd. + s << '"' << scan_dquoted << '"' + else + s << matched + end when check(/;|#{RE_COOKIE_COMMA}/o) break else diff --git a/test/test_http_cookie.rb b/test/test_http_cookie.rb index c666f97..6e2af09 100644 --- a/test/test_http_cookie.rb +++ b/test/test_http_cookie.rb @@ -84,6 +84,7 @@ def test_parse_no_space assert_equal 1, HTTP::Cookie.parse(cookie_str, uri) { |cookie| assert_equal 'foo', cookie.name assert_equal 'bar', cookie.value + assert_equal nil, cookie.raw_value assert_equal '/', cookie.path assert_equal Time.at(1320539286), cookie.expires }.size @@ -111,6 +112,8 @@ def test_parse_quoted assert_equal 1, HTTP::Cookie.parse(cookie_str, uri) { |cookie| assert_equal 'quoted', cookie.name assert_equal 'value', cookie.value + assert_equal '"value"', cookie.raw_value + assert_equal 'quoted="value"', cookie.cookie_value }.size end @@ -430,7 +433,10 @@ def test_cookie_with_secure def test_cookie_value [ ['foo="bar baz"', 'bar baz'], + ['foo="bar baz"', '"bar baz"'], ['foo="bar\"; \"baz"', 'bar"; "baz'], + ['foo="bar\"; \"baz"', '"bar\"; \"baz"'], + ['foo="ba\"r baz"', '"ba\"r baz"'], ].each { |cookie_value, value| cookie = HTTP::Cookie.new('foo', value) assert_equal(cookie_value, cookie.cookie_value) @@ -453,8 +459,14 @@ def test_cookie_value assert_equal 3, hash.size + parsed_pairs = [ + ['Foo', 'value1'], + ['Bar', '"value 2"'], + ['Baz', 'value3'], + ] + hash.each_pair { |name, value| - _, pvalue = pairs.assoc(name) + _, pvalue = parsed_pairs.assoc(name) assert_equal pvalue, value } end @@ -1062,6 +1074,23 @@ def test_valid_for_uri? } end + def test_yaml_quotes + require 'yaml' + uri = URI.parse('http://localhost/') + cookie_str = 'foo="bar"; Path=/' + assert_equal 1, HTTP::Cookie.parse(cookie_str, uri) { |cookie| + assert_equal 'foo', cookie.name + assert_equal 'bar', cookie.value + assert_equal '"bar"', cookie.raw_value + + ycookie = YAML.load(cookie.to_yaml) + assert_equal 'bar', ycookie.value + assert_equal '"bar"', ycookie.raw_value + assert_equal cookie_str, ycookie.set_cookie_value + + }.size + end + def test_yaml_expires require 'yaml' cookie = HTTP::Cookie.new(cookie_values)