mirror of
https://github.com/samsonjs/http-cookie.git
synced 2026-04-27 14:57:46 +00:00
Allow non-RFC 3986-compliant URLs
This commit is contained in:
parent
e76b7add4a
commit
052479536c
5 changed files with 115 additions and 15 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
# :markup: markdown
|
# :markup: markdown
|
||||||
require 'http/cookie/version'
|
require 'http/cookie/version'
|
||||||
|
require 'http/cookie/uri_parser'
|
||||||
require 'time'
|
require 'time'
|
||||||
require 'uri'
|
require 'uri'
|
||||||
require 'domain_name'
|
require 'domain_name'
|
||||||
|
|
@ -275,7 +276,7 @@ class HTTP::Cookie
|
||||||
logger = options[:logger]
|
logger = options[:logger]
|
||||||
created_at = options[:created_at]
|
created_at = options[:created_at]
|
||||||
end
|
end
|
||||||
origin = URI(origin)
|
origin = HTTP::Cookie::URIParser.instance.convert_to_uri(origin)
|
||||||
|
|
||||||
[].tap { |cookies|
|
[].tap { |cookies|
|
||||||
Scanner.new(set_cookie, logger).scan_set_cookie { |name, value, attrs|
|
Scanner.new(set_cookie, logger).scan_set_cookie { |name, value, attrs|
|
||||||
|
|
@ -455,7 +456,7 @@ class HTTP::Cookie
|
||||||
@origin.nil? or
|
@origin.nil? or
|
||||||
raise ArgumentError, "origin cannot be changed once it is set"
|
raise ArgumentError, "origin cannot be changed once it is set"
|
||||||
# Delay setting @origin because #domain= or #path= may fail
|
# Delay setting @origin because #domain= or #path= may fail
|
||||||
origin = URI(origin)
|
origin = HTTP::Cookie::URIParser.instance.convert_to_uri(origin)
|
||||||
if URI::HTTP === origin
|
if URI::HTTP === origin
|
||||||
self.domain ||= origin.host
|
self.domain ||= origin.host
|
||||||
self.path ||= (origin + './').path
|
self.path ||= (origin + './').path
|
||||||
|
|
@ -548,7 +549,7 @@ class HTTP::Cookie
|
||||||
# Tests if it is OK to accept this cookie if it is sent from a given
|
# Tests if it is OK to accept this cookie if it is sent from a given
|
||||||
# URI/URL, `uri`.
|
# URI/URL, `uri`.
|
||||||
def acceptable_from_uri?(uri)
|
def acceptable_from_uri?(uri)
|
||||||
uri = URI(uri)
|
uri = HTTP::Cookie::URIParser.instance.convert_to_uri(uri)
|
||||||
return false unless URI::HTTP === uri && uri.host
|
return false unless URI::HTTP === uri && uri.host
|
||||||
host = DomainName.new(uri.host)
|
host = DomainName.new(uri.host)
|
||||||
|
|
||||||
|
|
@ -585,7 +586,7 @@ class HTTP::Cookie
|
||||||
if @domain.nil?
|
if @domain.nil?
|
||||||
raise "cannot tell if this cookie is valid because the domain is unknown"
|
raise "cannot tell if this cookie is valid because the domain is unknown"
|
||||||
end
|
end
|
||||||
uri = URI(uri)
|
uri = HTTP::Cookie::URIParser.instance.convert_to_uri(uri)
|
||||||
# RFC 6265 5.4
|
# RFC 6265 5.4
|
||||||
return false if secure? && !(URI::HTTPS === uri)
|
return false if secure? && !(URI::HTTPS === uri)
|
||||||
acceptable_from_uri?(uri) && HTTP::Cookie.path_match?(@path, uri.path)
|
acceptable_from_uri?(uri) && HTTP::Cookie.path_match?(@path, uri.path)
|
||||||
|
|
|
||||||
49
lib/http/cookie/uri_parser.rb
Normal file
49
lib/http/cookie/uri_parser.rb
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
require 'singleton'
|
||||||
|
|
||||||
|
class HTTP::Cookie::URIParser
|
||||||
|
include Singleton
|
||||||
|
|
||||||
|
REGEXP = {
|
||||||
|
ABS_PATH: /\A[^?#]*\z/
|
||||||
|
}
|
||||||
|
|
||||||
|
def parse(uri)
|
||||||
|
m = /
|
||||||
|
\A
|
||||||
|
(?<scheme>https?)
|
||||||
|
:\/\/
|
||||||
|
((?<userinfo>.*)@)?
|
||||||
|
(?<host>[^\/]+)
|
||||||
|
(:(?<port>\d+))?
|
||||||
|
(?<path>[^?#]*)
|
||||||
|
(\?(?<query>[^#]*))?
|
||||||
|
(\#(?<fragment>.*))?
|
||||||
|
/xi.match(uri.to_s)
|
||||||
|
|
||||||
|
# Not an absolute HTTP/HTTPS URI
|
||||||
|
return URI::DEFAULT_PARSER.parse(uri) unless m
|
||||||
|
|
||||||
|
URI.for(
|
||||||
|
m['scheme'],
|
||||||
|
m['userinfo'],
|
||||||
|
m['host'],
|
||||||
|
m['port'],
|
||||||
|
nil, # registry
|
||||||
|
m['path'],
|
||||||
|
nil, # opaque
|
||||||
|
m['query'],
|
||||||
|
m['fragment'],
|
||||||
|
self
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def convert_to_uri(uri)
|
||||||
|
if uri.is_a?(URI::Generic)
|
||||||
|
uri
|
||||||
|
elsif uri = String.try_convert(uri)
|
||||||
|
parse(uri)
|
||||||
|
else
|
||||||
|
raise ArgumentError, "bad argument (expected URI object or URI string)"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -156,7 +156,7 @@ class HTTP::CookieJar
|
||||||
block_given? or return enum_for(__method__, uri)
|
block_given? or return enum_for(__method__, uri)
|
||||||
|
|
||||||
if uri
|
if uri
|
||||||
uri = URI(uri)
|
uri = HTTP::Cookie::URIParser.instance.convert_to_uri(uri)
|
||||||
return self unless URI::HTTP === uri && uri.host
|
return self unless URI::HTTP === uri && uri.host
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -302,7 +302,8 @@ class TestHTTPCookie < Test::Unit::TestCase
|
||||||
"name=Aaron; Domain=localhost; Expires=Sun, 06 Nov 2011 00:29:51 GMT; Path=/, " \
|
"name=Aaron; Domain=localhost; Expires=Sun, 06 Nov 2011 00:29:51 GMT; Path=/, " \
|
||||||
"name=Aaron; Domain=localhost; Expires=Sun, 06 Nov 2011 00:29:51 GMT; Path=/; HttpOnly, " \
|
"name=Aaron; Domain=localhost; Expires=Sun, 06 Nov 2011 00:29:51 GMT; Path=/; HttpOnly, " \
|
||||||
"expired=doh; Expires=Fri, 04 Nov 2011 00:29:51 GMT; Path=/, " \
|
"expired=doh; Expires=Fri, 04 Nov 2011 00:29:51 GMT; Path=/, " \
|
||||||
"a_path=some_path; Expires=Sun, 06 Nov 2011 00:29:51 GMT; Path=/some_path, " \
|
"a_path1=some_path; Expires=Sun, 06 Nov 2011 00:29:51 GMT; Path=/some_path, " \
|
||||||
|
"a_path2=some_path; Expires=Sun, 06 Nov 2011 00:29:51 GMT; Path=/some_path[], " \
|
||||||
"no_path1=no_path; Expires=Sun, 06 Nov 2011 00:29:52 GMT, no_expires=nope; Path=/, " \
|
"no_path1=no_path; Expires=Sun, 06 Nov 2011 00:29:52 GMT, no_expires=nope; Path=/, " \
|
||||||
"no_path2=no_path; Expires=Sun, 06 Nov 2011 00:29:52 GMT; no_expires=nope; Path, " \
|
"no_path2=no_path; Expires=Sun, 06 Nov 2011 00:29:52 GMT; no_expires=nope; Path, " \
|
||||||
"no_path3=no_path; Expires=Sun, 06 Nov 2011 00:29:52 GMT; no_expires=nope; Path=, " \
|
"no_path3=no_path; Expires=Sun, 06 Nov 2011 00:29:52 GMT; no_expires=nope; Path=, " \
|
||||||
|
|
@ -313,17 +314,22 @@ class TestHTTPCookie < Test::Unit::TestCase
|
||||||
"no_domain3=no_domain; Expires=Sun, 06 Nov 2011 00:29:53 GMT; no_expires=nope; Domain="
|
"no_domain3=no_domain; Expires=Sun, 06 Nov 2011 00:29:53 GMT; no_expires=nope; Domain="
|
||||||
|
|
||||||
cookies = HTTP::Cookie.parse cookie_str, url
|
cookies = HTTP::Cookie.parse cookie_str, url
|
||||||
assert_equal 15, cookies.length
|
assert_equal 16, cookies.length
|
||||||
|
|
||||||
name = cookies.find { |c| c.name == 'name' }
|
name = cookies.find { |c| c.name == 'name' }
|
||||||
assert_equal "Aaron", name.value
|
assert_equal "Aaron", name.value
|
||||||
assert_equal "/", name.path
|
assert_equal "/", name.path
|
||||||
assert_equal Time.at(1320539391), name.expires
|
assert_equal Time.at(1320539391), name.expires
|
||||||
|
|
||||||
a_path = cookies.find { |c| c.name == 'a_path' }
|
a_path1 = cookies.find { |c| c.name == 'a_path1' }
|
||||||
assert_equal "some_path", a_path.value
|
assert_equal "some_path", a_path1.value
|
||||||
assert_equal "/some_path", a_path.path
|
assert_equal "/some_path", a_path1.path
|
||||||
assert_equal Time.at(1320539391), a_path.expires
|
assert_equal Time.at(1320539391), a_path1.expires
|
||||||
|
|
||||||
|
a_path2 = cookies.find { |c| c.name == 'a_path2' }
|
||||||
|
assert_equal "some_path", a_path2.value
|
||||||
|
assert_equal "/some_path[]", a_path2.path
|
||||||
|
assert_equal Time.at(1320539391), a_path2.expires
|
||||||
|
|
||||||
no_expires = cookies.find { |c| c.name == 'no_expires' }
|
no_expires = cookies.find { |c| c.name == 'no_expires' }
|
||||||
assert_equal "nope", no_expires.value
|
assert_equal "nope", no_expires.value
|
||||||
|
|
@ -941,6 +947,10 @@ class TestHTTPCookie < Test::Unit::TestCase
|
||||||
cookie.origin = URI.parse('http://www.example.com/')
|
cookie.origin = URI.parse('http://www.example.com/')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cookie = HTTP::Cookie.new('a', 'b')
|
||||||
|
cookie.origin = HTTP::Cookie::URIParser.instance.parse('http://example.com/path[]/')
|
||||||
|
assert_equal '/path[]/', cookie.path
|
||||||
|
|
||||||
cookie = HTTP::Cookie.new('a', 'b', :domain => '.example.com')
|
cookie = HTTP::Cookie.new('a', 'b', :domain => '.example.com')
|
||||||
cookie.origin = URI.parse('http://example.org/')
|
cookie.origin = URI.parse('http://example.org/')
|
||||||
assert_equal false, cookie.acceptable?
|
assert_equal false, cookie.acceptable?
|
||||||
|
|
@ -1022,6 +1032,18 @@ class TestHTTPCookie < Test::Unit::TestCase
|
||||||
'file:///dir2/test.html',
|
'file:///dir2/test.html',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
HTTP::Cookie.parse('a4=b; domain=example.com; path=/dir2[]/',
|
||||||
|
HTTP::Cookie::URIParser.instance.parse('http://example.com/dir[]/file.html')).first => {
|
||||||
|
true => [
|
||||||
|
HTTP::Cookie::URIParser.instance.parse('https://example.com/dir2[]/file.html'),
|
||||||
|
HTTP::Cookie::URIParser.instance.parse('http://example.com/dir2[]/file.html'),
|
||||||
|
],
|
||||||
|
false => [
|
||||||
|
HTTP::Cookie::URIParser.instance.parse('https://example.com/dir[]/file.html'),
|
||||||
|
HTTP::Cookie::URIParser.instance.parse('http://example.com/dir[]/file.html'),
|
||||||
|
'file:///dir2/test.html',
|
||||||
|
]
|
||||||
|
},
|
||||||
HTTP::Cookie.parse('a4=b; secure',
|
HTTP::Cookie.parse('a4=b; secure',
|
||||||
URI('https://example.com/dir/file.html')).first => {
|
URI('https://example.com/dir/file.html')).first => {
|
||||||
true => [
|
true => [
|
||||||
|
|
@ -1069,7 +1091,7 @@ class TestHTTPCookie < Test::Unit::TestCase
|
||||||
hash.each { |expected, urls|
|
hash.each { |expected, urls|
|
||||||
urls.each { |url|
|
urls.each { |url|
|
||||||
assert_equal expected, cookie.valid_for_uri?(url), '%s: %s' % [cookie.name, url]
|
assert_equal expected, cookie.valid_for_uri?(url), '%s: %s' % [cookie.name, url]
|
||||||
assert_equal expected, cookie.valid_for_uri?(URI(url)), "%s: URI(%s)" % [cookie.name, url]
|
assert_equal expected, cookie.valid_for_uri?(HTTP::Cookie::URIParser.instance.parse(url)), "%s: URI(%s)" % [cookie.name, url]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -465,7 +465,7 @@ module TestHTTPCookieJar
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_save_and_read_cookiestxt
|
def test_save_and_read_cookiestxt
|
||||||
url = URI 'http://rubyforge.org/foo/'
|
url = HTTP::Cookie::URIParser.instance.convert_to_uri('https://rubyforge.org/foo[]/')
|
||||||
|
|
||||||
# Add one cookie with an expiration date in the future
|
# Add one cookie with an expiration date in the future
|
||||||
cookie = HTTP::Cookie.new(cookie_values)
|
cookie = HTTP::Cookie.new(cookie_values)
|
||||||
|
|
@ -474,7 +474,7 @@ module TestHTTPCookieJar
|
||||||
:expires => nil))
|
:expires => nil))
|
||||||
cookie2 = HTTP::Cookie.new(cookie_values(:name => 'Baz',
|
cookie2 = HTTP::Cookie.new(cookie_values(:name => 'Baz',
|
||||||
:value => 'Foo#Baz',
|
:value => 'Foo#Baz',
|
||||||
:path => '/foo/',
|
:path => '/foo[]/',
|
||||||
:for_domain => false))
|
:for_domain => false))
|
||||||
h_cookie = HTTP::Cookie.new(cookie_values(:name => 'Quux',
|
h_cookie = HTTP::Cookie.new(cookie_values(:name => 'Quux',
|
||||||
:value => 'Foo#Quux',
|
:value => 'Foo#Quux',
|
||||||
|
|
@ -523,7 +523,7 @@ module TestHTTPCookieJar
|
||||||
assert_equal 'Foo#Baz', cookie.value
|
assert_equal 'Foo#Baz', cookie.value
|
||||||
assert_equal 'rubyforge.org', cookie.domain
|
assert_equal 'rubyforge.org', cookie.domain
|
||||||
assert_equal false, cookie.for_domain
|
assert_equal false, cookie.for_domain
|
||||||
assert_equal '/foo/', cookie.path
|
assert_equal '/foo[]/', cookie.path
|
||||||
assert_equal false, cookie.httponly?
|
assert_equal false, cookie.httponly?
|
||||||
when 'Quux'
|
when 'Quux'
|
||||||
assert_equal 'Foo#Quux', cookie.value
|
assert_equal 'Foo#Quux', cookie.value
|
||||||
|
|
@ -656,6 +656,34 @@ module TestHTTPCookieJar
|
||||||
assert_equal(0, @jar.cookies(url).length)
|
assert_equal(0, @jar.cookies(url).length)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_non_rfc3986_compliant_paths
|
||||||
|
url = HTTP::Cookie::URIParser.instance.convert_to_uri('http://RubyForge.org/login[]')
|
||||||
|
|
||||||
|
values = cookie_values(:path => "/login[]", :expires => nil, :origin => url)
|
||||||
|
|
||||||
|
# Add one cookie with an expiration date in the future
|
||||||
|
cookie = HTTP::Cookie.new(values)
|
||||||
|
@jar.add(cookie)
|
||||||
|
assert_equal(1, @jar.cookies(url).length)
|
||||||
|
|
||||||
|
# Add a second cookie
|
||||||
|
@jar.add(HTTP::Cookie.new(values.merge( :name => 'Baz' )))
|
||||||
|
assert_equal(2, @jar.cookies(url).length)
|
||||||
|
|
||||||
|
# Make sure we don't get the cookie in a different path
|
||||||
|
assert_equal(0, @jar.cookies(HTTP::Cookie::URIParser.instance.convert_to_uri('http://RubyForge.org/hello[]')).length)
|
||||||
|
assert_equal(0, @jar.cookies(HTTP::Cookie::URIParser.instance.convert_to_uri('http://RubyForge.org/')).length)
|
||||||
|
|
||||||
|
# Expire the first cookie
|
||||||
|
@jar.add(HTTP::Cookie.new(values.merge( :expires => Time.now - (10 * 86400))))
|
||||||
|
assert_equal(1, @jar.cookies(url).length)
|
||||||
|
|
||||||
|
# Expire the second cookie
|
||||||
|
@jar.add(HTTP::Cookie.new(values.merge( :name => 'Baz',
|
||||||
|
:expires => Time.now - (10 * 86400))))
|
||||||
|
assert_equal(0, @jar.cookies(url).length)
|
||||||
|
end
|
||||||
|
|
||||||
def test_ssl_cookies
|
def test_ssl_cookies
|
||||||
# thanks to michal "ocher" ochman for reporting the bug responsible for this test.
|
# thanks to michal "ocher" ochman for reporting the bug responsible for this test.
|
||||||
values = cookie_values(:expires => nil)
|
values = cookie_values(:expires => nil)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue