pax_global_header 0000666 0000000 0000000 00000000064 13413645020 0014510 g ustar 00root root 0000000 0000000 52 comment=2daa161d7eed62a61c7270fc8e547e56acb0ea18
ruby-http-cookie-1.0.3/ 0000775 0000000 0000000 00000000000 13413645020 0014716 5 ustar 00root root 0000000 0000000 ruby-http-cookie-1.0.3/.gitignore 0000664 0000000 0000000 00000000232 13413645020 0016703 0 ustar 00root root 0000000 0000000 *.gem
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
ruby-http-cookie-1.0.3/.travis.yml 0000664 0000000 0000000 00000000410 13413645020 0017022 0 ustar 00root root 0000000 0000000 sudo: false
language: ruby
cache: bundler
rvm:
- 1.8.7
- ree
- 1.9.3
- 2.0.0
- 2.1
- 2.2
- 2.3.0
- ruby-head
- jruby-1.7
- jruby-9
- rbx-2
matrix:
allow_failures:
- rvm: ruby-head
- rvm: rbx-2
before_install:
- gem update bundler
ruby-http-cookie-1.0.3/CHANGELOG.md 0000664 0000000 0000000 00000001171 13413645020 0016527 0 ustar 00root root 0000000 0000000 ## 1.0.3 (2016-09-30)
- Treat comma as normal character in HTTP::Cookie.cookie_value_to_hash
instead of key-value pair separator. This should fix the problem
described in CVE-2016-7401.
## 1.0.2 (2013-09-10)
- Fix HTTP::Cookie.parse so that it does not raise ArgumentError
when it finds a bad name or value that is parsable but considered
invalid.
## 1.0.1 (2013-04-21)
- Minor error handling improvements and documentation updates.
- Argument error regarding specifying store/saver classes no longer
raises IndexError, but either ArgumentError or TypeError.
## 1.0.0 (2013-04-17)
- Initial Release.
ruby-http-cookie-1.0.3/Gemfile 0000664 0000000 0000000 00000000140 13413645020 0016204 0 ustar 00root root 0000000 0000000 source 'https://rubygems.org'
# Specify your gem's dependencies in http-cookie.gemspec
gemspec
ruby-http-cookie-1.0.3/LICENSE.txt 0000664 0000000 0000000 00000002227 13413645020 0016544 0 ustar 00root root 0000000 0000000 Copyright (c) 2013 Akinori MUSHA
Copyright (c) 2011-2012 Akinori MUSHA, Eric Hodel
Copyright (c) 2006-2011 Aaron Patterson, Mike Dalessio
MIT License
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
ruby-http-cookie-1.0.3/README.md 0000664 0000000 0000000 00000015604 13413645020 0016203 0 ustar 00root root 0000000 0000000 # HTTP::Cookie
HTTP::Cookie is a ruby library to handle HTTP cookies in a way both
compliant with RFCs and compatible with today's major browsers.
It was originally a part of the
[Mechanize](https://github.com/sparklemotion/mechanize) library,
separated as an independent library in the hope of serving as a common
component that is reusable from any HTTP related piece of software.
The following is an incomplete list of its features:
* Its behavior is highly compatible with that of today's major web
browsers.
* It is based on and conforms to RFC 6265 (the latest standard for the
HTTP cookie mechanism) to a high extent, with real world conventions
deeply in mind.
* It takes eTLD (effective TLD, also known as "Public Suffix") into
account just as major browsers do, to reject cookies with an eTLD
domain like "org", "co.jp", or "appspot.com". This feature is
brought to you by the domain_name gem.
* The number of cookies and the size are properly capped so that a
cookie store does not get flooded.
* It supports the legacy Netscape cookies.txt format for
serialization, maximizing the interoperability with other
implementations.
* It supports the cookies.sqlite format adopted by Mozilla Firefox for
backend store database which can be shared among multiple program
instances.
* It is relatively easy to add a new serialization format or a backend
store because of its modular API.
## Installation
Add this line to your application's `Gemfile`:
gem 'http-cookie'
And then execute:
$ bundle
Or install it yourself as:
$ gem install http-cookie
## Usage
########################
# Client side example 1
########################
# Initialize a cookie jar
jar = HTTP::CookieJar.new
# Load from a file
jar.load(filename) if File.exist?(filename)
# Store received cookies, where uri is the origin of this header
header["Set-Cookie"].each { |value|
jar.parse(value, uri)
}
# ...
# Set the Cookie header value, where uri is the destination URI
header["Cookie"] = HTTP::Cookie.cookie_value(jar.cookies(uri))
# Save to a file
jar.save(filename)
########################
# Client side example 2
########################
# Initialize a cookie jar using a Mozilla compatible SQLite3 backend
jar = HTTP::CookieJar.new(store: :mozilla, filename: 'cookies.sqlite')
# There is no need for load & save in this backend.
# Store received cookies, where uri is the origin of this header
header["Set-Cookie"].each { |value|
jar.parse(value, uri)
}
# ...
# Set the Cookie header value, where uri is the destination URI
header["Cookie"] = HTTP::Cookie.cookie_value(jar.cookies(uri))
########################
# Server side example
########################
# Generate a domain cookie
cookie1 = HTTP::Cookie.new("uid", "u12345", domain: 'example.org',
for_domain: true,
path: '/',
max_age: 7*86400)
# Add it to the Set-Cookie response header
header['Set-Cookie'] = cookie1.set_cookie_value
# Generate a host-only cookie
cookie2 = HTTP::Cookie.new("aid", "a12345", origin: my_url,
path: '/',
max_age: 7*86400)
# Add it to the Set-Cookie response header
header['Set-Cookie'] = cookie2.set_cookie_value
## Incompatibilities with Mechanize::Cookie/CookieJar
There are several incompatibilities between
Mechanize::Cookie/CookieJar and HTTP::Cookie/CookieJar. Below
is how to rewrite existing code written for Mechanize::Cookie with
equivalent using HTTP::Cookie:
- Mechanize::Cookie.parse
The parameter order changed in HTTP::Cookie.parse.
# before
cookies1 = Mechanize::Cookie.parse(uri, set_cookie1)
cookies2 = Mechanize::Cookie.parse(uri, set_cookie2, log)
# after
cookies1 = HTTP::Cookie.parse(set_cookie1, uri_or_url)
cookies2 = HTTP::Cookie.parse(set_cookie2, uri_or_url, logger: log)
# or you can directly store parsed cookies in your jar
jar.parse(set_cookie1, uri_or_url)
jar.parse(set_cookie1, uri_or_url, logger: log)
- Mechanize::Cookie#version, #version=
There is no longer a sense of version in the HTTP cookie
specification. The only version number ever defined was zero, and
there will be no other version defined since the version attribute
has been removed in RFC 6265.
- Mechanize::Cookie#comment, #comment=
Ditto. The comment attribute has been removed in RFC 6265.
- Mechanize::Cookie#set_domain
This method was unintentionally made public. Simply use
HTTP::Cookie#domain=.
# before
cookie.set_domain(domain)
# after
cookie.domain = domain
- Mechanize::CookieJar#add, #add!
Always use HTTP::CookieJar#add.
# before
jar.add!(cookie1)
jar.add(uri, cookie2)
# after
jar.add(cookie1)
cookie2.origin = uri; jar.add(cookie2) # or specify origin in parse() or new()
- Mechanize::CookieJar#clear!
Use HTTP::Cookiejar#clear.
# before
jar.clear!
# after
jar.clear
- Mechanize::CookieJar#save_as
Use HTTP::CookieJar#save.
# before
jar.save_as(file)
# after
jar.save(file)
- Mechanize::CookieJar#jar
There is no direct access to the internal hash in HTTP::CookieJar
since it has introduced an abstract store layer. If you want to
tweak the internals of the hash store, try creating a new store
class referring to the default store class
HTTP::CookieJar::HashStore.
If you desperately need it you can access it by
`jar.store.instance_variable_get(:@jar)`, but there is no
guarantee that it will remain available in the future.
HTTP::Cookie/CookieJar raise runtime errors to help migration, so
after replacing the class names, try running your test code once to
find out how to fix your code base.
### File formats
The YAML serialization format has changed, and HTTP::CookieJar#load
cannot import what is written in a YAML file saved by
Mechanize::CookieJar#save_as. HTTP::CookieJar#load will not raise an
exception if an incompatible YAML file is given, but the content is
silently ignored.
Note that there is (obviously) no forward compatibillity with this.
Trying to load a YAML file saved by HTTP::CookieJar with
Mechanize::CookieJar will fail in runtime error.
On the other hand, there has been (and will ever be) no change in the
cookies.txt format, so use it instead if compatibility is significant.
## Contributing
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
ruby-http-cookie-1.0.3/Rakefile 0000664 0000000 0000000 00000001005 13413645020 0016357 0 ustar 00root root 0000000 0000000 require 'bundler/gem_tasks'
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
test.ruby_opts << '-r./test/simplecov_start.rb' if RUBY_VERSION >= '1.9'
test.pattern = 'test/**/test_*.rb'
test.verbose = true
end
task :default => :test
require 'rdoc/task'
Rake::RDocTask.new do |rdoc|
version = HTTP::Cookie::VERSION
rdoc.rdoc_dir = 'rdoc'
rdoc.title = "http-cookie #{version}"
rdoc.rdoc_files.include('lib/**/*.rb')
rdoc.rdoc_files.include(Bundler::GemHelper.gemspec.extra_rdoc_files)
end
ruby-http-cookie-1.0.3/http-cookie.gemspec 0000664 0000000 0000000 00000003546 13413645020 0020521 0 ustar 00root root 0000000 0000000 # -*- encoding: utf-8 -*-
lib = File.expand_path('../lib', __FILE__)
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
require 'http/cookie/version'
Gem::Specification.new do |gem|
gem.name = "http-cookie"
gem.version = HTTP::Cookie::VERSION
gem.authors, gem.email = {
'Akinori MUSHA' => 'knu@idaemons.org',
'Aaron Patterson' => 'aaronp@rubyforge.org',
'Eric Hodel' => 'drbrain@segment7.net',
'Mike Dalessio' => 'mike.dalessio@gmail.com',
}.instance_eval { [keys, values] }
gem.description = %q{HTTP::Cookie is a Ruby library to handle HTTP Cookies based on RFC 6265. It has with security, standards compliance and compatibility in mind, to behave just the same as today's major web browsers. It has builtin support for the legacy cookies.txt and the latest cookies.sqlite formats of Mozilla Firefox, and its modular API makes it easy to add support for a new backend store.}
gem.summary = %q{A Ruby library to handle HTTP Cookies based on RFC 6265}
gem.homepage = "https://github.com/sparklemotion/http-cookie"
gem.license = "MIT"
gem.files = `git ls-files`.split($/)
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
gem.require_paths = ["lib"]
gem.extra_rdoc_files = ['README.md', 'LICENSE.txt']
gem.add_runtime_dependency("domain_name", ["~> 0.5"])
gem.add_development_dependency("sqlite3", ["~> 1.3.3"]) unless defined?(JRUBY_VERSION)
gem.add_development_dependency("bundler", [">= 1.2.0"])
gem.add_development_dependency("test-unit", [">= 2.4.3", *("< 3" if RUBY_VERSION < "1.9")])
gem.add_development_dependency("rake", [">= 0.9.2.2", *("< 11" if RUBY_VERSION < "1.9")])
gem.add_development_dependency("rdoc", ["> 2.4.2"])
gem.add_development_dependency("simplecov", [">= 0"])
end
ruby-http-cookie-1.0.3/lib/ 0000775 0000000 0000000 00000000000 13413645020 0015464 5 ustar 00root root 0000000 0000000 ruby-http-cookie-1.0.3/lib/http-cookie.rb 0000664 0000000 0000000 00000000026 13413645020 0020235 0 ustar 00root root 0000000 0000000 require 'http/cookie'
ruby-http-cookie-1.0.3/lib/http/ 0000775 0000000 0000000 00000000000 13413645020 0016443 5 ustar 00root root 0000000 0000000 ruby-http-cookie-1.0.3/lib/http/cookie.rb 0000664 0000000 0000000 00000046465 13413645020 0020260 0 ustar 00root root 0000000 0000000 # :markup: markdown
require 'http/cookie/version'
require 'time'
require 'uri'
require 'domain_name'
require 'http/cookie/ruby_compat'
module HTTP
autoload :CookieJar, 'http/cookie_jar'
end
# This class is used to represent an HTTP Cookie.
class HTTP::Cookie
# Maximum number of bytes per cookie (RFC 6265 6.1 requires 4096 at
# least)
MAX_LENGTH = 4096
# Maximum number of cookies per domain (RFC 6265 6.1 requires 50 at
# least)
MAX_COOKIES_PER_DOMAIN = 50
# Maximum number of cookies total (RFC 6265 6.1 requires 3000 at
# least)
MAX_COOKIES_TOTAL = 3000
# :stopdoc:
UNIX_EPOCH = Time.at(0)
PERSISTENT_PROPERTIES = %w[
name value
domain for_domain path
secure httponly
expires max_age
created_at accessed_at
]
# :startdoc:
# The cookie name. It may not be nil or empty.
#
# Assign a string containing any of the following characters will
# raise ArgumentError: control characters (`\x00-\x1F` and `\x7F`),
# space and separators `,;\"=`.
#
# Note that RFC 6265 4.1.1 lists more characters disallowed for use
# in a cookie name, which are these: `<>@:/[]?{}`. Using these
# characters will reduce interoperability.
#
# :attr_accessor: name
# The cookie value.
#
# Assign a string containing a control character (`\x00-\x1F` and
# `\x7F`) will raise ArgumentError.
#
# Assigning nil sets the value to an empty string and the expiration
# date to the Unix epoch. This is a handy way to make a cookie for
# expiration.
#
# Note that RFC 6265 4.1.1 lists more characters disallowed for use
# in a cookie value, which are these: ` ",;\`. Using these
# characters will reduce interoperability.
#
# :attr_accessor: value
# The cookie domain.
#
# Setting a domain with a leading dot implies that the #for_domain
# flag should be turned on. The setter accepts a DomainName object
# as well as a string-like.
#
# :attr_accessor: domain
# The path attribute value.
#
# The setter treats an empty path ("") as the root path ("/").
#
# :attr_accessor: path
# The origin of the cookie.
#
# Setting this will initialize the #domain and #path attribute
# values if unknown yet. If the cookie already has a domain value
# set, it is checked against the origin URL to see if the origin is
# allowed to issue a cookie of the domain, and ArgumentError is
# raised if the check fails.
#
# :attr_accessor: origin
# The Expires attribute value as a Time object.
#
# The setter method accepts a Time object, a string representation
# of date/time that Time.parse can understand, or `nil`.
#
# Setting this value resets #max_age to nil. When #max_age is
# non-nil, #expires returns `created_at + max_age`.
#
# :attr_accessor: expires
# The Max-Age attribute value as an integer, the number of seconds
# before expiration.
#
# The setter method accepts an integer, or a string-like that
# represents an integer which will be stringified and then
# integerized using #to_i.
#
# This value is reset to nil when #expires= is called.
#
# :attr_accessor: max_age
# :call-seq:
# new(name, value = nil)
# new(name, value = nil, **attr_hash)
# new(**attr_hash)
#
# Creates a cookie object. For each key of `attr_hash`, the setter
# is called if defined and any error (typically ArgumentError or
# TypeError) that is raised will be passed through. Each key can be
# either a downcased symbol or a string that may be mixed case.
# Support for the latter may, however, be obsoleted in future when
# Ruby 2.0's keyword syntax is adopted.
#
# If `value` is omitted or it is nil, an expiration cookie is
# created unless `max_age` or `expires` (`expires_at`) is given.
#
# e.g.
#
# new("uid", "a12345")
# new("uid", "a12345", :domain => 'example.org',
# :for_domain => true, :expired => Time.now + 7*86400)
# new("name" => "uid", "value" => "a12345", "Domain" => 'www.example.org')
#
def initialize(*args)
@origin = @domain = @path =
@expires = @max_age = nil
@for_domain = @secure = @httponly = false
@session = true
@created_at = @accessed_at = Time.now
case argc = args.size
when 1
if attr_hash = Hash.try_convert(args.last)
args.pop
else
self.name, self.value = args # value is set to nil
return
end
when 2..3
if attr_hash = Hash.try_convert(args.last)
args.pop
self.name, value = args
else
argc == 2 or
raise ArgumentError, "wrong number of arguments (#{argc} for 1-3)"
self.name, self.value = args
return
end
else
raise ArgumentError, "wrong number of arguments (#{argc} for 1-3)"
end
for_domain = false
domain = max_age = origin = nil
attr_hash.each_pair { |okey, val|
case key ||= okey
when :name
self.name = val
when :value
value = val
when :domain
domain = val
when :path
self.path = val
when :origin
origin = val
when :for_domain, :for_domain?
for_domain = val
when :max_age
# Let max_age take precedence over expires
max_age = val
when :expires, :expires_at
self.expires = val unless max_age
when :httponly, :httponly?
@httponly = val
when :secure, :secure?
@secure = val
when Symbol
setter = :"#{key}="
if respond_to?(setter)
__send__(setter, val)
else
warn "unknown attribute name: #{okey.inspect}" if $VERBOSE
next
end
when String
warn "use downcased symbol for keyword: #{okey.inspect}" if $VERBOSE
key = key.downcase.to_sym
redo
else
warn "invalid keyword ignored: #{okey.inspect}" if $VERBOSE
next
end
}
if @name.nil?
raise ArgumentError, "name must be specified"
end
@for_domain = for_domain
self.domain = domain if domain
self.origin = origin if origin
self.max_age = max_age if max_age
self.value = value.nil? && (@expires || @max_age) ? '' : value
end
autoload :Scanner, 'http/cookie/scanner'
class << self
# Tests if +target_path+ is under +base_path+ as described in RFC
# 6265 5.1.4. +base_path+ must be an absolute path.
# +target_path+ may be empty, in which case it is treated as the
# root path.
#
# e.g.
#
# path_match?('/admin/', '/admin/index') == true
# path_match?('/admin/', '/Admin/index') == false
# path_match?('/admin/', '/admin/') == true
# path_match?('/admin/', '/admin') == false
#
# path_match?('/admin', '/admin') == true
# path_match?('/admin', '/Admin') == false
# path_match?('/admin', '/admins') == false
# path_match?('/admin', '/admin/') == true
# path_match?('/admin', '/admin/index') == true
def path_match?(base_path, target_path)
base_path.start_with?('/') or return false
# RFC 6265 5.1.4
bsize = base_path.size
tsize = target_path.size
return bsize == 1 if tsize == 0 # treat empty target_path as "/"
return false unless target_path.start_with?(base_path)
return true if bsize == tsize || base_path.end_with?('/')
target_path[bsize] == ?/
end
# Parses a Set-Cookie header value `set_cookie` assuming that it
# is sent from a source URI/URL `origin`, and returns an array of
# Cookie objects. Parts (separated by commas) that are malformed
# or considered unacceptable are silently ignored.
#
# If a block is given, each cookie object is passed to the block.
#
# Available option keywords are below:
#
# :created_at
# : The creation time of the cookies parsed.
#
# :logger
# : Logger object useful for debugging
#
# ### Compatibility Note for Mechanize::Cookie users
#
# * Order of parameters changed in HTTP::Cookie.parse:
#
# Mechanize::Cookie.parse(uri, set_cookie[, log])
#
# HTTP::Cookie.parse(set_cookie, uri[, :logger => # log])
#
# * HTTP::Cookie.parse does not accept nil for `set_cookie`.
#
# * HTTP::Cookie.parse does not yield nil nor include nil in an
# returned array. It simply ignores unparsable parts.
#
# * HTTP::Cookie.parse is made to follow RFC 6265 to the extent
# not terribly breaking interoperability with broken
# implementations. In particular, it is capable of parsing
# cookie definitions containing double-quotes just as naturally
# expected.
def parse(set_cookie, origin, options = nil, &block)
if options
logger = options[:logger]
created_at = options[:created_at]
end
origin = URI(origin)
[].tap { |cookies|
Scanner.new(set_cookie, logger).scan_set_cookie { |name, value, attrs|
break if name.nil? || name.empty?
begin
cookie = new(name, value)
rescue => e
logger.warn("Invalid name or value: #{e}") if logger
next
end
cookie.created_at = created_at if created_at
attrs.each { |aname, avalue|
begin
case aname
when 'domain'
cookie.for_domain = true
# The following may negate @for_domain if the value is
# an eTLD or IP address, hence this order.
cookie.domain = avalue
when 'path'
cookie.path = avalue
when 'expires'
# RFC 6265 4.1.2.2
# The Max-Age attribute has precedence over the Expires
# attribute.
cookie.expires = avalue unless cookie.max_age
when 'max-age'
cookie.max_age = avalue
when 'secure'
cookie.secure = avalue
when 'httponly'
cookie.httponly = avalue
end
rescue => e
logger.warn("Couldn't parse #{aname} '#{avalue}': #{e}") if logger
end
}
cookie.origin = origin
cookie.acceptable? or next
yield cookie if block_given?
cookies << cookie
}
}
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
attr_reader :name
# See #name.
def name= name
name = (String.try_convert(name) or
raise TypeError, "#{name.class} is not a String")
if name.empty?
raise ArgumentError, "cookie name cannot be empty"
elsif name.match(/[\x00-\x20\x7F,;\\"=]/)
raise ArgumentError, "invalid cookie name"
end
# RFC 6265 4.1.1
# cookie-name may not match:
# /[\x00-\x20\x7F()<>@,;:\\"\/\[\]?={}]/
@name = name
end
attr_reader :value
# See #value.
def value= value
if value.nil?
self.expires = UNIX_EPOCH
return @value = ''
end
value = (String.try_convert(value) or
raise TypeError, "#{value.class} is not a String")
if value.match(/[\x00-\x1F\x7F]/)
raise ArgumentError, "invalid cookie value"
end
# RFC 6265 4.1.1
# cookie-name may not match:
# /[^\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]/
@value = value
end
attr_reader :domain
# See #domain.
def domain= domain
case domain
when nil
@for_domain = false
if @origin
@domain_name = DomainName.new(@origin.host)
@domain = @domain_name.hostname
else
@domain_name = @domain = nil
end
return nil
when DomainName
@domain_name = domain
else
domain = (String.try_convert(domain) or
raise TypeError, "#{domain.class} is not a String")
if domain.start_with?('.')
for_domain = true
domain = domain[1..-1]
end
if domain.empty?
return self.domain = nil
end
# Do we really need to support this?
if domain.match(/\A([^:]+):[0-9]+\z/)
domain = $1
end
@domain_name = DomainName.new(domain)
end
# RFC 6265 5.3 5.
if domain_name.domain.nil? # a public suffix or IP address
@for_domain = false
else
@for_domain = for_domain unless for_domain.nil?
end
@domain = @domain_name.hostname
end
# Returns the domain, with a dot prefixed only if the domain flag is
# on.
def dot_domain
@for_domain ? '.' << @domain : @domain
end
# Returns the domain attribute value as a DomainName object.
attr_reader :domain_name
# The domain flag. (the opposite of host-only-flag)
#
# If this flag is true, this cookie will be sent to any host in the
# \#domain, including the host domain itself. If it is false, this
# cookie will be sent only to the host indicated by the #domain.
attr_accessor :for_domain
alias for_domain? for_domain
attr_reader :path
# See #path.
def path= path
path = (String.try_convert(path) or
raise TypeError, "#{path.class} is not a String")
@path = path.start_with?('/') ? path : '/'
end
attr_reader :origin
# See #origin.
def origin= origin
return origin if origin == @origin
@origin.nil? or
raise ArgumentError, "origin cannot be changed once it is set"
# Delay setting @origin because #domain= or #path= may fail
origin = URI(origin)
if URI::HTTP === origin
self.domain ||= origin.host
self.path ||= (origin + './').path
end
@origin = origin
end
# The secure flag. (secure-only-flag)
#
# A cookie with this flag on should only be sent via a secure
# protocol like HTTPS.
attr_accessor :secure
alias secure? secure
# The HttpOnly flag. (http-only-flag)
#
# A cookie with this flag on should be hidden from a client script.
attr_accessor :httponly
alias httponly? httponly
# The session flag. (the opposite of persistent-flag)
#
# A cookie with this flag on should be hidden from a client script.
attr_reader :session
alias session? session
def expires
@expires or @created_at && @max_age ? @created_at + @max_age : nil
end
# See #expires.
def expires= t
case t
when nil, Time
else
t = Time.parse(t)
end
@max_age = nil
@session = t.nil?
@expires = t
end
alias expires_at expires
alias expires_at= expires=
attr_reader :max_age
# See #max_age.
def max_age= sec
case sec
when Integer, nil
else
str = String.try_convert(sec) or
raise TypeError, "#{sec.class} is not an Integer or String"
/\A-?\d+\z/.match(str) or
raise ArgumentError, "invalid Max-Age: #{sec.inspect}"
sec = str.to_i
end
@expires = nil
if @session = sec.nil?
@max_age = nil
else
@max_age = sec
end
end
# Tests if this cookie is expired by now, or by a given time.
def expired?(time = Time.now)
if expires = self.expires
expires <= time
else
false
end
end
# Expires this cookie by setting the expires attribute value to a
# past date.
def expire!
self.expires = UNIX_EPOCH
self
end
# The time this cookie was created at. This value is used as a base
# date for interpreting the Max-Age attribute value. See #expires.
attr_accessor :created_at
# The time this cookie was last accessed at.
attr_accessor :accessed_at
# Tests if it is OK to accept this cookie if it is sent from a given
# URI/URL, `uri`.
def acceptable_from_uri?(uri)
uri = URI(uri)
return false unless URI::HTTP === uri && uri.host
host = DomainName.new(uri.host)
# RFC 6265 5.3
case
when host.hostname == @domain
true
when @for_domain # !host-only-flag
host.cookie_domain?(@domain_name)
else
@domain.nil?
end
end
# Tests if it is OK to accept this cookie considering its origin.
# If either domain or path is missing, raises ArgumentError. If
# origin is missing, returns true.
def acceptable?
case
when @domain.nil?
raise "domain is missing"
when @path.nil?
raise "path is missing"
when @origin.nil?
true
else
acceptable_from_uri?(@origin)
end
end
# Tests if it is OK to send this cookie to a given `uri`. A
# RuntimeError is raised if the cookie's domain is unknown.
def valid_for_uri?(uri)
if @domain.nil?
raise "cannot tell if this cookie is valid because the domain is unknown"
end
uri = URI(uri)
# RFC 6265 5.4
return false if secure? && !(URI::HTTPS === uri)
acceptable_from_uri?(uri) && HTTP::Cookie.path_match?(@path, uri.path)
end
# Returns a string for use in the Cookie header, i.e. `name=value`
# or `name="value"`.
def cookie_value
"#{@name}=#{Scanner.quote(@value)}"
end
alias to_s cookie_value
# Returns a string for use in the Set-Cookie header. If necessary
# information like a path or domain (when `for_domain` is set) is
# missing, RuntimeError is raised. It is always the best to set an
# origin before calling this method.
def set_cookie_value
string = cookie_value
if @for_domain
if @domain
string << "; Domain=#{@domain}"
else
raise "for_domain is specified but domain is unknown"
end
end
if @path
if !@origin || (@origin + './').path != @path
string << "; Path=#{@path}"
end
else
raise "path is unknown"
end
if @max_age
string << "; Max-Age=#{@max_age}"
elsif @expires
string << "; Expires=#{@expires.httpdate}"
end
if @httponly
string << "; HttpOnly"
end
if @secure
string << "; Secure"
end
string
end
def inspect
'#<%s:' % self.class << PERSISTENT_PROPERTIES.map { |key|
'%s=%s' % [key, instance_variable_get(:"@#{key}").inspect]
}.join(', ') << ' origin=%s>' % [@origin ? @origin.to_s : 'nil']
end
# Compares the cookie with another. When there are many cookies with
# the same name for a URL, the value of the smallest must be used.
def <=> other
# RFC 6265 5.4
# Precedence: 1. longer path 2. older creation
(@name <=> other.name).nonzero? ||
(other.path.length <=> @path.length).nonzero? ||
(@created_at <=> other.created_at).nonzero? ||
@value <=> other.value
end
include Comparable
# YAML serialization helper for Syck.
def to_yaml_properties
PERSISTENT_PROPERTIES.map { |name| "@#{name}" }
end
# YAML serialization helper for Psych.
def encode_with(coder)
PERSISTENT_PROPERTIES.each { |key|
coder[key.to_s] = instance_variable_get(:"@#{key}")
}
end
# YAML deserialization helper for Syck.
def init_with(coder)
yaml_initialize(coder.tag, coder.map)
end
# YAML deserialization helper for Psych.
def yaml_initialize(tag, map)
expires = nil
@origin = nil
map.each { |key, value|
case key
when 'expires'
# avoid clobbering max_age
expires = value
when *PERSISTENT_PROPERTIES
__send__(:"#{key}=", value)
end
}
self.expires = expires if self.max_age.nil?
end
end
ruby-http-cookie-1.0.3/lib/http/cookie/ 0000775 0000000 0000000 00000000000 13413645020 0017714 5 ustar 00root root 0000000 0000000 ruby-http-cookie-1.0.3/lib/http/cookie/ruby_compat.rb 0000664 0000000 0000000 00000002355 13413645020 0022572 0 ustar 00root root 0000000 0000000 class Array
def select! # :yield: x
i = 0
each_with_index { |x, j|
yield x or next
self[i] = x if i != j
i += 1
}
return nil if i == size
self[i..-1] = []
self
end unless method_defined?(:select!)
def sort_by!(&block) # :yield: x
replace(sort_by(&block))
end unless method_defined?(:sort_by!)
end
class Hash
class << self
def try_convert(object)
if object.is_a?(Hash) ||
(object.respond_to?(:to_hash) && (object = object.to_hash).is_a?(Hash))
object
else
nil
end
end unless method_defined?(:try_convert)
end
end
class String
class << self
def try_convert(object)
if object.is_a?(String) ||
(object.respond_to?(:to_str) && (object = object.to_str).is_a?(String))
object
else
nil
end
end unless method_defined?(:try_convert)
end
end
# In Ruby < 1.9.3 URI() does not accept a URI object.
if RUBY_VERSION < "1.9.3"
require 'uri'
begin
URI(URI(''))
rescue
def URI(url) # :nodoc:
case url
when URI
url
when String
URI.parse(url)
else
raise ArgumentError, 'bad argument (expected URI object or URI string)'
end
end
end
end
ruby-http-cookie-1.0.3/lib/http/cookie/scanner.rb 0000664 0000000 0000000 00000012425 13413645020 0021676 0 ustar 00root root 0000000 0000000 require 'http/cookie'
require 'strscan'
require 'time'
class HTTP::Cookie::Scanner < StringScanner
# Whitespace.
RE_WSP = /[ \t]+/
# A pattern that matches a cookie name or attribute name which may
# be empty, capturing trailing whitespace.
RE_NAME = /(?!#{RE_WSP})[^,;\\"=]*/
RE_BAD_CHAR = /([\x00-\x20\x7F",;\\])/
# A pattern that matches the comma in a (typically date) value.
RE_COOKIE_COMMA = /,(?=#{RE_WSP}?#{RE_NAME}=)/
def initialize(string, logger = nil)
@logger = logger
super(string)
end
class << self
def quote(s)
return s unless s.match(RE_BAD_CHAR)
'"' << s.gsub(/([\\"])/, "\\\\\\1") << '"'
end
end
def skip_wsp
skip(RE_WSP)
end
def scan_dquoted
''.tap { |s|
case
when skip(/"/)
break
when skip(/\\/)
s << getch
when scan(/[^"\\]+/)
s << matched
end until eos?
}
end
def scan_name
scan(RE_NAME).tap { |s|
s.rstrip! if s
}
end
def scan_value(comma_as_separator = false)
''.tap { |s|
case
when scan(/[^,;"]+/)
s << matched
when skip(/"/)
# RFC 6265 2.2
# A cookie-value may be DQUOTE'd.
s << scan_dquoted
when check(/;/)
break
when comma_as_separator && check(RE_COOKIE_COMMA)
break
else
s << getch
end until eos?
s.rstrip!
}
end
def scan_name_value(comma_as_separator = false)
name = scan_name
if skip(/\=/)
value = scan_value(comma_as_separator)
else
scan_value(comma_as_separator)
value = nil
end
[name, value]
end
if Time.respond_to?(:strptime)
def tuple_to_time(day_of_month, month, year, time)
Time.strptime(
'%02d %s %04d %02d:%02d:%02d UTC' % [day_of_month, month, year, *time],
'%d %b %Y %T %Z'
).tap { |date|
date.day == day_of_month or return nil
}
end
else
def tuple_to_time(day_of_month, month, year, time)
Time.parse(
'%02d %s %04d %02d:%02d:%02d UTC' % [day_of_month, month, year, *time]
).tap { |date|
date.day == day_of_month or return nil
}
end
end
private :tuple_to_time
def parse_cookie_date(s)
# RFC 6265 5.1.1
time = day_of_month = month = year = nil
s.split(/[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]+/).each { |token|
case
when time.nil? && token.match(/\A(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?(?=\D|\z)/)
sec =
if $3
$3.to_i
else
# violation of the RFC
@logger.warn("Time lacks the second part: #{token}") if @logger
0
end
time = [$1.to_i, $2.to_i, sec]
when day_of_month.nil? && token.match(/\A(\d{1,2})(?=\D|\z)/)
day_of_month = $1.to_i
when month.nil? && token.match(/\A(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)/i)
month = $1.capitalize
when year.nil? && token.match(/\A(\d{2,4})(?=\D|\z)/)
year = $1.to_i
end
}
if day_of_month.nil? || month.nil? || year.nil? || time.nil?
return nil
end
case day_of_month
when 1..31
else
return nil
end
case year
when 100..1600
return nil
when 70..99
year += 1900
when 0..69
year += 2000
end
hh, mm, ss = time
if hh > 23 || mm > 59 || ss > 59
return nil
end
tuple_to_time(day_of_month, month, year, time)
end
def scan_set_cookie
# RFC 6265 4.1.1 & 5.2
until eos?
start = pos
len = nil
skip_wsp
name, value = scan_name_value(true)
if value.nil?
@logger.warn("Cookie definition lacks a name-value pair.") if @logger
elsif name.empty?
@logger.warn("Cookie definition has an empty name.") if @logger
value = nil
end
attrs = {}
case
when skip(/,/)
# The comma is used as separator for concatenating multiple
# values of a header.
len = (pos - 1) - start
break
when skip(/;/)
skip_wsp
aname, avalue = scan_name_value(true)
next if aname.empty? || value.nil?
aname.downcase!
case aname
when 'expires'
# RFC 6265 5.2.1
avalue &&= parse_cookie_date(avalue) or next
when 'max-age'
# RFC 6265 5.2.2
next unless /\A-?\d+\z/.match(avalue)
when 'domain'
# RFC 6265 5.2.3
# An empty value SHOULD be ignored.
next if avalue.nil? || avalue.empty?
when 'path'
# RFC 6265 5.2.4
# A relative path must be ignored rather than normalizing it
# to "/".
next unless /\A\//.match(avalue)
when 'secure', 'httponly'
# RFC 6265 5.2.5, 5.2.6
avalue = true
end
attrs[aname] = avalue
end until eos?
len ||= pos - start
if len > HTTP::Cookie::MAX_LENGTH
@logger.warn("Cookie definition too long: #{name}") if @logger
next
end
yield name, value, attrs if value
end
end
def scan_cookie
# RFC 6265 4.1.1 & 5.4
until eos?
skip_wsp
# Do not treat comma in a Cookie header value as separator; see CVE-2016-7401
name, value = scan_name_value(false)
yield name, value if value
skip(/;/)
end
end
end
ruby-http-cookie-1.0.3/lib/http/cookie/version.rb 0000664 0000000 0000000 00000000073 13413645020 0021726 0 ustar 00root root 0000000 0000000 module HTTP
class Cookie
VERSION = "1.0.3"
end
end
ruby-http-cookie-1.0.3/lib/http/cookie_jar.rb 0000664 0000000 0000000 00000023123 13413645020 0021076 0 ustar 00root root 0000000 0000000 # :markup: markdown
require 'http/cookie'
##
# This class is used to manage the Cookies that have been returned from
# any particular website.
class HTTP::CookieJar
class << self
def const_missing(name)
case name.to_s
when /\A([A-Za-z]+)Store\z/
file = 'http/cookie_jar/%s_store' % $1.downcase
when /\A([A-Za-z]+)Saver\z/
file = 'http/cookie_jar/%s_saver' % $1.downcase
end
begin
require file
rescue LoadError
raise NameError, 'can\'t resolve constant %s; failed to load %s' % [name, file]
end
if const_defined?(name)
const_get(name)
else
raise NameError, 'can\'t resolve constant %s after loading %s' % [name, file]
end
end
end
attr_reader :store
def get_impl(base, value, *args)
case value
when base
value
when Symbol
begin
base.implementation(value).new(*args)
rescue IndexError => e
raise ArgumentError, e.message
end
when Class
if base >= value
value.new(*args)
else
raise TypeError, 'not a subclass of %s: %s' % [base, value]
end
else
raise TypeError, 'invalid object: %s' % value.inspect
end
end
private :get_impl
# Generates a new cookie jar.
#
# Available option keywords are as below:
#
# :store
# : The store class that backs this jar. (default: `:hash`)
# A symbol addressing a store class, a store class, or an instance
# of a store class is accepted. Symbols are mapped to store
# classes, like `:hash` to HTTP::CookieJar::HashStore and `:mozilla`
# to HTTP::CookieJar::MozillaStore.
#
# Any options given are passed through to the initializer of the
# specified store class. For example, the `:mozilla`
# (HTTP::CookieJar::MozillaStore) store class requires a `:filename`
# option. See individual store classes for details.
def initialize(options = nil)
opthash = {
:store => :hash,
}
opthash.update(options) if options
@store = get_impl(AbstractStore, opthash[:store], opthash)
end
# The copy constructor. Not all backend store classes support cloning.
def initialize_copy(other)
@store = other.instance_eval { @store.dup }
end
# Adds a cookie to the jar if it is acceptable, and returns self in
# any case. A given cookie must have domain and path attributes
# set, or ArgumentError is raised.
#
# Whether a cookie with the `for_domain` flag on overwrites another
# with the flag off or vice versa depends on the store used. See
# individual store classes for that matter.
#
# ### Compatibility Note for Mechanize::Cookie users
#
# In HTTP::Cookie, each cookie object can store its origin URI
# (cf. #origin). While the origin URI of a cookie can be set
# manually by #origin=, one is typically given in its generation.
# To be more specific, HTTP::Cookie.new takes an `:origin` option
# and HTTP::Cookie.parse takes one via the second argument.
#
# # Mechanize::Cookie
# jar.add(origin, cookie)
# jar.add!(cookie) # no acceptance check is performed
#
# # HTTP::Cookie
# jar.origin = origin
# jar.add(cookie) # acceptance check is performed
def add(cookie)
@store.add(cookie) if
begin
cookie.acceptable?
rescue RuntimeError => e
raise ArgumentError, e.message
end
self
end
alias << add
# Deletes a cookie that has the same name, domain and path as a
# given cookie from the jar and returns self.
#
# How the `for_domain` flag value affects the set of deleted cookies
# depends on the store used. See individual store classes for that
# matter.
def delete(cookie)
@store.delete(cookie)
self
end
# Gets an array of cookies sorted by the path and creation time. If
# `url` is given, only ones that should be sent to the URL/URI are
# selected, with the access time of each of them updated.
def cookies(url = nil)
each(url).sort
end
# Tests if the jar is empty. If `url` is given, tests if there is
# no cookie for the URL.
def empty?(url = nil)
if url
each(url) { return false }
return true
else
@store.empty?
end
end
# Iterates over all cookies that are not expired in no particular
# order.
#
# An optional argument `uri` specifies a URI/URL indicating the
# destination of the cookies being selected. Every cookie yielded
# should be good to send to the given URI,
# i.e. cookie.valid_for_uri?(uri) evaluates to true.
#
# If (and only if) the `uri` option is given, last access time of
# each cookie is updated to the current time.
def each(uri = nil, &block) # :yield: cookie
block_given? or return enum_for(__method__, uri)
if uri
uri = URI(uri)
return self unless URI::HTTP === uri && uri.host
end
@store.each(uri, &block)
self
end
include Enumerable
# Parses a Set-Cookie field value `set_cookie` assuming that it is
# sent from a source URL/URI `origin`, and adds the cookies parsed
# as valid and considered acceptable to the jar. Returns an array
# of cookies that have been added.
#
# If a block is given, it is called for each cookie and the cookie
# is added only if the block returns a true value.
#
# `jar.parse(set_cookie, origin)` is a shorthand for this:
#
# HTTP::Cookie.parse(set_cookie, origin) { |cookie|
# jar.add(cookie)
# }
#
# See HTTP::Cookie.parse for available options.
def parse(set_cookie, origin, options = nil) # :yield: cookie
if block_given?
HTTP::Cookie.parse(set_cookie, origin, options).tap { |cookies|
cookies.select! { |cookie|
yield(cookie) && add(cookie)
}
}
else
HTTP::Cookie.parse(set_cookie, origin, options) { |cookie|
add(cookie)
}
end
end
# call-seq:
# jar.save(filename_or_io, **options)
# jar.save(filename_or_io, format = :yaml, **options)
#
# Saves the cookie jar into a file or an IO in the format specified
# and returns self. If a given object responds to #write it is
# taken as an IO, or taken as a filename otherwise.
#
# Available option keywords are below:
#
# * `:format`
#
# Specifies the format for saving. A saver class, a symbol
# addressing a saver class, or a pre-generated instance of a
# saver class is accepted.
#
#
# - :yaml
# - YAML structure (default)
# - :cookiestxt
# - Mozilla's cookies.txt format
#
#
# * `:session`
#
#
# - true
# - Save session cookies as well.
# - false
# - Do not save session cookies. (default)
#
#
# All options given are passed through to the underlying cookie
# saver module's constructor.
def save(writable, *options)
opthash = {
:format => :yaml,
:session => false,
}
case options.size
when 0
when 1
case options = options.first
when Symbol
opthash[:format] = options
else
if hash = Hash.try_convert(options)
opthash.update(hash)
end
end
when 2
opthash[:format], options = options
if hash = Hash.try_convert(options)
opthash.update(hash)
end
else
raise ArgumentError, 'wrong number of arguments (%d for 1-3)' % (1 + options.size)
end
saver = get_impl(AbstractSaver, opthash[:format], opthash)
if writable.respond_to?(:write)
saver.save(writable, self)
else
File.open(writable, 'w') { |io|
saver.save(io, self)
}
end
self
end
# call-seq:
# jar.load(filename_or_io, **options)
# jar.load(filename_or_io, format = :yaml, **options)
#
# Loads cookies recorded in a file or an IO in the format specified
# into the jar and returns self. If a given object responds to
# \#read it is taken as an IO, or taken as a filename otherwise.
#
# Available option keywords are below:
#
# * `:format`
#
# Specifies the format for loading. A saver class, a symbol
# addressing a saver class, or a pre-generated instance of a
# saver class is accepted.
#
#
# - :yaml
# - YAML structure (default)
# - :cookiestxt
# - Mozilla's cookies.txt format
#
#
# All options given are passed through to the underlying cookie
# saver module's constructor.
def load(readable, *options)
opthash = {
:format => :yaml,
:session => false,
}
case options.size
when 0
when 1
case options = options.first
when Symbol
opthash[:format] = options
else
if hash = Hash.try_convert(options)
opthash.update(hash)
end
end
when 2
opthash[:format], options = options
if hash = Hash.try_convert(options)
opthash.update(hash)
end
else
raise ArgumentError, 'wrong number of arguments (%d for 1-3)' % (1 + options.size)
end
saver = get_impl(AbstractSaver, opthash[:format], opthash)
if readable.respond_to?(:write)
saver.load(readable, self)
else
File.open(readable, 'r') { |io|
saver.load(io, self)
}
end
self
end
# Clears the cookie jar and returns self.
def clear
@store.clear
self
end
# Removes expired cookies and returns self. If `session` is true,
# all session cookies are removed as well.
def cleanup(session = false)
@store.cleanup session
self
end
end
ruby-http-cookie-1.0.3/lib/http/cookie_jar/ 0000775 0000000 0000000 00000000000 13413645020 0020550 5 ustar 00root root 0000000 0000000 ruby-http-cookie-1.0.3/lib/http/cookie_jar/abstract_saver.rb 0000664 0000000 0000000 00000003207 13413645020 0024102 0 ustar 00root root 0000000 0000000 # :markup: markdown
# An abstract superclass for all saver classes.
class HTTP::CookieJar::AbstractSaver
class << self
@@class_map = {}
# Gets an implementation class by the name, optionally trying to
# load "http/cookie_jar/*_saver" if not found. If loading fails,
# IndexError is raised.
def implementation(symbol)
@@class_map.fetch(symbol)
rescue IndexError
begin
require 'http/cookie_jar/%s_saver' % symbol
@@class_map.fetch(symbol)
rescue LoadError, IndexError
raise IndexError, 'cookie saver unavailable: %s' % symbol.inspect
end
end
def inherited(subclass) # :nodoc:
@@class_map[class_to_symbol(subclass)] = subclass
end
def class_to_symbol(klass) # :nodoc:
klass.name[/[^:]+?(?=Saver$|$)/].downcase.to_sym
end
end
# Defines options and their default values.
def default_options
# {}
end
private :default_options
# :call-seq:
# new(**options)
#
# Called by the constructor of each subclass using super().
def initialize(options = nil)
options ||= {}
@logger = options[:logger]
@session = options[:session]
# Initializes each instance variable of the same name as option
# keyword.
default_options.each_pair { |key, default|
instance_variable_set("@#{key}", options.fetch(key, default))
}
end
# Implements HTTP::CookieJar#save().
#
# This is an abstract method that each subclass must override.
def save(io, jar)
# self
end
# Implements HTTP::CookieJar#load().
#
# This is an abstract method that each subclass must override.
def load(io, jar)
# self
end
end
ruby-http-cookie-1.0.3/lib/http/cookie_jar/abstract_store.rb 0000664 0000000 0000000 00000006052 13413645020 0024117 0 ustar 00root root 0000000 0000000 # :markup: markdown
require 'monitor'
# An abstract superclass for all store classes.
class HTTP::CookieJar::AbstractStore
include MonitorMixin
class << self
@@class_map = {}
# Gets an implementation class by the name, optionally trying to
# load "http/cookie_jar/*_store" if not found. If loading fails,
# IndexError is raised.
def implementation(symbol)
@@class_map.fetch(symbol)
rescue IndexError
begin
require 'http/cookie_jar/%s_store' % symbol
@@class_map.fetch(symbol)
rescue LoadError, IndexError
raise IndexError, 'cookie store unavailable: %s' % symbol.inspect
end
end
def inherited(subclass) # :nodoc:
@@class_map[class_to_symbol(subclass)] = subclass
end
def class_to_symbol(klass) # :nodoc:
klass.name[/[^:]+?(?=Store$|$)/].downcase.to_sym
end
end
# Defines options and their default values.
def default_options
# {}
end
private :default_options
# :call-seq:
# new(**options)
#
# Called by the constructor of each subclass using super().
def initialize(options = nil)
super() # MonitorMixin
options ||= {}
@logger = options[:logger]
# Initializes each instance variable of the same name as option
# keyword.
default_options.each_pair { |key, default|
instance_variable_set("@#{key}", options.fetch(key, default))
}
end
# This is an abstract method that each subclass must override.
def initialize_copy(other)
# self
end
# Implements HTTP::CookieJar#add().
#
# This is an abstract method that each subclass must override.
def add(cookie)
# self
end
# Implements HTTP::CookieJar#delete().
#
# This is an abstract method that each subclass must override.
def delete(cookie)
# self
end
# Iterates over all cookies that are not expired.
#
# An optional argument +uri+ specifies a URI object indicating the
# destination of the cookies being selected. Every cookie yielded
# should be good to send to the given URI,
# i.e. cookie.valid_for_uri?(uri) evaluates to true.
#
# If (and only if) the +uri+ option is given, last access time of
# each cookie is updated to the current time.
#
# This is an abstract method that each subclass must override.
def each(uri = nil, &block) # :yield: cookie
# if uri
# ...
# else
# synchronize {
# ...
# }
# end
# self
end
include Enumerable
# Implements HTTP::CookieJar#empty?().
def empty?
each { return false }
true
end
# Implements HTTP::CookieJar#clear().
#
# This is an abstract method that each subclass must override.
def clear
# self
end
# Implements HTTP::CookieJar#cleanup().
#
# This is an abstract method that each subclass must override.
def cleanup(session = false)
# if session
# select { |cookie| cookie.session? || cookie.expired? }
# else
# select(&:expired?)
# end.each { |cookie|
# delete(cookie)
# }
# # subclasses can optionally remove over-the-limit cookies.
# self
end
end
ruby-http-cookie-1.0.3/lib/http/cookie_jar/cookiestxt_saver.rb 0000664 0000000 0000000 00000004650 13413645020 0024476 0 ustar 00root root 0000000 0000000 # :markup: markdown
require 'http/cookie_jar'
# CookiestxtSaver saves and loads cookies in the cookies.txt format.
class HTTP::CookieJar::CookiestxtSaver < HTTP::CookieJar::AbstractSaver
# :singleton-method: new
# :call-seq:
# new(**options)
#
# Available option keywords are below:
#
# * `:header`
#
# Specifies the header line not including a line feed, which is
# only used by #save(). None is output if nil is
# given. (default: `"# HTTP Cookie File"`)
#
# * `:linefeed`
#
# Specifies the line separator (default: `"\n"`).
##
def save(io, jar)
io.puts @header if @header
jar.each { |cookie|
next if !@session && cookie.session?
io.print cookie_to_record(cookie)
}
end
def load(io, jar)
io.each_line { |line|
cookie = parse_record(line) and jar.add(cookie)
}
end
private
def default_options
{
:header => "# HTTP Cookie File",
:linefeed => "\n",
}
end
# :stopdoc:
True = "TRUE"
False = "FALSE"
HTTPONLY_PREFIX = '#HttpOnly_'
RE_HTTPONLY_PREFIX = /\A#{HTTPONLY_PREFIX}/
# :startdoc:
# Serializes the cookie into a cookies.txt line.
def cookie_to_record(cookie)
cookie.instance_eval {
[
@httponly ? HTTPONLY_PREFIX + dot_domain : dot_domain,
@for_domain ? True : False,
@path,
@secure ? True : False,
expires.to_i,
@name,
@value
]
}.join("\t") << @linefeed
end
# Parses a line from cookies.txt and returns a cookie object if the
# line represents a cookie record or returns nil otherwise.
def parse_record(line)
case line
when RE_HTTPONLY_PREFIX
httponly = true
line = $'
when /\A#/
return nil
else
httponly = false
end
domain,
s_for_domain, # Whether this cookie is for domain
path, # Path for which the cookie is relevant
s_secure, # Requires a secure connection
s_expires, # Time the cookie expires (Unix epoch time)
name, value = line.split("\t", 7)
return nil if value.nil?
value.chomp!
if (expires_seconds = s_expires.to_i).nonzero?
expires = Time.at(expires_seconds)
return nil if expires < Time.now
end
HTTP::Cookie.new(name, value,
:domain => domain,
:for_domain => s_for_domain == True,
:path => path,
:secure => s_secure == True,
:httponly => httponly,
:expires => expires)
end
end
ruby-http-cookie-1.0.3/lib/http/cookie_jar/hash_store.rb 0000664 0000000 0000000 00000010171 13413645020 0023234 0 ustar 00root root 0000000 0000000 # :markup: markdown
require 'http/cookie_jar'
class HTTP::CookieJar
# A store class that uses a hash-based cookie store.
#
# In this store, cookies that share the same name, domain and path
# will overwrite each other regardless of the `for_domain` flag
# value. This store is built after the storage model described in
# RFC 6265 5.3 where there is no mention of how the host-only-flag
# affects in storing cookies. On the other hand, in MozillaStore
# two cookies with the same name, domain and path coexist as long as
# they differ in the `for_domain` flag value, which means they need
# to be expired individually.
class HashStore < AbstractStore
def default_options
{
:gc_threshold => HTTP::Cookie::MAX_COOKIES_TOTAL / 20
}
end
# :call-seq:
# new(**options)
#
# Generates a hash based cookie store.
#
# Available option keywords are as below:
#
# :gc_threshold
# : GC threshold; A GC happens when this many times cookies have
# been stored (default: `HTTP::Cookie::MAX_COOKIES_TOTAL / 20`)
def initialize(options = nil)
super
@jar = {
# hostname => {
# path => {
# name => cookie,
# ...
# },
# ...
# },
# ...
}
@gc_index = 0
end
# The copy constructor. This store class supports cloning.
def initialize_copy(other)
@jar = Marshal.load(Marshal.dump(other.instance_variable_get(:@jar)))
end
def add(cookie)
path_cookies = ((@jar[cookie.domain] ||= {})[cookie.path] ||= {})
path_cookies[cookie.name] = cookie
cleanup if (@gc_index += 1) >= @gc_threshold
self
end
def delete(cookie)
path_cookies = ((@jar[cookie.domain] ||= {})[cookie.path] ||= {})
path_cookies.delete(cookie.name)
self
end
def each(uri = nil) # :yield: cookie
now = Time.now
if uri
thost = DomainName.new(uri.host)
tpath = uri.path
@jar.each { |domain, paths|
next unless thost.cookie_domain?(domain)
paths.each { |path, hash|
next unless HTTP::Cookie.path_match?(path, tpath)
hash.delete_if { |name, cookie|
if cookie.expired?(now)
true
else
if cookie.valid_for_uri?(uri)
cookie.accessed_at = now
yield cookie
end
false
end
}
}
}
else
synchronize {
@jar.each { |domain, paths|
paths.each { |path, hash|
hash.delete_if { |name, cookie|
if cookie.expired?(now)
true
else
yield cookie
false
end
}
}
}
}
end
self
end
def clear
@jar.clear
self
end
def cleanup(session = false)
now = Time.now
all_cookies = []
synchronize {
break if @gc_index == 0
@jar.each { |domain, paths|
domain_cookies = []
paths.each { |path, hash|
hash.delete_if { |name, cookie|
if cookie.expired?(now) || (session && cookie.session?)
true
else
domain_cookies << cookie
false
end
}
}
if (debt = domain_cookies.size - HTTP::Cookie::MAX_COOKIES_PER_DOMAIN) > 0
domain_cookies.sort_by!(&:created_at)
domain_cookies.slice!(0, debt).each { |cookie|
delete(cookie)
}
end
all_cookies.concat(domain_cookies)
}
if (debt = all_cookies.size - HTTP::Cookie::MAX_COOKIES_TOTAL) > 0
all_cookies.sort_by!(&:created_at)
all_cookies.slice!(0, debt).each { |cookie|
delete(cookie)
}
end
@jar.delete_if { |domain, paths|
paths.delete_if { |path, hash|
hash.empty?
}
paths.empty?
}
@gc_index = 0
}
self
end
end
end
ruby-http-cookie-1.0.3/lib/http/cookie_jar/mozilla_store.rb 0000664 0000000 0000000 00000032236 13413645020 0023766 0 ustar 00root root 0000000 0000000 # :markup: markdown
require 'http/cookie_jar'
require 'sqlite3'
class HTTP::CookieJar
# A store class that uses Mozilla compatible SQLite3 database as
# backing store.
#
# Session cookies are stored separately on memory and will not be
# stored persistently in the SQLite3 database.
class MozillaStore < AbstractStore
# :stopdoc:
SCHEMA_VERSION = 5
def default_options
{
:gc_threshold => HTTP::Cookie::MAX_COOKIES_TOTAL / 20,
:app_id => 0,
:in_browser_element => false,
}
end
ALL_COLUMNS = %w[
baseDomain
appId inBrowserElement
name value
host path
expiry creationTime lastAccessed
isSecure isHttpOnly
]
UK_COLUMNS = %w[
name host path
appId inBrowserElement
]
SQL = {}
Callable = proc { |obj, meth, *args|
proc {
obj.__send__(meth, *args)
}
}
class Database < SQLite3::Database
def initialize(file, options = {})
@stmts = []
options = {
:results_as_hash => true,
}.update(options)
super
end
def prepare(sql)
case st = super
when SQLite3::Statement
@stmts << st
end
st
end
def close
return self if closed?
@stmts.reject! { |st|
st.closed? || st.close
}
super
end
end
# :startdoc:
# :call-seq:
# new(**options)
#
# Generates a Mozilla cookie store. If the file does not exist,
# it is created. If it does and its schema is old, it is
# automatically upgraded with a new schema keeping the existing
# data.
#
# Available option keywords are as below:
#
# :filename
# : A file name of the SQLite3 database to open. This option is
# mandatory.
#
# :gc_threshold
# : GC threshold; A GC happens when this many times cookies have
# been stored (default: `HTTP::Cookie::MAX_COOKIES_TOTAL / 20`)
#
# :app_id
# : application ID (default: `0`) to have per application jar.
#
# :in_browser_element
# : a flag to tell if cookies are stored in an in browser
# element. (default: `false`)
def initialize(options = nil)
super
@filename = options[:filename] or raise ArgumentError, ':filename option is missing'
@sjar = HTTP::CookieJar::HashStore.new
@db = Database.new(@filename)
@stmt = Hash.new { |st, key|
st[key] = @db.prepare(SQL[key])
}
ObjectSpace.define_finalizer(self, Callable[@db, :close])
upgrade_database
@gc_index = 0
end
# Raises TypeError. Cloning is inhibited in this store class.
def initialize_copy(other)
raise TypeError, 'can\'t clone %s' % self.class
end
# The file name of the SQLite3 database given in initialization.
attr_reader :filename
# Closes the SQLite3 database. After closing, any operation may
# raise an error.
def close
@db.closed? || @db.close
self
end
# Tests if the SQLite3 database is closed.
def closed?
@db.closed?
end
# Returns the schema version of the database.
def schema_version
@schema_version ||= @db.execute("PRAGMA user_version").first[0]
rescue SQLite3::SQLException
@logger.warn "couldn't get schema version!" if @logger
return nil
end
protected
def schema_version= version
@db.execute("PRAGMA user_version = %d" % version)
@schema_version = version
end
def create_table
self.schema_version = SCHEMA_VERSION
@db.execute("DROP TABLE IF EXISTS moz_cookies")
@db.execute(<<-'SQL')
CREATE TABLE moz_cookies (
id INTEGER PRIMARY KEY,
baseDomain TEXT,
appId INTEGER DEFAULT 0,
inBrowserElement INTEGER DEFAULT 0,
name TEXT,
value TEXT,
host TEXT,
path TEXT,
expiry INTEGER,
lastAccessed INTEGER,
creationTime INTEGER,
isSecure INTEGER,
isHttpOnly INTEGER,
CONSTRAINT moz_uniqueid UNIQUE (name, host, path, appId, inBrowserElement)
)
SQL
@db.execute(<<-'SQL')
CREATE INDEX moz_basedomain
ON moz_cookies (baseDomain,
appId,
inBrowserElement);
SQL
end
def db_prepare(sql)
st = @db.prepare(sql)
yield st
ensure
st.close if st
end
def upgrade_database
loop {
case schema_version
when nil, 0
self.schema_version = SCHEMA_VERSION
break
when 1
@db.execute("ALTER TABLE moz_cookies ADD lastAccessed INTEGER")
self.schema_version += 1
when 2
@db.execute("ALTER TABLE moz_cookies ADD baseDomain TEXT")
db_prepare("UPDATE moz_cookies SET baseDomain = :baseDomain WHERE id = :id") { |st_update|
@db.execute("SELECT id, host FROM moz_cookies") { |row|
domain_name = DomainName.new(row['host'][/\A\.?(.*)/, 1])
domain = domain_name.domain || domain_name.hostname
st_update.execute(:baseDomain => domain, :id => row['id'])
}
}
@db.execute("CREATE INDEX moz_basedomain ON moz_cookies (baseDomain)")
self.schema_version += 1
when 3
db_prepare("DELETE FROM moz_cookies WHERE id = :id") { |st_delete|
prev_row = nil
@db.execute(<<-'SQL') { |row|
SELECT id, name, host, path FROM moz_cookies
ORDER BY name ASC, host ASC, path ASC, expiry ASC
SQL
if %w[name host path].all? { |col| prev_row and row[col] == prev_row[col] }
st_delete.execute(prev_row['id'])
end
prev_row = row
}
}
@db.execute("ALTER TABLE moz_cookies ADD creationTime INTEGER")
@db.execute("UPDATE moz_cookies SET creationTime = (SELECT id WHERE id = moz_cookies.id)")
@db.execute("CREATE UNIQUE INDEX moz_uniqueid ON moz_cookies (name, host, path)")
self.schema_version += 1
when 4
@db.execute("ALTER TABLE moz_cookies RENAME TO moz_cookies_old")
@db.execute("DROP INDEX moz_basedomain")
create_table
@db.execute(<<-'SQL')
INSERT INTO moz_cookies
(baseDomain, appId, inBrowserElement, name, value, host, path, expiry,
lastAccessed, creationTime, isSecure, isHttpOnly)
SELECT baseDomain, 0, 0, name, value, host, path, expiry,
lastAccessed, creationTime, isSecure, isHttpOnly
FROM moz_cookies_old
SQL
@db.execute("DROP TABLE moz_cookies_old")
@logger.info("Upgraded database to schema version %d" % schema_version) if @logger
else
break
end
}
begin
@db.execute("SELECT %s from moz_cookies limit 1" % ALL_COLUMNS.join(', '))
rescue SQLite3::SQLException
create_table
end
end
SQL[:add] = <<-'SQL' % [
INSERT OR REPLACE INTO moz_cookies (%s) VALUES (%s)
SQL
ALL_COLUMNS.join(', '),
ALL_COLUMNS.map { |col| ":#{col}" }.join(', ')
]
def db_add(cookie)
@stmt[:add].execute({
:baseDomain => cookie.domain_name.domain || cookie.domain,
:appId => @app_id,
:inBrowserElement => @in_browser_element ? 1 : 0,
:name => cookie.name, :value => cookie.value,
:host => cookie.dot_domain,
:path => cookie.path,
:expiry => cookie.expires_at.to_i,
:creationTime => cookie.created_at.to_i,
:lastAccessed => cookie.accessed_at.to_i,
:isSecure => cookie.secure? ? 1 : 0,
:isHttpOnly => cookie.httponly? ? 1 : 0,
})
cleanup if (@gc_index += 1) >= @gc_threshold
self
end
SQL[:delete] = <<-'SQL'
DELETE FROM moz_cookies
WHERE appId = :appId AND
inBrowserElement = :inBrowserElement AND
name = :name AND
host = :host AND
path = :path
SQL
def db_delete(cookie)
@stmt[:delete].execute({
:appId => @app_id,
:inBrowserElement => @in_browser_element ? 1 : 0,
:name => cookie.name,
:host => cookie.dot_domain,
:path => cookie.path,
})
self
end
public
def add(cookie)
if cookie.session?
@sjar.add(cookie)
db_delete(cookie)
else
@sjar.delete(cookie)
db_add(cookie)
end
end
def delete(cookie)
@sjar.delete(cookie)
db_delete(cookie)
end
SQL[:cookies_for_domain] = <<-'SQL'
SELECT * FROM moz_cookies
WHERE baseDomain = :baseDomain AND
appId = :appId AND
inBrowserElement = :inBrowserElement AND
expiry >= :expiry
SQL
SQL[:update_lastaccessed] = <<-'SQL'
UPDATE moz_cookies
SET lastAccessed = :lastAccessed
WHERE id = :id
SQL
SQL[:all_cookies] = <<-'SQL'
SELECT * FROM moz_cookies
WHERE appId = :appId AND
inBrowserElement = :inBrowserElement AND
expiry >= :expiry
SQL
def each(uri = nil, &block) # :yield: cookie
now = Time.now
if uri
thost = DomainName.new(uri.host)
tpath = uri.path
@stmt[:cookies_for_domain].execute({
:baseDomain => thost.domain || thost.hostname,
:appId => @app_id,
:inBrowserElement => @in_browser_element ? 1 : 0,
:expiry => now.to_i,
}).each { |row|
if secure = row['isSecure'] != 0
next unless URI::HTTPS === uri
end
cookie = HTTP::Cookie.new({}.tap { |attrs|
attrs[:name] = row['name']
attrs[:value] = row['value']
attrs[:domain] = row['host']
attrs[:path] = row['path']
attrs[:expires_at] = Time.at(row['expiry'])
attrs[:accessed_at] = Time.at(row['lastAccessed'] || 0)
attrs[:created_at] = Time.at(row['creationTime'] || 0)
attrs[:secure] = secure
attrs[:httponly] = row['isHttpOnly'] != 0
})
if cookie.valid_for_uri?(uri)
cookie.accessed_at = now
@stmt[:update_lastaccessed].execute({
'lastAccessed' => now.to_i,
'id' => row['id'],
})
yield cookie
end
}
@sjar.each(uri, &block)
else
@stmt[:all_cookies].execute({
:appId => @app_id,
:inBrowserElement => @in_browser_element ? 1 : 0,
:expiry => now.to_i,
}).each { |row|
cookie = HTTP::Cookie.new({}.tap { |attrs|
attrs[:name] = row['name']
attrs[:value] = row['value']
attrs[:domain] = row['host']
attrs[:path] = row['path']
attrs[:expires_at] = Time.at(row['expiry'])
attrs[:accessed_at] = Time.at(row['lastAccessed'] || 0)
attrs[:created_at] = Time.at(row['creationTime'] || 0)
attrs[:secure] = row['isSecure'] != 0
attrs[:httponly] = row['isHttpOnly'] != 0
})
yield cookie
}
@sjar.each(&block)
end
self
end
def clear
@db.execute("DELETE FROM moz_cookies")
@sjar.clear
self
end
SQL[:delete_expired] = <<-'SQL'
DELETE FROM moz_cookies WHERE expiry < :expiry
SQL
SQL[:overusing_domains] = <<-'SQL'
SELECT LTRIM(host, '.') domain, COUNT(*) count
FROM moz_cookies
GROUP BY domain
HAVING count > :count
SQL
SQL[:delete_per_domain_overuse] = <<-'SQL'
DELETE FROM moz_cookies WHERE id IN (
SELECT id FROM moz_cookies
WHERE LTRIM(host, '.') = :domain
ORDER BY creationtime
LIMIT :limit)
SQL
SQL[:delete_total_overuse] = <<-'SQL'
DELETE FROM moz_cookies WHERE id IN (
SELECT id FROM moz_cookies ORDER BY creationTime ASC LIMIT :limit
)
SQL
def cleanup(session = false)
synchronize {
break if @gc_index == 0
@stmt[:delete_expired].execute({ 'expiry' => Time.now.to_i })
@stmt[:overusing_domains].execute({
'count' => HTTP::Cookie::MAX_COOKIES_PER_DOMAIN
}).each { |row|
domain, count = row['domain'], row['count']
@stmt[:delete_per_domain_overuse].execute({
'domain' => domain,
'limit' => count - HTTP::Cookie::MAX_COOKIES_PER_DOMAIN,
})
}
overrun = count - HTTP::Cookie::MAX_COOKIES_TOTAL
if overrun > 0
@stmt[:delete_total_overuse].execute({ 'limit' => overrun })
end
@gc_index = 0
}
self
end
end
end
ruby-http-cookie-1.0.3/lib/http/cookie_jar/yaml_saver.rb 0000664 0000000 0000000 00000003736 13413645020 0023250 0 ustar 00root root 0000000 0000000 # :markup: markdown
require 'http/cookie_jar'
require 'psych' if !defined?(YAML) && RUBY_VERSION == "1.9.2"
require 'yaml'
# YAMLSaver saves and loads cookies in the YAML format. It can load a
# YAML file saved by Mechanize, but the saving format is not
# compatible with older versions of Mechanize (< 2.7).
class HTTP::CookieJar::YAMLSaver < HTTP::CookieJar::AbstractSaver
# :singleton-method: new
# :call-seq:
# new(**options)
#
# There is no option keyword supported at the moment.
##
def save(io, jar)
YAML.dump(@session ? jar.to_a : jar.reject(&:session?), io)
end
def load(io, jar)
begin
data = YAML.load(io)
rescue ArgumentError => e
case e.message
when %r{\Aundefined class/module Mechanize::}
# backward compatibility with Mechanize::Cookie
begin
io.rewind # hopefully
yaml = io.read
# a gross hack
yaml.gsub!(%r{^( [^ ].*:) !ruby/object:Mechanize::Cookie$}, "\\1")
data = YAML.load(yaml)
rescue Errno::ESPIPE
@logger.warn "could not rewind the stream for conversion" if @logger
rescue ArgumentError
end
end
end
case data
when Array
data.each { |cookie|
jar.add(cookie)
}
when Hash
# backward compatibility with Mechanize::Cookie
data.each { |domain, paths|
paths.each { |path, names|
names.each { |cookie_name, cookie_hash|
if cookie_hash.respond_to?(:ivars)
# YAML::Object of Syck
cookie_hash = cookie_hash.ivars
end
cookie = HTTP::Cookie.new({}.tap { |hash|
cookie_hash.each_pair { |key, value|
hash[key.to_sym] = value
}
})
jar.add(cookie)
}
}
}
else
@logger.warn "incompatible YAML cookie data discarded" if @logger
return
end
end
private
def default_options
{}
end
end
ruby-http-cookie-1.0.3/test/ 0000775 0000000 0000000 00000000000 13413645020 0015675 5 ustar 00root root 0000000 0000000 ruby-http-cookie-1.0.3/test/helper.rb 0000664 0000000 0000000 00000002120 13413645020 0017474 0 ustar 00root root 0000000 0000000 require 'rubygems'
require 'test-unit'
require 'uri'
require 'http/cookie'
module Test
module Unit
module Assertions
def assert_warn(pattern, message = nil, &block)
class << (output = "")
alias write <<
end
stderr, $stderr = $stderr, output
yield
assert_match(pattern, output, message)
ensure
$stderr = stderr
end
def assert_warning(pattern, message = nil, &block)
verbose, $VERBOSE = $VERBOSE, true
assert_warn(pattern, message, &block)
ensure
$VERBOSE = verbose
end
end
end
end
module Enumerable
def combine
masks = inject([[], 1]){|(ar, m), e| [ar << m, m << 1 ] }[0]
all = masks.inject(0){ |al, m| al|m }
result = []
for i in 1..all do
tmp = []
each_with_index do |e, idx|
tmp << e unless (masks[idx] & i) == 0
end
result << tmp
end
result
end
end
def test_file(filename)
File.expand_path(filename, File.dirname(__FILE__))
end
def sleep_until(time)
if (s = time - Time.now) > 0
sleep s
end
end
ruby-http-cookie-1.0.3/test/mechanize.yml 0000664 0000000 0000000 00000005572 13413645020 0020374 0 ustar 00root root 0000000 0000000 ---
google.com:
/:
PREF: !ruby/object:Mechanize::Cookie
version: 0
port:
discard:
comment_url:
expires: Tue, 24 Mar 2065 08:20:15 GMT
max_age:
comment:
secure: false
path: /
domain: google.com
accessed_at: 2013-03-24 17:20:15.822619000 +09:00
created_at: 2013-03-24 17:20:15.822619000 +09:00
name: PREF
value: ID=7571a59c059e09db:FF=0:TM=1364199615:LM=1364199615:S=BxUqnqPrchd2cVmC
for_domain: true
domain_name: !ruby/object:DomainName
ipaddr:
hostname: google.com
uri_host: google.com
tld: com
canonical_tld_p: true
domain: google.com
session: false
NID: !ruby/object:Mechanize::Cookie
version: 0
port:
discard:
comment_url:
expires: Sun, 23 Sep 2063 08:20:15 GMT
max_age:
comment:
secure: false
path: /
domain: google.com
accessed_at: 2013-03-24 17:20:15.828434000 +09:00
created_at: 2013-03-24 17:20:15.828434000 +09:00
name: NID
value: 67=Kn2osS6wOzILpl7sCM1QIDmGg2VESBiwCyt6zx4vOVSWKOYDlwGIpgIGrpD8FpkbS9eqizo3QWFa5YkOygnCF6vRIQpbvlTxWB2Hq1Oo-qXWy0317yCqQ-B25eJLfUcC
for_domain: true
domain_name: !ruby/object:DomainName
ipaddr:
hostname: google.com
uri_host: google.com
tld: com
canonical_tld_p: true
domain: google.com
session: false
google.co.jp:
/:
PREF: !ruby/object:Mechanize::Cookie
version: 0
port:
discard:
comment_url:
expires: Tue, 24 Mar 2065 08:20:16 GMT
max_age:
comment:
secure: false
path: /
domain: google.co.jp
accessed_at: 2013-03-24 17:20:17.136581000 +09:00
created_at: 2013-03-24 17:20:17.136581000 +09:00
name: PREF
value: ID=cb25dd1567d8b5c8:FF=0:TM=1364199616:LM=1364199616:S=c3PbhRq79Wo5T_vV
for_domain: true
domain_name: !ruby/object:DomainName
ipaddr:
hostname: google.co.jp
uri_host: google.co.jp
tld: jp
canonical_tld_p: true
domain: google.co.jp
session: false
NID: !ruby/object:Mechanize::Cookie
version: 0
port:
discard:
comment_url:
expires: Sun, 23 Sep 2063 08:20:16 GMT
max_age:
comment:
secure: false
path: /
domain: google.co.jp
accessed_at: 2013-03-24 17:20:17.139782000 +09:00
created_at: 2013-03-24 17:20:17.139782000 +09:00
name: NID
value: 67=GS7P-68zgm_KRA0e0dpN_XbYpmw9uBDe56qUeoCGiSRTahsM7dtOBCKfCoIFRKlzSuOiwJQdIZNpwv3DSXQNHXDKltucgfv2qkHlGeoj8-5VlowPXLLesz2VIpLOLw-a
for_domain: true
domain_name: !ruby/object:DomainName
ipaddr:
hostname: google.co.jp
uri_host: google.co.jp
tld: jp
canonical_tld_p: true
domain: google.co.jp
session: false
ruby-http-cookie-1.0.3/test/simplecov_start.rb 0000664 0000000 0000000 00000000044 13413645020 0021436 0 ustar 00root root 0000000 0000000 require 'simplecov'
SimpleCov.start
ruby-http-cookie-1.0.3/test/test_http_cookie.rb 0000664 0000000 0000000 00000110721 13413645020 0021573 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
require File.expand_path('helper', File.dirname(__FILE__))
class TestHTTPCookie < Test::Unit::TestCase
def setup
httpdate = 'Sun, 27-Sep-2037 00:00:00 GMT'
@cookie_params = {
'expires' => 'expires=%s' % httpdate,
'path' => 'path=/',
'domain' => 'domain=.rubyforge.org',
'httponly' => 'HttpOnly',
}
@expires = Time.parse(httpdate)
end
def test_parse_dates
url = URI.parse('http://localhost/')
yesterday = Time.now - 86400
dates = [ "14 Apr 89 03:20:12",
"14 Apr 89 03:20 GMT",
"Fri, 17 Mar 89 4:01:33",
"Fri, 17 Mar 89 4:01 GMT",
"Mon Jan 16 16:12 PDT 1989",
#"Mon Jan 16 16:12 +0130 1989",
"6 May 1992 16:41-JST (Wednesday)",
#"22-AUG-1993 10:59:12.82",
"22-AUG-1993 10:59pm",
"22-AUG-1993 12:59am",
"22-AUG-1993 12:59 PM",
#"Friday, August 04, 1995 3:54 PM",
#"06/21/95 04:24:34 PM",
#"20/06/95 21:07",
#"95-06-08 19:32:48 EDT",
]
dates.each do |date|
cookie = "PREF=1; expires=#{date}"
assert_equal 1, HTTP::Cookie.parse(cookie, url) { |c|
assert c.expires, "Tried parsing: #{date}"
assert_send [c.expires, :<, yesterday]
}.size
end
[
["PREF=1; expires=Wed, 01 Jan 100 12:34:56 GMT", nil],
["PREF=1; expires=Sat, 01 Jan 1600 12:34:56 GMT", nil],
["PREF=1; expires=Tue, 01 Jan 69 12:34:56 GMT", 2069],
["PREF=1; expires=Thu, 01 Jan 70 12:34:56 GMT", 1970],
["PREF=1; expires=Wed, 01 Jan 20 12:34:56 GMT", 2020],
["PREF=1; expires=Sat, 01 Jan 2020 12:34:60 GMT", nil],
["PREF=1; expires=Sat, 01 Jan 2020 12:60:56 GMT", nil],
["PREF=1; expires=Sat, 01 Jan 2020 24:00:00 GMT", nil],
["PREF=1; expires=Sat, 32 Jan 2020 12:34:56 GMT", nil],
].each { |set_cookie, year|
cookie, = HTTP::Cookie.parse(set_cookie, url)
if year
assert_equal year, cookie.expires.year, "#{set_cookie}: expires in #{year}"
else
assert_equal nil, cookie.expires, "#{set_cookie}: invalid expiry date"
end
}
end
def test_parse_empty
cookie_str = 'a=b; ; c=d'
uri = URI.parse 'http://example'
assert_equal 1, HTTP::Cookie.parse(cookie_str, uri) { |cookie|
assert_equal 'a', cookie.name
assert_equal 'b', cookie.value
}.size
end
def test_parse_no_space
cookie_str = "foo=bar;Expires=Sun, 06 Nov 2011 00:28:06 GMT;Path=/"
uri = URI.parse 'http://example'
assert_equal 1, HTTP::Cookie.parse(cookie_str, uri) { |cookie|
assert_equal 'foo', cookie.name
assert_equal 'bar', cookie.value
assert_equal '/', cookie.path
assert_equal Time.at(1320539286), cookie.expires
}.size
end
def test_parse_too_long_cookie
uri = URI.parse 'http://example'
cookie_str = "foo=#{'Cookie' * 680}; path=/ab/"
assert_equal(HTTP::Cookie::MAX_LENGTH - 1, cookie_str.bytesize)
assert_equal 1, HTTP::Cookie.parse(cookie_str, uri).size
assert_equal 1, HTTP::Cookie.parse(cookie_str.sub(';', 'x;'), uri).size
assert_equal 0, HTTP::Cookie.parse(cookie_str.sub(';', 'xx;'), uri).size
end
def test_parse_quoted
cookie_str =
"quoted=\"value\"; Expires=Sun, 06 Nov 2011 00:11:18 GMT; Path=/; comment=\"comment is \\\"comment\\\"\""
uri = URI.parse 'http://example'
assert_equal 1, HTTP::Cookie.parse(cookie_str, uri) { |cookie|
assert_equal 'quoted', cookie.name
assert_equal 'value', cookie.value
}.size
end
def test_parse_no_nothing
cookie = '; "", ;'
url = URI.parse('http://www.example.com/')
assert_equal 0, HTTP::Cookie.parse(cookie, url).size
end
def test_parse_no_name
cookie = '=no-name; path=/'
url = URI.parse('http://www.example.com/')
assert_equal 0, HTTP::Cookie.parse(cookie, url).size
end
def test_parse_bad_name
cookie = "a\001b=c"
url = URI.parse('http://www.example.com/')
assert_nothing_raised {
assert_equal 0, HTTP::Cookie.parse(cookie, url).size
}
end
def test_parse_bad_value
cookie = "a=b\001c"
url = URI.parse('http://www.example.com/')
assert_nothing_raised {
assert_equal 0, HTTP::Cookie.parse(cookie, url).size
}
end
def test_parse_weird_cookie
cookie = 'n/a, ASPSESSIONIDCSRRQDQR=FBLDGHPBNDJCPCGNCPAENELB; path=/'
url = URI.parse('http://www.searchinnovation.com/')
assert_equal 1, HTTP::Cookie.parse(cookie, url) { |c|
assert_equal('ASPSESSIONIDCSRRQDQR', c.name)
assert_equal('FBLDGHPBNDJCPCGNCPAENELB', c.value)
}.size
end
def test_double_semicolon
double_semi = 'WSIDC=WEST;; domain=.williams-sonoma.com; path=/'
url = URI.parse('http://williams-sonoma.com/')
assert_equal 1, HTTP::Cookie.parse(double_semi, url) { |cookie|
assert_equal('WSIDC', cookie.name)
assert_equal('WEST', cookie.value)
}.size
end
def test_parse_bad_version
bad_cookie = 'PRETANET=TGIAqbFXtt; Name=/PRETANET; Path=/; Version=1.2; Content-type=text/html; Domain=192.168.6.196; expires=Friday, 13-November-2026 23:01:46 GMT;'
url = URI.parse('http://192.168.6.196/')
# The version attribute is obsolete and simply ignored
cookies = HTTP::Cookie.parse(bad_cookie, url)
assert_equal 1, cookies.size
end
def test_parse_bad_max_age
bad_cookie = 'PRETANET=TGIAqbFXtt; Name=/PRETANET; Path=/; Max-Age=forever; Content-type=text/html; Domain=192.168.6.196; expires=Friday, 13-November-2026 23:01:46 GMT;'
url = URI.parse('http://192.168.6.196/')
# A bad max-age is simply ignored
cookies = HTTP::Cookie.parse(bad_cookie, url)
assert_equal 1, cookies.size
assert_equal nil, cookies.first.max_age
end
def test_parse_date_fail
url = URI.parse('http://localhost/')
dates = [
"20/06/95 21:07",
]
dates.each { |date|
cookie = "PREF=1; expires=#{date}"
assert_equal 1, HTTP::Cookie.parse(cookie, url) { |c|
assert_equal(true, c.expires.nil?)
}.size
}
end
def test_parse_domain_dot
url = URI.parse('http://host.example.com/')
cookie_str = 'a=b; domain=.example.com'
cookie = HTTP::Cookie.parse(cookie_str, url).first
assert_equal 'example.com', cookie.domain
assert cookie.for_domain?
assert_equal '.example.com', cookie.dot_domain
end
def test_parse_domain_no_dot
url = URI.parse('http://host.example.com/')
cookie_str = 'a=b; domain=example.com'
cookie = HTTP::Cookie.parse(cookie_str, url).first
assert_equal 'example.com', cookie.domain
assert cookie.for_domain?
assert_equal '.example.com', cookie.dot_domain
end
def test_parse_public_suffix
cookie = HTTP::Cookie.new('a', 'b', :domain => 'com')
assert_equal('com', cookie.domain)
assert_equal(false, cookie.for_domain?)
cookie.origin = 'http://com/'
assert_equal('com', cookie.domain)
assert_equal(false, cookie.for_domain?)
assert_raises(ArgumentError) {
cookie.origin = 'http://example.com/'
}
end
def test_parse_domain_none
url = URI.parse('http://example.com/')
cookie_str = 'a=b;'
cookie = HTTP::Cookie.parse(cookie_str, url).first
assert_equal 'example.com', cookie.domain
assert !cookie.for_domain?
assert_equal 'example.com', cookie.dot_domain
end
def test_parse_max_age
url = URI.parse('http://localhost/')
epoch, date = 4485353164, 'Fri, 19 Feb 2112 19:26:04 GMT'
base = Time.at(1363014000)
cookie = HTTP::Cookie.parse("name=Akinori; expires=#{date}", url).first
assert_equal Time.at(epoch), cookie.expires
cookie = HTTP::Cookie.parse('name=Akinori; max-age=3600', url).first
assert_in_delta Time.now + 3600, cookie.expires, 1
cookie = HTTP::Cookie.parse('name=Akinori; max-age=3600', url, :created_at => base).first
assert_equal base + 3600, cookie.expires
# Max-Age has precedence over Expires
cookie = HTTP::Cookie.parse("name=Akinori; max-age=3600; expires=#{date}", url).first
assert_in_delta Time.now + 3600, cookie.expires, 1
cookie = HTTP::Cookie.parse("name=Akinori; max-age=3600; expires=#{date}", url, :created_at => base).first
assert_equal base + 3600, cookie.expires
cookie = HTTP::Cookie.parse("name=Akinori; expires=#{date}; max-age=3600", url).first
assert_in_delta Time.now + 3600, cookie.expires, 1
cookie = HTTP::Cookie.parse("name=Akinori; expires=#{date}; max-age=3600", url, :created_at => base).first
assert_equal base + 3600, cookie.expires
end
def test_parse_expires_session
url = URI.parse('http://localhost/')
[
'name=Akinori',
'name=Akinori; expires',
'name=Akinori; max-age',
'name=Akinori; expires=',
'name=Akinori; max-age=',
].each { |str|
cookie = HTTP::Cookie.parse(str, url).first
assert cookie.session?, str
}
[
'name=Akinori; expires=Mon, 19 Feb 2012 19:26:04 GMT',
'name=Akinori; max-age=3600',
].each { |str|
cookie = HTTP::Cookie.parse(str, url).first
assert !cookie.session?, str
}
end
def test_parse_many
url = URI 'http://localhost/'
cookie_str =
"abc, " \
"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=/, " \
"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=/, " \
"a_path=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_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=, " \
"rel_path1=rel_path; Expires=Sun, 06 Nov 2011 00:29:52 GMT; no_expires=nope; Path=foo/bar, " \
"rel_path1=rel_path; Expires=Sun, 06 Nov 2011 00:29:52 GMT; no_expires=nope; Path=foo, " \
"no_domain1=no_domain; Expires=Sun, 06 Nov 2011 00:29:53 GMT; no_expires=nope, " \
"no_domain2=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
assert_equal 15, cookies.length
name = cookies.find { |c| c.name == 'name' }
assert_equal "Aaron", name.value
assert_equal "/", name.path
assert_equal Time.at(1320539391), name.expires
a_path = cookies.find { |c| c.name == 'a_path' }
assert_equal "some_path", a_path.value
assert_equal "/some_path", a_path.path
assert_equal Time.at(1320539391), a_path.expires
no_expires = cookies.find { |c| c.name == 'no_expires' }
assert_equal "nope", no_expires.value
assert_equal "/", no_expires.path
assert_nil no_expires.expires
no_path_cookies = cookies.select { |c| c.value == 'no_path' }
assert_equal 3, no_path_cookies.size
no_path_cookies.each { |c|
assert_equal "/", c.path, c.name
assert_equal Time.at(1320539392), c.expires, c.name
}
rel_path_cookies = cookies.select { |c| c.value == 'rel_path' }
assert_equal 2, rel_path_cookies.size
rel_path_cookies.each { |c|
assert_equal "/", c.path, c.name
assert_equal Time.at(1320539392), c.expires, c.name
}
no_domain_cookies = cookies.select { |c| c.value == 'no_domain' }
assert_equal 3, no_domain_cookies.size
no_domain_cookies.each { |c|
assert !c.for_domain?, c.name
assert_equal c.domain, url.host, c.name
assert_equal Time.at(1320539393), c.expires, c.name
}
assert cookies.find { |c| c.name == 'expired' }
end
def test_parse_valid_cookie
url = URI.parse('http://rubyforge.org/')
cookie_params = @cookie_params
cookie_value = '12345%7D=ASDFWEE345%3DASda'
cookie_params.keys.combine.each do |keys|
cookie_text = [cookie_value, *keys.map { |key| cookie_params[key] }].join('; ')
cookie, = HTTP::Cookie.parse(cookie_text, url)
assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
assert_equal('/', cookie.path)
assert_equal(keys.include?('expires') ? @expires : nil, cookie.expires)
assert_equal(keys.include?('httponly'), cookie.httponly?)
end
end
def test_parse_valid_cookie_empty_value
url = URI.parse('http://rubyforge.org/')
cookie_params = @cookie_params
cookie_value = '12345%7D='
cookie_params.keys.combine.each do |keys|
cookie_text = [cookie_value, *keys.map { |key| cookie_params[key] }].join('; ')
cookie, = HTTP::Cookie.parse(cookie_text, url)
assert_equal('12345%7D=', cookie.to_s)
assert_equal('', cookie.value)
assert_equal('/', cookie.path)
assert_equal(keys.include?('expires') ? @expires : nil, cookie.expires)
assert_equal(keys.include?('httponly'), cookie.httponly?)
end
end
# If no path was given, use the one from the URL
def test_cookie_using_url_path
url = URI.parse('http://rubyforge.org/login.php')
cookie_params = @cookie_params
cookie_value = '12345%7D=ASDFWEE345%3DASda'
cookie_params.keys.combine.each do |keys|
next if keys.include?('path')
cookie_text = [cookie_value, *keys.map { |key| cookie_params[key] }].join('; ')
cookie, = HTTP::Cookie.parse(cookie_text, url)
assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
assert_equal('/', cookie.path)
assert_equal(keys.include?('expires') ? @expires : nil, cookie.expires)
assert_equal(keys.include?('httponly'), cookie.httponly?)
end
end
# Test using secure cookies
def test_cookie_with_secure
url = URI.parse('http://rubyforge.org/')
cookie_params = @cookie_params.merge('secure' => 'secure')
cookie_value = '12345%7D=ASDFWEE345%3DASda'
cookie_params.keys.combine.each do |keys|
next unless keys.include?('secure')
cookie_text = [cookie_value, *keys.map { |key| cookie_params[key] }].join('; ')
cookie, = HTTP::Cookie.parse(cookie_text, url)
assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
assert_equal('/', cookie.path)
assert_equal(true, cookie.secure)
assert_equal(keys.include?('expires') ? @expires : nil, cookie.expires)
assert_equal(keys.include?('httponly'), cookie.httponly?)
end
end
def test_cookie_value
[
['foo="bar baz"', 'bar baz'],
['foo="bar\"; \"baz"', 'bar"; "baz'],
].each { |cookie_value, value|
cookie = HTTP::Cookie.new('foo', value)
assert_equal(cookie_value, cookie.cookie_value)
}
pairs = [
['Foo', 'value1'],
['Bar', 'value 2'],
['Baz', 'value3'],
['Bar', 'value"4'],
['Quux', 'x, value=5'],
]
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"; Quux="x, value=5"', cookie_value
hash = HTTP::Cookie.cookie_value_to_hash(cookie_value)
assert_equal pairs.map(&:first).uniq.size, hash.size
hash.each_pair { |name, value|
_, pvalue = pairs.assoc(name)
assert_equal pvalue, value
}
# Do not treat comma in a Cookie header value as separator; see CVE-2016-7401
hash = HTTP::Cookie.cookie_value_to_hash('Quux=x, value=5; Foo=value1; Bar="value 2"; Baz=value3; Bar="value\\"4"')
assert_equal pairs.map(&:first).uniq.size, hash.size
hash.each_pair { |name, value|
_, pvalue = pairs.assoc(name)
assert_equal pvalue, value
}
end
def test_set_cookie_value
url = URI.parse('http://rubyforge.org/path/')
[
HTTP::Cookie.new('a', 'b', :domain => 'rubyforge.org', :path => '/path/'),
HTTP::Cookie.new('a', 'b', :origin => url),
].each { |cookie|
cookie.set_cookie_value
}
[
HTTP::Cookie.new('a', 'b', :domain => 'rubyforge.org'),
HTTP::Cookie.new('a', 'b', :for_domain => true, :path => '/path/'),
].each { |cookie|
assert_raises(RuntimeError) {
cookie.set_cookie_value
}
}
['foo=bar', 'foo="bar"', 'foo="ba\"r baz"'].each { |cookie_value|
cookie_params = @cookie_params.merge('path' => '/path/', 'secure' => 'secure', 'max-age' => 'Max-Age=1000')
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, url, :created_at => date)
cookie2, = HTTP::Cookie.parse(cookie.set_cookie_value, url, :created_at => date)
assert_equal(cookie.name, cookie2.name)
assert_equal(cookie.value, cookie2.value)
assert_equal(cookie.domain, cookie2.domain)
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
}
end
def test_parse_cookie_no_spaces
url = URI.parse('http://rubyforge.org/')
cookie_params = @cookie_params
cookie_value = '12345%7D=ASDFWEE345%3DASda'
cookie_params.keys.combine.each do |keys|
cookie_text = [cookie_value, *keys.map { |key| cookie_params[key] }].join(';')
cookie, = HTTP::Cookie.parse(cookie_text, url)
assert_equal('12345%7D=ASDFWEE345%3DASda', cookie.to_s)
assert_equal('/', cookie.path)
assert_equal(keys.include?('expires') ? @expires : nil, cookie.expires)
assert_equal(keys.include?('httponly'), cookie.httponly?)
end
end
def test_new
cookie = HTTP::Cookie.new('key', 'value')
assert_equal 'key', cookie.name
assert_equal 'value', cookie.value
assert_equal nil, cookie.expires
assert_raises(RuntimeError) {
cookie.acceptable?
}
# Minimum unit for the expires attribute is second
expires = Time.at((Time.now + 3600).to_i)
cookie = HTTP::Cookie.new('key', 'value', :expires => expires.dup)
assert_equal 'key', cookie.name
assert_equal 'value', cookie.value
assert_equal expires, cookie.expires
assert_raises(RuntimeError) {
cookie.acceptable?
}
# various keywords
[
["Expires", /use downcased symbol/],
].each { |key, pattern|
assert_warning(pattern, "warn of key: #{key.inspect}") {
cookie = HTTP::Cookie.new(:value => 'value', :name => 'key', key => expires.dup)
assert_equal 'key', cookie.name
assert_equal 'value', cookie.value
assert_equal expires, cookie.expires, "key: #{key.inspect}"
}
}
[
[:Expires, /unknown attribute name/],
[:expires?, /unknown attribute name/],
[[:expires], /invalid keyword/],
].each { |key, pattern|
assert_warning(pattern, "warn of key: #{key.inspect}") {
cookie = HTTP::Cookie.new(:value => 'value', :name => 'key', key => expires.dup)
assert_equal 'key', cookie.name
assert_equal 'value', cookie.value
assert_equal nil, cookie.expires, "key: #{key.inspect}"
}
}
cookie = HTTP::Cookie.new(:value => 'value', :name => 'key', :expires => expires.dup)
assert_equal 'key', cookie.name
assert_equal 'value', cookie.value
assert_equal expires, cookie.expires
assert_equal false, cookie.for_domain?
assert_raises(RuntimeError) {
# domain and path are missing
cookie.acceptable?
}
cookie = HTTP::Cookie.new(:value => 'value', :name => 'key', :expires => expires.dup, :domain => '.example.com')
assert_equal 'key', cookie.name
assert_equal 'value', cookie.value
assert_equal expires, cookie.expires
assert_equal true, cookie.for_domain?
assert_raises(RuntimeError) {
# path is missing
cookie.acceptable?
}
cookie = HTTP::Cookie.new(:value => 'value', :name => 'key', :expires => expires.dup, :domain => 'example.com', :for_domain => false)
assert_equal 'key', cookie.name
assert_equal 'value', cookie.value
assert_equal expires, cookie.expires
assert_equal false, cookie.for_domain?
assert_raises(RuntimeError) {
# path is missing
cookie.acceptable?
}
cookie = HTTP::Cookie.new(:value => 'value', :name => 'key', :expires => expires.dup, :domain => 'example.org', :for_domain? => true)
assert_equal 'key', cookie.name
assert_equal 'value', cookie.value
assert_equal expires, cookie.expires
assert_equal 'example.org', cookie.domain
assert_equal true, cookie.for_domain?
assert_raises(RuntimeError) {
# path is missing
cookie.acceptable?
}
assert_raises(ArgumentError) { HTTP::Cookie.new() }
assert_raises(ArgumentError) { HTTP::Cookie.new(:value => 'value') }
assert_raises(ArgumentError) { HTTP::Cookie.new('', 'value') }
assert_raises(ArgumentError) { HTTP::Cookie.new('key=key', 'value') }
assert_raises(ArgumentError) { HTTP::Cookie.new("key\tkey", 'value') }
assert_raises(ArgumentError) { HTTP::Cookie.new('key', 'value', 'something') }
assert_raises(ArgumentError) { HTTP::Cookie.new('key', 'value', {}, 'something') }
[
HTTP::Cookie.new(:name => 'name'),
HTTP::Cookie.new("key", nil, :for_domain => true),
HTTP::Cookie.new("key", nil),
HTTP::Cookie.new("key", :secure => true),
HTTP::Cookie.new("key"),
].each { |cookie|
assert_equal '', cookie.value
assert_equal true, cookie.expired?
}
[
HTTP::Cookie.new(:name => 'name', :max_age => 3600),
HTTP::Cookie.new("key", nil, :expires => Time.now + 3600),
HTTP::Cookie.new("key", :expires => Time.now + 3600),
HTTP::Cookie.new("key", :expires => Time.now + 3600, :value => nil),
].each { |cookie|
assert_equal '', cookie.value
assert_equal false, cookie.expired?
}
end
def cookie_values(options = {})
{
:name => 'Foo',
:value => 'Bar',
:path => '/',
:expires => Time.now + (10 * 86400),
:for_domain => true,
:domain => 'rubyforge.org',
:origin => 'http://rubyforge.org/'
}.merge(options)
end
def test_bad_name
[
"a\tb", "a\vb", "a\rb", "a\nb", 'a b',
"a\\b", 'a"b', # 'a:b', 'a/b', 'a[b]',
'a=b', 'a,b', 'a;b',
].each { |name|
assert_raises(ArgumentError) {
HTTP::Cookie.new(cookie_values(:name => name))
}
cookie = HTTP::Cookie.new(cookie_values)
assert_raises(ArgumentError) {
cookie.name = name
}
}
end
def test_bad_value
[
"a\tb", "a\vb", "a\rb", "a\nb",
"a\\b", 'a"b', # 'a:b', 'a/b', 'a[b]',
].each { |name|
assert_raises(ArgumentError) {
HTTP::Cookie.new(cookie_values(:name => name))
}
cookie = HTTP::Cookie.new(cookie_values)
assert_raises(ArgumentError) {
cookie.name = name
}
}
end
def test_compare
time = Time.now
cookies = [
{ :created_at => time + 1 },
{ :created_at => time - 1 },
{ :created_at => time },
{ :created_at => time, :path => '/foo/bar/' },
{ :created_at => time, :path => '/foo/' },
{ :created_at => time, :path => '/foo' },
].map { |attrs| HTTP::Cookie.new(cookie_values(attrs)) }
assert_equal([3, 4, 5, 1, 2, 0], cookies.sort.map { |i|
cookies.find_index { |j| j.equal?(i) }
})
end
def test_expiration
cookie = HTTP::Cookie.new(cookie_values)
assert_equal false, cookie.expired?
assert_equal true, cookie.expired?(cookie.expires + 1)
assert_equal false, cookie.expired?(cookie.expires - 1)
cookie.expire!
assert_equal true, cookie.expired?
end
def test_max_age=
cookie = HTTP::Cookie.new(cookie_values)
expires = cookie.expires
assert_raises(ArgumentError) {
cookie.max_age = "+1"
}
# make sure #expires is not destroyed
assert_equal expires, cookie.expires
assert_raises(ArgumentError) {
cookie.max_age = "1.5"
}
# make sure #expires is not destroyed
assert_equal expires, cookie.expires
assert_raises(ArgumentError) {
cookie.max_age = "1 day"
}
# make sure #expires is not destroyed
assert_equal expires, cookie.expires
assert_raises(TypeError) {
cookie.max_age = [1]
}
# make sure #expires is not destroyed
assert_equal expires, cookie.expires
cookie.max_age = "12"
assert_equal 12, cookie.max_age
cookie.max_age = -3
assert_equal -3, cookie.max_age
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 cookie.created_at + 3600, 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')))
end
def test_new_tld_domain
url = URI 'http://rubyforge.org/'
tld_cookie1 = HTTP::Cookie.new(cookie_values(:domain => 'org', :origin => url))
assert_equal false, tld_cookie1.for_domain?
assert_equal 'org', tld_cookie1.domain
assert_equal false, tld_cookie1.acceptable?
tld_cookie2 = HTTP::Cookie.new(cookie_values(:domain => '.org', :origin => url))
assert_equal false, tld_cookie1.for_domain?
assert_equal 'org', tld_cookie2.domain
assert_equal false, tld_cookie2.acceptable?
end
def test_new_tld_domain_from_tld
url = URI 'http://org/'
tld_cookie1 = HTTP::Cookie.new(cookie_values(:domain => 'org', :origin => url))
assert_equal false, tld_cookie1.for_domain?
assert_equal 'org', tld_cookie1.domain
assert_equal true, tld_cookie1.acceptable?
tld_cookie2 = HTTP::Cookie.new(cookie_values(:domain => '.org', :origin => url))
assert_equal false, tld_cookie1.for_domain?
assert_equal 'org', tld_cookie2.domain
assert_equal true, tld_cookie2.acceptable?
end
def test_fall_back_rules_for_local_domains
url = URI 'http://www.example.local'
tld_cookie = HTTP::Cookie.new(cookie_values(:domain => '.local', :origin => url))
assert_equal false, tld_cookie.acceptable?
sld_cookie = HTTP::Cookie.new(cookie_values(:domain => '.example.local', :origin => url))
assert_equal true, sld_cookie.acceptable?
end
def test_new_rejects_cookies_with_ipv4_address_subdomain
url = URI 'http://192.168.0.1/'
cookie = HTTP::Cookie.new(cookie_values(:domain => '.0.1', :origin => url))
assert_equal false, cookie.acceptable?
end
def test_value
cookie = HTTP::Cookie.new('name', 'value')
assert_equal 'value', cookie.value
cookie.value = 'new value'
assert_equal 'new value', cookie.value
assert_raises(ArgumentError) { cookie.value = "a\tb" }
assert_raises(ArgumentError) { cookie.value = "a\nb" }
assert_equal false, cookie.expired?
cookie.value = nil
assert_equal '', cookie.value
assert_equal true, cookie.expired?
end
def test_path
uri = URI.parse('http://example.com/foo/bar')
assert_equal '/foo/bar', uri.path
cookie_str = 'a=b'
cookie = HTTP::Cookie.parse(cookie_str, uri).first
assert '/foo/', cookie.path
cookie_str = 'a=b; path=/foo'
cookie = HTTP::Cookie.parse(cookie_str, uri).first
assert '/foo', cookie.path
uri = URI.parse('http://example.com')
assert_equal '', uri.path
cookie_str = 'a=b'
cookie = HTTP::Cookie.parse(cookie_str, uri).first
assert '/', cookie.path
cookie_str = 'a=b; path=/foo'
cookie = HTTP::Cookie.parse(cookie_str, uri).first
assert '/foo', cookie.path
end
def test_domain_nil
cookie = HTTP::Cookie.new('a', 'b')
assert_raises(RuntimeError) {
cookie.valid_for_uri?('http://example.com/')
}
end
def test_domain=
url = URI.parse('http://host.dom.example.com:8080/')
cookie_str = 'a=b; domain=Example.Com'
cookie = HTTP::Cookie.parse(cookie_str, url).first
assert 'example.com', cookie.domain
cookie.domain = DomainName(url.host)
assert 'host.dom.example.com', cookie.domain
cookie.domain = 'Dom.example.com'
assert 'dom.example.com', cookie.domain
cookie.domain = Object.new.tap { |o|
def o.to_str
'Example.com'
end
}
assert 'example.com', cookie.domain
url = URI 'http://rubyforge.org/'
[nil, '', '.'].each { |d|
cookie = HTTP::Cookie.new('Foo', 'Bar', :path => '/')
cookie.domain = d
assert_equal nil, cookie.domain, "domain=#{d.inspect}"
assert_equal nil, cookie.domain_name, "domain=#{d.inspect}"
assert_raises(RuntimeError) {
cookie.acceptable?
}
cookie = HTTP::Cookie.new('Foo', 'Bar', :path => '/')
cookie.origin = url
cookie.domain = d
assert_equal url.host, cookie.domain, "domain=#{d.inspect}"
assert_equal true, cookie.acceptable?, "domain=#{d.inspect}"
}
end
def test_origin=
url = URI.parse('http://example.com/path/')
cookie = HTTP::Cookie.new('a', 'b')
assert_raises(ArgumentError) {
cookie.origin = 123
}
cookie.origin = url
assert_equal '/path/', cookie.path
assert_equal 'example.com', cookie.domain
assert_equal false, cookie.for_domain
assert_raises(ArgumentError) {
# cannot change the origin once set
cookie.origin = URI.parse('http://www.example.com/')
}
cookie = HTTP::Cookie.new('a', 'b', :domain => '.example.com', :path => '/')
cookie.origin = url
assert_equal '/', cookie.path
assert_equal 'example.com', cookie.domain
assert_equal true, cookie.for_domain
assert_raises(ArgumentError) {
# cannot change the origin once set
cookie.origin = URI.parse('http://www.example.com/')
}
cookie = HTTP::Cookie.new('a', 'b', :domain => '.example.com')
cookie.origin = URI.parse('http://example.org/')
assert_equal false, cookie.acceptable?
cookie = HTTP::Cookie.new('a', 'b', :domain => '.example.com')
cookie.origin = 'file:///tmp/test.html'
assert_equal nil, cookie.path
cookie = HTTP::Cookie.new('a', 'b', :domain => '.example.com', :path => '/')
cookie.origin = 'file:///tmp/test.html'
assert_equal false, cookie.acceptable?
end
def test_acceptable_from_uri?
cookie = HTTP::Cookie.new(cookie_values(
:domain => 'uk',
:for_domain => true,
:origin => nil))
assert_equal false, cookie.for_domain?
assert_equal true, cookie.acceptable_from_uri?('http://uk/')
assert_equal false, cookie.acceptable_from_uri?('http://foo.uk/')
end
def test_valid_for_uri?
{
HTTP::Cookie.parse('a1=b',
'http://example.com/dir/file.html').first => {
true => [
'http://example.com/dir/',
'http://example.com/dir/test.html',
'https://example.com/dir/',
'https://example.com/dir/test.html',
],
false => [
'file:///dir/test.html',
'http://example.com/dir',
'http://example.com/dir2/test.html',
'http://www.example.com/dir/test.html',
'http://www.example.com/dir2/test.html',
'https://example.com/dir',
'https://example.com/dir2/test.html',
'https://www.example.com/dir/test.html',
'https://www.example.com/dir2/test.html',
]
},
HTTP::Cookie.parse('a2=b; path=/dir2/',
'http://example.com/dir/file.html').first => {
true => [
'http://example.com/dir2/',
'http://example.com/dir2/test.html',
'https://example.com/dir2/',
'https://example.com/dir2/test.html',
],
false => [
'file:///dir/test.html',
'http://example.com/dir/test.html',
'http://www.example.com/dir/test.html',
'http://www.example.com/dir2',
'http://www.example.com/dir2/test.html',
'https://example.com/dir/test.html',
'https://www.example.com/dir/test.html',
'https://www.example.com/dir2',
'https://www.example.com/dir2/test.html',
]
},
HTTP::Cookie.parse('a4=b; domain=example.com; path=/dir2/',
URI('http://example.com/dir/file.html')).first => {
true => [
'https://example.com/dir2/test.html',
'http://example.com/dir2/test.html',
'https://www.example.com/dir2/test.html',
'http://www.example.com/dir2/test.html',
],
false => [
'https://example.com/dir/test.html',
'http://example.com/dir/test.html',
'https://www.example.com/dir/test.html',
'http://www.example.com/dir/test.html',
'file:///dir2/test.html',
]
},
HTTP::Cookie.parse('a4=b; secure',
URI('https://example.com/dir/file.html')).first => {
true => [
'https://example.com/dir/test.html',
],
false => [
'http://example.com/dir/test.html',
'https://example.com/dir2/test.html',
'http://example.com/dir2/test.html',
'file:///dir2/test.html',
]
},
HTTP::Cookie.parse('a5=b',
URI('https://example.com/')).first => {
true => [
'https://example.com',
],
false => [
'file:///',
]
},
HTTP::Cookie.parse('a6=b; path=/dir',
'http://example.com/dir/file.html').first => {
true => [
'http://example.com/dir',
'http://example.com/dir/',
'http://example.com/dir/test.html',
'https://example.com/dir',
'https://example.com/dir/',
'https://example.com/dir/test.html',
],
false => [
'file:///dir/test.html',
'http://example.com/dir2',
'http://example.com/dir2/test.html',
'http://www.example.com/dir/test.html',
'http://www.example.com/dir2/test.html',
'https://example.com/dir2',
'https://example.com/dir2/test.html',
'https://www.example.com/dir/test.html',
'https://www.example.com/dir2/test.html',
]
},
}.each { |cookie, hash|
hash.each { |expected, urls|
urls.each { |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]
}
}
}
end
def test_yaml_expires
require 'yaml'
cookie = HTTP::Cookie.new(cookie_values)
assert_equal false, cookie.session?
assert_equal nil, cookie.max_age
ycookie = YAML.load(cookie.to_yaml)
assert_equal false, ycookie.session?
assert_equal nil, ycookie.max_age
assert_in_delta cookie.expires, ycookie.expires, 1
cookie.expires = nil
ycookie = YAML.load(cookie.to_yaml)
assert_equal true, ycookie.session?
assert_equal nil, ycookie.max_age
cookie.expires = Time.now + 3600
ycookie = YAML.load(cookie.to_yaml)
assert_equal false, ycookie.session?
assert_equal nil, ycookie.max_age
assert_in_delta cookie.expires, ycookie.expires, 1
cookie.max_age = 3600
ycookie = YAML.load(cookie.to_yaml)
assert_equal false, ycookie.session?
assert_in_delta cookie.created_at + 3600, ycookie.expires, 1
cookie.max_age = nil
ycookie = YAML.load(cookie.to_yaml)
assert_equal true, ycookie.session?
assert_equal nil, ycookie.expires
end
def test_s_path_match?
assert_equal true, HTTP::Cookie.path_match?('/admin/', '/admin/index')
assert_equal false, HTTP::Cookie.path_match?('/admin/', '/Admin/index')
assert_equal true, HTTP::Cookie.path_match?('/admin/', '/admin/')
assert_equal false, HTTP::Cookie.path_match?('/admin/', '/admin')
assert_equal true, HTTP::Cookie.path_match?('/admin', '/admin')
assert_equal false, HTTP::Cookie.path_match?('/admin', '/Admin')
assert_equal false, HTTP::Cookie.path_match?('/admin', '/admins')
assert_equal true, HTTP::Cookie.path_match?('/admin', '/admin/')
assert_equal true, HTTP::Cookie.path_match?('/admin', '/admin/index')
end
end
ruby-http-cookie-1.0.3/test/test_http_cookie_jar.rb 0000664 0000000 0000000 00000077134 13413645020 0022441 0 ustar 00root root 0000000 0000000 require File.expand_path('helper', File.dirname(__FILE__))
require 'tmpdir'
module TestHTTPCookieJar
class TestAutoloading < Test::Unit::TestCase
def test_nonexistent_store
assert_raises(NameError) {
HTTP::CookieJar::NonexistentStore
}
end
def test_erroneous_store
Dir.mktmpdir { |dir|
Dir.mkdir(File.join(dir, 'http'))
Dir.mkdir(File.join(dir, 'http', 'cookie_jar'))
rb = File.join(dir, 'http', 'cookie_jar', 'erroneous_store.rb')
File.open(rb, 'w').close
$LOAD_PATH.unshift(dir)
assert_raises(NameError) {
HTTP::CookieJar::ErroneousStore
}
if RUBY_VERSION >= "1.9"
assert_includes $LOADED_FEATURES, rb
else
assert_includes $LOADED_FEATURES, rb[(dir.size + 1)..-1]
end
}
end
def test_nonexistent_saver
assert_raises(NameError) {
HTTP::CookieJar::NonexistentSaver
}
end
def test_erroneous_saver
Dir.mktmpdir { |dir|
Dir.mkdir(File.join(dir, 'http'))
Dir.mkdir(File.join(dir, 'http', 'cookie_jar'))
rb = File.join(dir, 'http', 'cookie_jar', 'erroneous_saver.rb')
File.open(rb, 'w').close
$LOAD_PATH.unshift(dir)
assert_raises(NameError) {
HTTP::CookieJar::ErroneousSaver
}
if RUBY_VERSION >= "1.9"
assert_includes $LOADED_FEATURES, rb
else
assert_includes $LOADED_FEATURES, rb[(dir.size + 1)..-1]
end
}
end
end
module CommonTests
def setup(options = nil, options2 = nil)
default_options = {
:store => :hash,
:gc_threshold => 1500, # increased by 10 for shorter test time
}
new_options = default_options.merge(options || {})
new_options2 = new_options.merge(options2 || {})
@store_type = new_options[:store]
@gc_threshold = new_options[:gc_threshold]
@jar = HTTP::CookieJar.new(new_options)
@jar2 = HTTP::CookieJar.new(new_options2)
end
#def hash_store?
# @store_type == :hash
#end
def mozilla_store?
@store_type == :mozilla
end
def cookie_values(options = {})
{
:name => 'Foo',
:value => 'Bar',
:path => '/',
:expires => Time.at(Time.now.to_i + 10 * 86400), # to_i is important here
:for_domain => true,
:domain => 'rubyforge.org',
:origin => 'http://rubyforge.org/'
}.merge(options)
end
def test_empty?
assert_equal true, @jar.empty?
cookie = HTTP::Cookie.new(cookie_values)
@jar.add(cookie)
assert_equal false, @jar.empty?
assert_equal false, @jar.empty?('http://rubyforge.org/')
assert_equal true, @jar.empty?('http://example.local/')
end
def test_two_cookies_same_domain_and_name_different_paths
url = URI 'http://rubyforge.org/'
cookie = HTTP::Cookie.new(cookie_values)
@jar.add(cookie)
@jar.add(HTTP::Cookie.new(cookie_values(:path => '/onetwo')))
assert_equal(1, @jar.cookies(url).length)
assert_equal 2, @jar.cookies(URI('http://rubyforge.org/onetwo')).length
end
def test_domain_case
url = URI 'http://rubyforge.org/'
# Add one cookie with an expiration date in the future
cookie = HTTP::Cookie.new(cookie_values)
@jar.add(cookie)
assert_equal(1, @jar.cookies(url).length)
@jar.add(HTTP::Cookie.new(cookie_values(:domain => 'RuByForge.Org', :name => 'aaron')))
assert_equal(2, @jar.cookies(url).length)
url2 = URI 'http://RuByFoRgE.oRg/'
assert_equal(2, @jar.cookies(url2).length)
end
def test_host_only
url = URI.parse('http://rubyforge.org/')
@jar.add(HTTP::Cookie.new(
cookie_values(:domain => 'rubyforge.org', :for_domain => false)))
assert_equal(1, @jar.cookies(url).length)
assert_equal(1, @jar.cookies(URI('http://RubyForge.org/')).length)
assert_equal(1, @jar.cookies(URI('https://RubyForge.org/')).length)
assert_equal(0, @jar.cookies(URI('http://www.rubyforge.org/')).length)
end
def test_empty_value
url = URI 'http://rubyforge.org/'
values = cookie_values(:value => "")
# 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)
@jar.add HTTP::Cookie.new(values.merge(:domain => 'RuByForge.Org',
:name => 'aaron'))
assert_equal(2, @jar.cookies(url).length)
url2 = URI 'http://RuByFoRgE.oRg/'
assert_equal(2, @jar.cookies(url2).length)
end
def test_add_future_cookies
url = URI 'http://rubyforge.org/'
# Add one cookie with an expiration date in the future
cookie = HTTP::Cookie.new(cookie_values)
@jar.add(cookie)
assert_equal(1, @jar.cookies(url).length)
# Add the same cookie, and we should still only have one
@jar.add(HTTP::Cookie.new(cookie_values))
assert_equal(1, @jar.cookies(url).length)
# Make sure we can get the cookie from different paths
assert_equal(1, @jar.cookies(URI('http://rubyforge.org/login')).length)
# Make sure we can't get the cookie from different domains
assert_equal(0, @jar.cookies(URI('http://google.com/')).length)
end
def test_add_multiple_cookies
url = URI 'http://rubyforge.org/'
# Add one cookie with an expiration date in the future
cookie = HTTP::Cookie.new(cookie_values)
@jar.add(cookie)
assert_equal(1, @jar.cookies(url).length)
# Add the same cookie, and we should still only have one
@jar.add(HTTP::Cookie.new(cookie_values(:name => 'Baz')))
assert_equal(2, @jar.cookies(url).length)
# Make sure we can get the cookie from different paths
assert_equal(2, @jar.cookies(URI('http://rubyforge.org/login')).length)
# Make sure we can't get the cookie from different domains
assert_equal(0, @jar.cookies(URI('http://google.com/')).length)
end
def test_add_multiple_cookies_with_the_same_name
now = Time.now
cookies = [
{ :value => 'a', :path => '/', },
{ :value => 'b', :path => '/abc/def/', :created_at => now - 1 },
{ :value => 'c', :path => '/abc/def/', :domain => 'www.rubyforge.org', :origin => 'http://www.rubyforge.org/abc/def/', :created_at => now },
{ :value => 'd', :path => '/abc/' },
].map { |attrs|
HTTP::Cookie.new(cookie_values(attrs))
}
url = URI 'http://www.rubyforge.org/abc/def/ghi'
cookies.permutation(cookies.size) { |shuffled|
@jar.clear
shuffled.each { |cookie| @jar.add(cookie) }
assert_equal %w[b c d a], @jar.cookies(url).map { |cookie| cookie.value }
}
end
def test_fall_back_rules_for_local_domains
url = URI 'http://www.example.local'
sld_cookie = HTTP::Cookie.new(cookie_values(:domain => '.example.local', :origin => url))
@jar.add(sld_cookie)
assert_equal(1, @jar.cookies(url).length)
end
def test_add_makes_exception_for_localhost
url = URI 'http://localhost'
tld_cookie = HTTP::Cookie.new(cookie_values(:domain => 'localhost', :origin => url))
@jar.add(tld_cookie)
assert_equal(1, @jar.cookies(url).length)
end
def test_add_cookie_for_the_parent_domain
url = URI 'http://x.foo.com'
cookie = HTTP::Cookie.new(cookie_values(:domain => '.foo.com', :origin => url))
@jar.add(cookie)
assert_equal(1, @jar.cookies(url).length)
end
def test_add_rejects_cookies_with_unknown_domain_or_path
cookie = HTTP::Cookie.new(cookie_values.reject { |k,v| [:origin, :domain].include?(k) })
assert_raises(ArgumentError) {
@jar.add(cookie)
}
cookie = HTTP::Cookie.new(cookie_values.reject { |k,v| [:origin, :path].include?(k) })
assert_raises(ArgumentError) {
@jar.add(cookie)
}
end
def test_add_does_not_reject_cookies_from_a_nested_subdomain
url = URI 'http://y.x.foo.com'
cookie = HTTP::Cookie.new(cookie_values(:domain => '.foo.com', :origin => url))
@jar.add(cookie)
assert_equal(1, @jar.cookies(url).length)
end
def test_cookie_without_leading_dot_does_not_cause_substring_match
url = URI 'http://arubyforge.org/'
cookie = HTTP::Cookie.new(cookie_values(:domain => 'rubyforge.org'))
@jar.add(cookie)
assert_equal(0, @jar.cookies(url).length)
end
def test_cookie_without_leading_dot_matches_subdomains
url = URI 'http://admin.rubyforge.org/'
cookie = HTTP::Cookie.new(cookie_values(:domain => 'rubyforge.org', :origin => url))
@jar.add(cookie)
assert_equal(1, @jar.cookies(url).length)
end
def test_cookies_with_leading_dot_match_subdomains
url = URI 'http://admin.rubyforge.org/'
@jar.add(HTTP::Cookie.new(cookie_values(:domain => '.rubyforge.org', :origin => url)))
assert_equal(1, @jar.cookies(url).length)
end
def test_cookies_with_leading_dot_match_parent_domains
url = URI 'http://rubyforge.org/'
@jar.add(HTTP::Cookie.new(cookie_values(:domain => '.rubyforge.org', :origin => url)))
assert_equal(1, @jar.cookies(url).length)
end
def test_cookies_with_leading_dot_match_parent_domains_exactly
url = URI 'http://arubyforge.org/'
@jar.add(HTTP::Cookie.new(cookie_values(:domain => '.rubyforge.org')))
assert_equal(0, @jar.cookies(url).length)
end
def test_cookie_for_ipv4_address_matches_the_exact_ipaddress
url = URI 'http://192.168.0.1/'
cookie = HTTP::Cookie.new(cookie_values(:domain => '192.168.0.1', :origin => url))
@jar.add(cookie)
assert_equal(1, @jar.cookies(url).length)
end
def test_cookie_for_ipv6_address_matches_the_exact_ipaddress
url = URI 'http://[fe80::0123:4567:89ab:cdef]/'
cookie = HTTP::Cookie.new(cookie_values(:domain => '[fe80::0123:4567:89ab:cdef]', :origin => url))
@jar.add(cookie)
assert_equal(1, @jar.cookies(url).length)
end
def test_cookies_dot
url = URI 'http://www.host.example/'
@jar.add(HTTP::Cookie.new(cookie_values(:domain => 'www.host.example', :origin => url)))
url = URI 'http://wwwxhost.example/'
assert_equal(0, @jar.cookies(url).length)
end
def test_cookies_no_host
url = URI 'file:///path/'
@jar.add(HTTP::Cookie.new(cookie_values(:origin => url)))
assert_equal(0, @jar.cookies(url).length)
end
def test_clear
url = URI 'http://rubyforge.org/'
# Add one cookie with an expiration date in the future
cookie = HTTP::Cookie.new(cookie_values(:origin => url))
@jar.add(cookie)
@jar.add(HTTP::Cookie.new(cookie_values(:name => 'Baz', :origin => url)))
assert_equal(2, @jar.cookies(url).length)
@jar.clear
assert_equal(0, @jar.cookies(url).length)
end
def test_save_cookies_yaml
url = URI 'http://rubyforge.org/'
# Add one cookie with an expiration date in the future
cookie = HTTP::Cookie.new(cookie_values(:origin => url))
s_cookie = HTTP::Cookie.new(cookie_values(:name => 'Bar',
:expires => nil,
:origin => url))
@jar.add(cookie)
@jar.add(s_cookie)
@jar.add(HTTP::Cookie.new(cookie_values(:name => 'Baz', :for_domain => false, :origin => url)))
assert_equal(3, @jar.cookies(url).length)
Dir.mktmpdir do |dir|
value = @jar.save(File.join(dir, "cookies.yml"))
assert_same @jar, value
@jar2.load(File.join(dir, "cookies.yml"))
cookies = @jar2.cookies(url).sort_by { |cookie| cookie.name }
assert_equal(2, cookies.length)
assert_equal('Baz', cookies[0].name)
assert_equal(false, cookies[0].for_domain)
assert_equal('Foo', cookies[1].name)
assert_equal(true, cookies[1].for_domain)
end
assert_equal(3, @jar.cookies(url).length)
end
def test_save_load_signature
Dir.mktmpdir { |dir|
filename = File.join(dir, "cookies.yml")
@jar.save(filename, :format => :cookiestxt, :session => true)
@jar.save(filename, :format => :cookiestxt, :session => true)
@jar.save(filename, :format => :cookiestxt)
@jar.save(filename, :cookiestxt, :session => true)
@jar.save(filename, :cookiestxt)
@jar.save(filename, HTTP::CookieJar::CookiestxtSaver)
@jar.save(filename, HTTP::CookieJar::CookiestxtSaver.new)
@jar.save(filename, :session => true)
@jar.save(filename)
assert_raises(ArgumentError) {
@jar.save()
}
assert_raises(ArgumentError) {
@jar.save(filename, :nonexistent)
}
assert_raises(TypeError) {
@jar.save(filename, { :format => :cookiestxt }, { :session => true })
}
assert_raises(ArgumentError) {
@jar.save(filename, :cookiestxt, { :session => true }, { :format => :cookiestxt })
}
@jar.load(filename, :format => :cookiestxt, :linefeed => "\n")
@jar.load(filename, :format => :cookiestxt, :linefeed => "\n")
@jar.load(filename, :format => :cookiestxt)
@jar.load(filename, HTTP::CookieJar::CookiestxtSaver)
@jar.load(filename, HTTP::CookieJar::CookiestxtSaver.new)
@jar.load(filename, :cookiestxt, :linefeed => "\n")
@jar.load(filename, :cookiestxt)
@jar.load(filename, :linefeed => "\n")
@jar.load(filename)
assert_raises(ArgumentError) {
@jar.load()
}
assert_raises(ArgumentError) {
@jar.load(filename, :nonexistent)
}
assert_raises(TypeError) {
@jar.load(filename, { :format => :cookiestxt }, { :linefeed => "\n" })
}
assert_raises(ArgumentError) {
@jar.load(filename, :cookiestxt, { :linefeed => "\n" }, { :format => :cookiestxt })
}
}
end
def test_save_session_cookies_yaml
url = URI 'http://rubyforge.org/'
# Add one cookie with an expiration date in the future
cookie = HTTP::Cookie.new(cookie_values)
s_cookie = HTTP::Cookie.new(cookie_values(:name => 'Bar',
:expires => nil))
@jar.add(cookie)
@jar.add(s_cookie)
@jar.add(HTTP::Cookie.new(cookie_values(:name => 'Baz')))
assert_equal(3, @jar.cookies(url).length)
Dir.mktmpdir do |dir|
@jar.save(File.join(dir, "cookies.yml"), :format => :yaml, :session => true)
@jar2.load(File.join(dir, "cookies.yml"))
assert_equal(3, @jar2.cookies(url).length)
end
assert_equal(3, @jar.cookies(url).length)
end
def test_save_and_read_cookiestxt
url = URI 'http://rubyforge.org/foo/'
# Add one cookie with an expiration date in the future
cookie = HTTP::Cookie.new(cookie_values)
expires = cookie.expires
s_cookie = HTTP::Cookie.new(cookie_values(:name => 'Bar',
:expires => nil))
cookie2 = HTTP::Cookie.new(cookie_values(:name => 'Baz',
:value => 'Foo#Baz',
:path => '/foo/',
:for_domain => false))
h_cookie = HTTP::Cookie.new(cookie_values(:name => 'Quux',
:value => 'Foo#Quux',
:httponly => true))
ma_cookie = HTTP::Cookie.new(cookie_values(:name => 'Maxage',
:value => 'Foo#Maxage',
:max_age => 15000))
@jar.add(cookie)
@jar.add(s_cookie)
@jar.add(cookie2)
@jar.add(h_cookie)
@jar.add(ma_cookie)
assert_equal(5, @jar.cookies(url).length)
Dir.mktmpdir do |dir|
filename = File.join(dir, "cookies.txt")
@jar.save(filename, :cookiestxt)
content = File.read(filename)
filename2 = File.join(dir, "cookies2.txt")
open(filename2, 'w') { |w|
w.puts '# HTTP Cookie File'
@jar.save(w, :cookiestxt, :header => nil)
}
assert_equal content, File.read(filename2)
assert_match(/^\.rubyforge\.org\t.*\tFoo\t/, content)
assert_match(/^rubyforge\.org\t.*\tBaz\t/, content)
assert_match(/^#HttpOnly_\.rubyforge\.org\t/, content)
@jar2.load(filename, :cookiestxt) # HACK test the format
cookies = @jar2.cookies(url)
assert_equal(4, cookies.length)
cookies.each { |cookie|
case cookie.name
when 'Foo'
assert_equal 'Bar', cookie.value
assert_equal expires, cookie.expires
assert_equal 'rubyforge.org', cookie.domain
assert_equal true, cookie.for_domain
assert_equal '/', cookie.path
assert_equal false, cookie.httponly?
when 'Baz'
assert_equal 'Foo#Baz', cookie.value
assert_equal 'rubyforge.org', cookie.domain
assert_equal false, cookie.for_domain
assert_equal '/foo/', cookie.path
assert_equal false, cookie.httponly?
when 'Quux'
assert_equal 'Foo#Quux', cookie.value
assert_equal expires, cookie.expires
assert_equal 'rubyforge.org', cookie.domain
assert_equal true, cookie.for_domain
assert_equal '/', cookie.path
assert_equal true, cookie.httponly?
when 'Maxage'
assert_equal 'Foo#Maxage', cookie.value
assert_equal nil, cookie.max_age
assert_in_delta ma_cookie.expires, cookie.expires, 1
else
raise
end
}
end
assert_equal(5, @jar.cookies(url).length)
end
def test_load_yaml_mechanize
@jar.load(test_file('mechanize.yml'), :yaml)
assert_equal 4, @jar.to_a.size
com_nid, com_pref = @jar.cookies('http://www.google.com/')
assert_equal 'NID', com_nid.name
assert_equal 'Sun, 23 Sep 2063 08:20:15 GMT', com_nid.expires.httpdate
assert_equal 'google.com', com_nid.domain_name.hostname
assert_equal 'PREF', com_pref.name
assert_equal 'Tue, 24 Mar 2065 08:20:15 GMT', com_pref.expires.httpdate
assert_equal 'google.com', com_pref.domain_name.hostname
cojp_nid, cojp_pref = @jar.cookies('http://www.google.co.jp/')
assert_equal 'NID', cojp_nid.name
assert_equal 'Sun, 23 Sep 2063 08:20:16 GMT', cojp_nid.expires.httpdate
assert_equal 'google.co.jp', cojp_nid.domain_name.hostname
assert_equal 'PREF', cojp_pref.name
assert_equal 'Tue, 24 Mar 2065 08:20:16 GMT', cojp_pref.expires.httpdate
assert_equal 'google.co.jp', cojp_pref.domain_name.hostname
end
def test_expire_cookies
url = URI 'http://rubyforge.org/'
# Add one cookie with an expiration date in the future
cookie = HTTP::Cookie.new(cookie_values)
@jar.add(cookie)
assert_equal(1, @jar.cookies(url).length)
# Add a second cookie
@jar.add(HTTP::Cookie.new(cookie_values(:name => 'Baz')))
assert_equal(2, @jar.cookies(url).length)
# Make sure we can get the cookie from different paths
assert_equal(2, @jar.cookies(URI('http://rubyforge.org/login')).length)
# Expire the first cookie
@jar.add(HTTP::Cookie.new(cookie_values(:expires => Time.now - (10 * 86400))))
assert_equal(1, @jar.cookies(url).length)
# Expire the second cookie
@jar.add(HTTP::Cookie.new(cookie_values( :name => 'Baz', :expires => Time.now - (10 * 86400))))
assert_equal(0, @jar.cookies(url).length)
end
def test_session_cookies
values = cookie_values(:expires => nil)
url = URI 'http://rubyforge.org/'
# 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 can get the cookie from different paths
assert_equal(2, @jar.cookies(URI('http://rubyforge.org/login')).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)
# When given a URI with a blank path, CookieJar#cookies should return
# cookies with the path '/':
url = URI 'http://rubyforge.org'
assert_equal '', url.path
assert_equal(0, @jar.cookies(url).length)
# Now add a cookie with the path set to '/':
@jar.add(HTTP::Cookie.new(values.merge(:name => 'has_root_path', :path => '/')))
assert_equal(1, @jar.cookies(url).length)
end
def test_paths
url = 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(URI('http://rubyforge.org/hello')).length)
assert_equal(0, @jar.cookies(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
# thanks to michal "ocher" ochman for reporting the bug responsible for this test.
values = cookie_values(:expires => nil)
values_ssl = values.merge(:name => 'Baz', :domain => "#{values[:domain]}:443")
url = URI 'https://rubyforge.org/login'
cookie = HTTP::Cookie.new(values)
@jar.add(cookie)
assert_equal(1, @jar.cookies(url).length, "did not handle SSL cookie")
cookie = HTTP::Cookie.new(values_ssl)
@jar.add(cookie)
assert_equal(2, @jar.cookies(url).length, "did not handle SSL cookie with :443")
end
def test_secure_cookie
nurl = URI 'http://rubyforge.org/login'
surl = URI 'https://rubyforge.org/login'
nncookie = HTTP::Cookie.new(cookie_values(:name => 'Foo1', :origin => nurl))
sncookie = HTTP::Cookie.new(cookie_values(:name => 'Foo1', :origin => surl))
nscookie = HTTP::Cookie.new(cookie_values(:name => 'Foo2', :secure => true, :origin => nurl))
sscookie = HTTP::Cookie.new(cookie_values(:name => 'Foo2', :secure => true, :origin => surl))
@jar.add(nncookie)
@jar.add(sncookie)
@jar.add(nscookie)
@jar.add(sscookie)
assert_equal('Foo1', @jar.cookies(nurl).map { |c| c.name }.sort.join(' ') )
assert_equal('Foo1 Foo2', @jar.cookies(surl).map { |c| c.name }.sort.join(' ') )
end
def test_delete
cookie1 = HTTP::Cookie.new(cookie_values)
cookie2 = HTTP::Cookie.new(:name => 'Foo', :value => '',
:domain => 'rubyforge.org',
:for_domain => false,
:path => '/')
cookie3 = HTTP::Cookie.new(:name => 'Foo', :value => '',
:domain => 'rubyforge.org',
:for_domain => true,
:path => '/')
@jar.add(cookie1)
@jar.delete(cookie2)
if mozilla_store?
assert_equal(1, @jar.to_a.length)
@jar.delete(cookie3)
end
assert_equal(0, @jar.to_a.length)
end
def test_accessed_at
orig = HTTP::Cookie.new(cookie_values(:expires => nil))
@jar.add(orig)
time = orig.accessed_at
assert_in_delta 1.0, time, Time.now, "accessed_at is initialized to the current time"
cookie, = @jar.to_a
assert_equal time, cookie.accessed_at, "accessed_at is not updated by each()"
cookie, = @jar.cookies("http://rubyforge.org/")
assert_send [cookie.accessed_at, :>, time], "accessed_at is not updated by each(url)"
end
def test_max_cookies
slimit = HTTP::Cookie::MAX_COOKIES_TOTAL + @gc_threshold
limit_per_domain = HTTP::Cookie::MAX_COOKIES_PER_DOMAIN
uri = URI('http://www.example.org/')
date = Time.at(Time.now.to_i + 86400)
(1..(limit_per_domain + 1)).each { |i|
@jar << HTTP::Cookie.new(cookie_values(
:name => 'Foo%d' % i,
:value => 'Bar%d' % i,
:domain => uri.host,
:for_domain => true,
:path => '/dir%d/' % (i / 2),
:origin => uri
)).tap { |cookie|
cookie.created_at = i == 42 ? date - i : date
}
}
assert_equal limit_per_domain + 1, @jar.to_a.size
@jar.cleanup
count = @jar.to_a.size
assert_equal limit_per_domain, count
assert_equal [*1..(limit_per_domain + 1)] - [42], @jar.map { |cookie|
cookie.name[/(\d+)$/].to_i
}.sort
hlimit = HTTP::Cookie::MAX_COOKIES_TOTAL
n = hlimit / limit_per_domain * 2
(1..n).each { |i|
(1..(limit_per_domain + 1)).each { |j|
uri = URI('http://www%d.example.jp/' % i)
@jar << HTTP::Cookie.new(cookie_values(
:name => 'Baz%d' % j,
:value => 'www%d.example.jp' % j,
:domain => uri.host,
:for_domain => true,
:path => '/dir%d/' % (i / 2),
:origin => uri
)).tap { |cookie|
cookie.created_at = i == j ? date - i : date
}
count += 1
}
}
assert_send [count, :>, slimit]
assert_send [@jar.to_a.size, :<=, slimit]
@jar.cleanup
assert_equal hlimit, @jar.to_a.size
assert_equal false, @jar.any? { |cookie|
cookie.domain == cookie.value
}
end
def test_parse
set_cookie = [
"name=Akinori; Domain=rubyforge.org; Expires=Sun, 08 Aug 2076 19:00:00 GMT; Path=/",
"country=Japan; Domain=rubyforge.org; Expires=Sun, 08 Aug 2076 19:00:00 GMT; Path=/",
"city=Tokyo; Domain=rubyforge.org; Expires=Sun, 08 Aug 2076 19:00:00 GMT; Path=/",
].join(', ')
cookies = @jar.parse(set_cookie, 'http://rubyforge.org/')
assert_equal %w[Akinori Japan Tokyo], cookies.map { |c| c.value }
assert_equal %w[Tokyo Japan Akinori], @jar.to_a.sort_by { |c| c.name }.map { |c| c.value }
end
def test_parse_with_block
set_cookie = [
"name=Akinori; Domain=rubyforge.org; Expires=Sun, 08 Aug 2076 19:00:00 GMT; Path=/",
"country=Japan; Domain=rubyforge.org; Expires=Sun, 08 Aug 2076 19:00:00 GMT; Path=/",
"city=Tokyo; Domain=rubyforge.org; Expires=Sun, 08 Aug 2076 19:00:00 GMT; Path=/",
].join(', ')
cookies = @jar.parse(set_cookie, 'http://rubyforge.org/') { |c| c.name != 'city' }
assert_equal %w[Akinori Japan], cookies.map { |c| c.value }
assert_equal %w[Japan Akinori], @jar.to_a.sort_by { |c| c.name }.map { |c| c.value }
end
def test_expire_by_each_and_cleanup
uri = URI('http://www.example.org/')
ts = Time.now.to_f
if ts % 1 > 0.5
sleep 0.5
ts += 0.5
end
expires = Time.at(ts.floor)
time = expires
if mozilla_store?
# MozillaStore only has the time precision of seconds.
time = expires
expires -= 1
end
0.upto(2) { |i|
c = HTTP::Cookie.new('Foo%d' % (3 - i), 'Bar', :expires => expires + i, :origin => uri)
@jar << c
@jar2 << c
}
assert_equal %w[Foo1 Foo2], @jar.cookies.map(&:name)
assert_equal %w[Foo1 Foo2], @jar2.cookies(uri).map(&:name)
sleep_until time + 1
assert_equal %w[Foo1], @jar.cookies.map(&:name)
assert_equal %w[Foo1], @jar2.cookies(uri).map(&:name)
sleep_until time + 2
@jar.cleanup
@jar2.cleanup
assert_send [@jar, :empty?]
assert_send [@jar2, :empty?]
end
end
class WithHashStore < Test::Unit::TestCase
include CommonTests
def test_new
jar = HTTP::CookieJar.new(:store => :hash)
assert_instance_of HTTP::CookieJar::HashStore, jar.store
assert_raises(ArgumentError) {
jar = HTTP::CookieJar.new(:store => :nonexistent)
}
jar = HTTP::CookieJar.new(:store => HTTP::CookieJar::HashStore.new)
assert_instance_of HTTP::CookieJar::HashStore, jar.store
jar = HTTP::CookieJar.new(:store => HTTP::CookieJar::HashStore)
end
def test_clone
jar = @jar.clone
assert_not_send [
@jar.store,
:equal?,
jar.store
]
assert_not_send [
@jar.store.instance_variable_get(:@jar),
:equal?,
jar.store.instance_variable_get(:@jar)
]
assert_equal @jar.cookies, jar.cookies
end
end
class WithMozillaStore < Test::Unit::TestCase
include CommonTests
def setup
super(
{ :store => :mozilla, :filename => ":memory:" },
{ :store => :mozilla, :filename => ":memory:" })
end
def add_and_delete(jar)
jar.parse("name=Akinori; Domain=rubyforge.org; Expires=Sun, 08 Aug 2076 19:00:00 GMT; Path=/",
'http://rubyforge.org/')
jar.parse("country=Japan; Domain=rubyforge.org; Expires=Sun, 08 Aug 2076 19:00:00 GMT; Path=/",
'http://rubyforge.org/')
jar.delete(HTTP::Cookie.new("name", :domain => 'rubyforge.org'))
end
def test_clone
assert_raises(TypeError) {
@jar.clone
}
end
def test_close
add_and_delete(@jar)
assert_not_send [@jar.store, :closed?]
@jar.store.close
assert_send [@jar.store, :closed?]
@jar.store.close # should do nothing
assert_send [@jar.store, :closed?]
end
def test_finalizer
db = nil
loop {
jar = HTTP::CookieJar.new(:store => :mozilla, :filename => ':memory:')
add_and_delete(jar)
db = jar.store.instance_variable_get(:@db)
class << db
alias close_orig close
def close
STDERR.print "[finalizer is called]"
STDERR.flush
close_orig
end
end
break
}
end
def test_upgrade_mozillastore
Dir.mktmpdir { |dir|
filename = File.join(dir, 'cookies.sqlite')
sqlite = SQLite3::Database.new(filename)
sqlite.execute(<<-'SQL')
CREATE TABLE moz_cookies (
id INTEGER PRIMARY KEY,
name TEXT,
value TEXT,
host TEXT,
path TEXT,
expiry INTEGER,
isSecure INTEGER,
isHttpOnly INTEGER)
SQL
sqlite.execute(<<-'SQL')
PRAGMA user_version = 1
SQL
begin
st_insert = sqlite.prepare(<<-'SQL')
INSERT INTO moz_cookies (
id, name, value, host, path, expiry, isSecure, isHttpOnly
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
SQL
st_insert.execute(1, 'name1', 'value1', '.example.co.jp', '/', 2312085765, 0, 0)
st_insert.execute(2, 'name1', 'value2', '.example.co.jp', '/', 2312085765, 0, 0)
st_insert.execute(3, 'name1', 'value3', 'www.example.co.jp', '/', 2312085765, 0, 0)
ensure
st_insert.close if st_insert
end
sqlite.close
jar = HTTP::CookieJar.new(:store => :mozilla, :filename => filename)
assert_equal 2, jar.to_a.size
assert_equal 2, jar.cookies('http://www.example.co.jp/').size
cookie, *rest = jar.cookies('http://host.example.co.jp/')
assert_send [rest, :empty?]
assert_equal 'value2', cookie.value
}
end
end if begin
require 'sqlite3'
true
rescue LoadError
STDERR.puts 'sqlite3 missing?'
false
end
end