http-cookie-1.0.2/ 0000755 0000041 0000041 00000000000 12261202272 013764 5 ustar www-data www-data http-cookie-1.0.2/Rakefile 0000644 0000041 0000041 00000001005 12261202272 015425 0 ustar www-data www-data 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
http-cookie-1.0.2/Gemfile 0000644 0000041 0000041 00000000140 12261202272 015252 0 ustar www-data www-data source 'https://rubygems.org'
# Specify your gem's dependencies in http-cookie.gemspec
gemspec
http-cookie-1.0.2/LICENSE.txt 0000644 0000041 0000041 00000002227 12261202272 015612 0 ustar www-data www-data 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.
http-cookie-1.0.2/.travis.yml 0000644 0000041 0000041 00000000362 12261202272 016076 0 ustar www-data www-data language: ruby
rvm:
- 1.8.7
- ree
- 1.9.3
- 2.0.0
- ruby-head
- jruby-18mode
- jruby-19mode
- jruby-head
- rbx-18mode
- rbx-19mode
matrix:
allow_failures:
- rvm: ruby-head
- rvm: rbx-18mode
- rvm: rbx-19mode
http-cookie-1.0.2/lib/ 0000755 0000041 0000041 00000000000 12261202272 014532 5 ustar www-data www-data http-cookie-1.0.2/lib/http-cookie.rb 0000644 0000041 0000041 00000000026 12261202272 017303 0 ustar www-data www-data require 'http/cookie'
http-cookie-1.0.2/lib/http/ 0000755 0000041 0000041 00000000000 12261202272 015511 5 ustar www-data www-data http-cookie-1.0.2/lib/http/cookie_jar.rb 0000644 0000041 0000041 00000023123 12261202272 020144 0 ustar www-data www-data # :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
http-cookie-1.0.2/lib/http/cookie/ 0000755 0000041 0000041 00000000000 12261202272 016762 5 ustar www-data www-data http-cookie-1.0.2/lib/http/cookie/ruby_compat.rb 0000644 0000041 0000041 00000002355 12261202272 021640 0 ustar www-data www-data 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
http-cookie-1.0.2/lib/http/cookie/scanner.rb 0000644 0000041 0000041 00000012175 12261202272 020746 0 ustar www-data www-data 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
''.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(/;|#{RE_COOKIE_COMMA}/o)
break
else
s << getch
end until eos?
s.rstrip!
}
end
def scan_name_value
name = scan_name
if skip(/\=/)
value = scan_value
else
scan_value
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
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
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
name, value = scan_name_value
yield name, value if value
# The comma is used as separator for concatenating multiple
# values of a header.
skip(/[;,]/)
end
end
end
http-cookie-1.0.2/lib/http/cookie/version.rb 0000644 0000041 0000041 00000000073 12261202272 020774 0 ustar www-data www-data module HTTP
class Cookie
VERSION = "1.0.2"
end
end
http-cookie-1.0.2/lib/http/cookie.rb 0000644 0000041 0000041 00000046465 12261202272 017326 0 ustar www-data www-data # :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
http-cookie-1.0.2/lib/http/cookie_jar/ 0000755 0000041 0000041 00000000000 12261202272 017616 5 ustar www-data www-data http-cookie-1.0.2/lib/http/cookie_jar/hash_store.rb 0000644 0000041 0000041 00000010171 12261202272 022302 0 ustar www-data www-data # :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
http-cookie-1.0.2/lib/http/cookie_jar/abstract_saver.rb 0000644 0000041 0000041 00000003207 12261202272 023150 0 ustar www-data www-data # :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
http-cookie-1.0.2/lib/http/cookie_jar/mozilla_store.rb 0000644 0000041 0000041 00000032236 12261202272 023034 0 ustar www-data www-data # :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
http-cookie-1.0.2/lib/http/cookie_jar/cookiestxt_saver.rb 0000644 0000041 0000041 00000004650 12261202272 023544 0 ustar www-data www-data # :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
http-cookie-1.0.2/lib/http/cookie_jar/yaml_saver.rb 0000644 0000041 0000041 00000003736 12261202272 022316 0 ustar www-data www-data # :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
http-cookie-1.0.2/lib/http/cookie_jar/abstract_store.rb 0000644 0000041 0000041 00000006052 12261202272 023165 0 ustar www-data www-data # :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
http-cookie-1.0.2/metadata.yml 0000644 0000041 0000041 00000011174 12261202272 016273 0 ustar www-data www-data --- !ruby/object:Gem::Specification
name: http-cookie
version: !ruby/object:Gem::Version
version: 1.0.2
platform: ruby
authors:
- Akinori MUSHA
- Aaron Patterson
- Eric Hodel
- Mike Dalessio
autorequire:
bindir: bin
cert_chain: []
date: 2013-09-10 00:00:00.000000000 Z
dependencies:
- !ruby/object:Gem::Dependency
name: domain_name
requirement: !ruby/object:Gem::Requirement
requirements:
- - ~>
- !ruby/object:Gem::Version
version: '0.5'
type: :runtime
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - ~>
- !ruby/object:Gem::Version
version: '0.5'
- !ruby/object:Gem::Dependency
name: sqlite3
requirement: !ruby/object:Gem::Requirement
requirements:
- - ~>
- !ruby/object:Gem::Version
version: 1.3.3
type: :development
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - ~>
- !ruby/object:Gem::Version
version: 1.3.3
- !ruby/object:Gem::Dependency
name: bundler
requirement: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: 1.2.0
type: :development
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: 1.2.0
- !ruby/object:Gem::Dependency
name: test-unit
requirement: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: 2.4.3
type: :development
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: 2.4.3
- !ruby/object:Gem::Dependency
name: rake
requirement: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: 0.9.2.2
type: :development
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: 0.9.2.2
- !ruby/object:Gem::Dependency
name: rdoc
requirement: !ruby/object:Gem::Requirement
requirements:
- - '>'
- !ruby/object:Gem::Version
version: 2.4.2
type: :development
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - '>'
- !ruby/object:Gem::Version
version: 2.4.2
- !ruby/object:Gem::Dependency
name: simplecov
requirement: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: '0'
type: :development
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: '0'
description: 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.
email:
- knu@idaemons.org
- aaronp@rubyforge.org
- drbrain@segment7.net
- mike.dalessio@gmail.com
executables: []
extensions: []
extra_rdoc_files:
- README.md
- LICENSE.txt
files:
- .gitignore
- .travis.yml
- CHANGELOG.md
- Gemfile
- LICENSE.txt
- README.md
- Rakefile
- http-cookie.gemspec
- lib/http-cookie.rb
- lib/http/cookie.rb
- lib/http/cookie/ruby_compat.rb
- lib/http/cookie/scanner.rb
- lib/http/cookie/version.rb
- lib/http/cookie_jar.rb
- lib/http/cookie_jar/abstract_saver.rb
- lib/http/cookie_jar/abstract_store.rb
- lib/http/cookie_jar/cookiestxt_saver.rb
- lib/http/cookie_jar/hash_store.rb
- lib/http/cookie_jar/mozilla_store.rb
- lib/http/cookie_jar/yaml_saver.rb
- test/helper.rb
- test/mechanize.yml
- test/simplecov_start.rb
- test/test_http_cookie.rb
- test/test_http_cookie_jar.rb
homepage: https://github.com/sparklemotion/http-cookie
licenses:
- MIT
metadata: {}
post_install_message:
rdoc_options: []
require_paths:
- lib
required_ruby_version: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: '0'
required_rubygems_version: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: '0'
requirements: []
rubyforge_project:
rubygems_version: 2.0.3
signing_key:
specification_version: 4
summary: A Ruby library to handle HTTP Cookies based on RFC 6265
test_files:
- test/helper.rb
- test/mechanize.yml
- test/simplecov_start.rb
- test/test_http_cookie.rb
- test/test_http_cookie_jar.rb
http-cookie-1.0.2/test/ 0000755 0000041 0000041 00000000000 12261202272 014743 5 ustar www-data www-data http-cookie-1.0.2/test/helper.rb 0000644 0000041 0000041 00000002120 12261202272 016542 0 ustar www-data www-data 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
http-cookie-1.0.2/test/mechanize.yml 0000644 0000041 0000041 00000005572 12261202272 017442 0 ustar www-data www-data ---
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
http-cookie-1.0.2/test/test_http_cookie.rb 0000644 0000041 0000041 00000110021 12261202272 020632 0 ustar www-data www-data # -*- 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'],
]
cookie_value = HTTP::Cookie.cookie_value(pairs.map { |name, value|
HTTP::Cookie.new(:name => name, :value => value)
})
assert_equal 'Foo=value1; Bar="value 2"; Baz=value3; Bar="value\\"4"', cookie_value
hash = HTTP::Cookie.cookie_value_to_hash(cookie_value)
assert_equal 3, hash.size
hash.each_pair { |name, value|
_, pvalue = pairs.assoc(name)
assert_equal pvalue, value
}
end
def test_set_cookie_value
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
http-cookie-1.0.2/test/test_http_cookie_jar.rb 0000644 0000041 0000041 00000077134 12261202272 021507 0 ustar www-data www-data 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
http-cookie-1.0.2/test/simplecov_start.rb 0000644 0000041 0000041 00000000044 12261202272 020504 0 ustar www-data www-data require 'simplecov'
SimpleCov.start
http-cookie-1.0.2/.gitignore 0000644 0000041 0000041 00000000232 12261202272 015751 0 ustar www-data www-data *.gem
*.rbc
.bundle
.config
.yardoc
Gemfile.lock
InstalledFiles
_yardoc
coverage
doc/
lib/bundler/man
pkg
rdoc
spec/reports
test/tmp
test/version_tmp
tmp
http-cookie-1.0.2/http-cookie.gemspec 0000644 0000041 0000041 00000003441 12261202272 017561 0 ustar www-data www-data # -*- 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"])
gem.add_development_dependency("rake", [">= 0.9.2.2"])
gem.add_development_dependency("rdoc", ["> 2.4.2"])
gem.add_development_dependency("simplecov", [">= 0"])
end
http-cookie-1.0.2/CHANGELOG.md 0000644 0000041 0000041 00000000670 12261202272 015600 0 ustar www-data www-data ## 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.
http-cookie-1.0.2/checksums.yaml.gz 0000444 0000041 0000041 00000000414 12261202272 017251 0 ustar www-data www-data .Re9R0E"ŒttڨhT6Rv|~r~c~췯Uw(G[@sD=ܓy'3VW{
q_,9B=ԿU^hP!:4ON,wsMLA`Ie)aӉS'iYgTP,TivJ.aBrC_Caf<[N
E*tZ5,&