From e1857d76a46cc63746888cd28d77363cfc9905ca Mon Sep 17 00:00:00 2001 From: Akinori MUSHA Date: Fri, 12 Apr 2013 02:15:13 +0900 Subject: [PATCH] Add Cookie.cookie_value and Cookie.cookie_value_to_hash. --- lib/http/cookie.rb | 23 +++++++++++++++++++---- lib/http/cookie/scanner.rb | 33 +++++++++++++++++++++++++++++++-- test/test_http_cookie.rb | 22 ++++++++++++++++++++++ 3 files changed, 72 insertions(+), 6 deletions(-) diff --git a/lib/http/cookie.rb b/lib/http/cookie.rb index 269f1f9..9923e02 100644 --- a/lib/http/cookie.rb +++ b/lib/http/cookie.rb @@ -313,9 +313,7 @@ class HTTP::Cookie origin = URI(origin) [].tap { |cookies| - s = Scanner.new(set_cookie, logger) - until s.eos? - name, value, attrs = s.scan_cookie + Scanner.new(set_cookie, logger).scan_set_cookie { |name, value, attrs| break if name.nil? || name.empty? cookie = new(name, value) @@ -352,7 +350,24 @@ class HTTP::Cookie yield cookie if block_given? cookies << cookie - end + } + } + end + + # Takes an array of cookies and returns a string for use in the + # Cookie header, like "name1=value2; name2=value2". + def cookie_value(cookies) + cookies.join('; ') + end + + # Parses a Cookie header value into a hash of name-value string + # pairs. The first appearance takes precedence if multiple pairs + # with the same name occur. + def cookie_value_to_hash(cookie_value) + {}.tap { |hash| + Scanner.new(cookie_value).scan_cookie { |name, value| + hash[name] ||= value + } } end end diff --git a/lib/http/cookie/scanner.rb b/lib/http/cookie/scanner.rb index 8b0e4d9..05d8455 100644 --- a/lib/http/cookie/scanner.rb +++ b/lib/http/cookie/scanner.rb @@ -150,7 +150,14 @@ class HTTP::Cookie::Scanner < StringScanner tuple_to_time(day_of_month, month, year, time) end - def scan_cookie + def scan_set_cookie + unless block_given? + scan_set_cookie { |*values| + return values + } + return + end + # RFC 6265 4.1.1 & 5.2 until eos? start = pos @@ -211,7 +218,29 @@ class HTTP::Cookie::Scanner < StringScanner next end - return [name, value, attrs] if value + yield name, value, attrs if value + end + end + + def scan_cookie + unless block_given? + scan_cookie { |*values| + return values + } + return + end + + # RFC 6265 4.1.1 & 5.4 + until eos? + skip_wsp + + name, value = scan_name_value + + yield name, value if name && value + + # The comma is used as separator for concatenating multiple + # values of a header. + skip(/[;,]/) end end end diff --git a/test/test_http_cookie.rb b/test/test_http_cookie.rb index 53ad3eb..80eb0da 100644 --- a/test/test_http_cookie.rb +++ b/test/test_http_cookie.rb @@ -400,6 +400,28 @@ class TestHTTPCookie < Test::Unit::TestCase cookie = HTTP::Cookie.new('foo', value) assert_equal(cookie_value, cookie.cookie_value) } + + pairs = [ + ['Foo', 'value1'], + ['Bar', 'value 2'], + ['Baz', 'value3'], + ['Bar', 'value"4'], + ] + + cookie_value = HTTP::Cookie.cookie_value(pairs.map { |name, value| + HTTP::Cookie.new(:name => name, :value => value) + }) + + assert_equal 'Foo=value1; Bar="value 2"; Baz=value3; Bar="value\\"4"', cookie_value + + hash = HTTP::Cookie.cookie_value_to_hash(cookie_value) + + assert_equal 3, hash.size + + hash.each_pair { |name, value| + _, pvalue = pairs.assoc(name) + assert_equal pvalue, value + } end def test_set_cookie_value