lib/rufus/verbs/cookies.rb in rufus-verbs-0.10 vs lib/rufus/verbs/cookies.rb in rufus-verbs-1.0.0

- old
+ new

@@ -1,327 +1,317 @@ -# #-- -# Copyright (c) 2008, John Mettraux, jmettraux@gmail.com +# Copyright (c) 2008-2010, John Mettraux, jmettraux@gmail.com # # 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. # -# (MIT license) +# Made in Japan. #++ -# -# -# John Mettraux -# -# Made in Japan -# -# 2008/01/19 -# require 'webrick/cookie' -#require 'rubygems' require 'rufus/lru' module Rufus module Verbs + # + # Cookies related methods + # + # http://www.ietf.org/rfc/rfc2109.txt + # + module CookieMixin + # - # Cookies related methods + # making the cookie jar available # - # http://www.ietf.org/rfc/rfc2109.txt + attr_reader :cookies + + protected + # - module CookieMixin + # Prepares the instance variable @cookies for storing + # cooking for this endpoint. + # + # Reads the :cookies endpoint option for determining the + # size of the cookie jar (77 by default). + # + def prepare_cookie_jar - # - # making the cookie jar available - # - attr_reader :cookies + o = @opts[:cookies] - protected + return unless o and o != false - # - # Prepares the instance variable @cookies for storing - # cooking for this endpoint. - # - # Reads the :cookies endpoint option for determining the - # size of the cookie jar (77 by default). - # - def prepare_cookie_jar + s = o.to_s.to_i + s = 77 if s < 1 - o = @opts[:cookies] + @cookies = CookieJar.new s + end - return unless o and o != false + # + # Parses the HTTP response for a potential 'Set-Cookie' header, + # parses and returns it as a hash. + # + def parse_cookies (response) - s = o.to_s.to_i - s = 77 if s < 1 + c = response['Set-Cookie'] + return nil unless c + Cookie.parse_set_cookies c + end - @cookies = CookieJar.new s - end + # + # (This method will have no effect if the EndPoint is not + # tracking cookies) + # + # Registers a potential cookie set by the server. + # + def register_cookies (response, opts) - # - # Parses the HTTP response for a potential 'Set-Cookie' header, - # parses and returns it as a hash. - # - def parse_cookies (response) + return unless @cookies - c = response['Set-Cookie'] - return nil unless c - Cookie.parse_set_cookies c - end + cs = parse_cookies response - # - # (This method will have no effect if the EndPoint is not - # tracking cookies) - # - # Registers a potential cookie set by the server. - # - def register_cookies (response, opts) + return unless cs - return unless @cookies + # "The origin server effectively ends a session by + # sending the client a Set-Cookie header with Max-Age=0" - cs = parse_cookies response + cs.each do |c| - return unless cs + host = opts[:host] + path = opts[:path] + cpath = c.path || "/" - # "The origin server effectively ends a session by - # sending the client a Set-Cookie header with Max-Age=0" + next unless cookie_acceptable?(opts, response, c) - cs.each do |c| + domain = c.domain || host - host = opts[:host] - path = opts[:path] - cpath = c.path || "/" + if c.max_age == 0 + @cookies.remove_cookie domain, path, c + else + @cookies.add_cookie domain, path, c + end + end + end - next unless cookie_acceptable?(opts, response, c) + # + # Checks if the cookie is acceptable in the context of + # the request that sent it. + # + def cookie_acceptable? (opts, response, cookie) - domain = c.domain || host + # reject if : + # + # * The value for the Path attribute is not a prefix of the + # request-URI. + # * The value for the Domain attribute contains no embedded dots + # or does not start with a dot. + # * The value for the request-host does not domain-match the + # Domain attribute. + # * The request-host is a FQDN (not IP address) and has the form + # HD, where D is the value of the Domain attribute, and H is a + # string that contains one or more dots. - if c.max_age == 0 - @cookies.remove_cookie domain, path, c - else - @cookies.add_cookie domain, path, c - end - end - end + cdomain = cookie.domain - # - # Checks if the cookie is acceptable in the context of - # the request that sent it. - # - def cookie_acceptable? (opts, response, cookie) + if cdomain - # reject if : - # - # * The value for the Path attribute is not a prefix of the - # request-URI. - # * The value for the Domain attribute contains no embedded dots - # or does not start with a dot. - # * The value for the request-host does not domain-match the - # Domain attribute. - # * The request-host is a FQDN (not IP address) and has the form - # HD, where D is the value of the Domain attribute, and H is a - # string that contains one or more dots. + return false unless cdomain.index '.' + return false if cdomain[0, 1] != '.' - cdomain = cookie.domain + h, d = split_host(opts[:host]) + return false if d != cdomain + end - if cdomain + #path = opts[:path] + path = response.request.path - return false unless cdomain.index '.' - return false if cdomain[0, 1] != '.' + cpath = cookie.path || "/" - h, d = split_host(opts[:host]) - return false if d != cdomain - end + return false if path[0..cpath.length-1] != cpath - #path = opts[:path] - path = response.request.path + true + end - cpath = cookie.path || "/" + # + # Places the 'Cookie' header in the request if appropriate. + # + # (This method will have no effect if the EndPoint is not + # tracking cookies) + # + def mention_cookies (request, opts) - return false if path[0..cpath.length-1] != cpath + return unless @cookies - true - end + cs = @cookies.fetch_cookies opts[:host], opts[:path] - # - # Places the 'Cookie' header in the request if appropriate. - # - # (This method will have no effect if the EndPoint is not - # tracking cookies) - # - def mention_cookies (request, opts) + request['Cookie'] = cs.collect { |c| c.to_header_s }.join(",") + end + end - return unless @cookies + # + # An extension of the cookie implementation found in WEBrick. + # + # Unmodified for now. + # + class Cookie < WEBrick::Cookie - cs = @cookies.fetch_cookies opts[:host], opts[:path] + def to_header_s - request['Cookie'] = cs.collect { |c| c.to_header_s }.join(",") - end + ret = '' + ret << @name << '=' << @value + ret << '; ' << '$Version=' << @version.to_s if @version > 0 + ret << '; ' << '$Domain=' << @domain if @domain + ret << '; ' << '$Port=' << @port if @port + ret << '; ' << '$Path=' << @path if @path + ret end + end - # - # An extension of the cookie implementation found in WEBrick. - # - # Unmodified for now. - # - class Cookie < WEBrick::Cookie + # + # Cookies are stored by domain, they via this CookieKey which gathers + # path and name of the cookie. + # + class CookieKey - def to_header_s + attr_reader :name, :path - ret = "" - ret << @name << "=" << @value - ret << "; " << "$Version=" << @version.to_s if @version > 0 - ret << "; " << "$Domain=" << @domain if @domain - ret << "; " << "$Port=" << @port if @port - ret << "; " << "$Path=" << @path if @path - ret - end + def initialize (path, cookie) + + @name = cookie.name + @path = path || cookie.path end # - # Cookies are stored by domain, they via this CookieKey which gathers - # path and name of the cookie. + # longer paths first # - class CookieKey + def <=> (other) - attr_reader :name, :path + -1 * (@path <=> other.path) + end - def initialize (path, cookie) + def hash + "#{@name}|#{@path}".hash + end - @name = cookie.name - @path = path || cookie.path - end + def == (other) + (@path == other.path and @name == other.name) + end - # - # longer paths first - # - def <=> (other) + alias eql? == + end - -1 * (@path <=> other.path) - end + # + # A few methods about hostnames. + # + # (in a mixin... could be helpful somewhere else later) + # + module HostMixin - def hash - "#{@name}|#{@path}".hash - end + # + # Matching a classical IP address (not a v6 though). + # Should be sufficient for now. + # + IP_REGEX = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ - def == (other) - (@path == other.path and @name == other.name) - end - - alias eql? == - end - # - # A few methods about hostnames. + # Returns a pair host/domain, note that the domain starts with a dot. # - # (in a mixin... could be helpful somewhere else later) + # split_host('localhost') --> [ 'localhost', nil ] + # split_host('benz.car.co.nz') --> [ 'benz', '.car.co.nz' ] + # split_host('127.0.0.1') --> [ '127.0.0.1', nil ] + # split_host('::1') --> [ '::1', nil ] # - module HostMixin + def split_host (host) - # - # Matching a classical IP address (not a v6 though). - # Should be sufficient for now. - # - IP_REGEX = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ + return [ host, nil ] if IP_REGEX.match host + i = host.index('.') + return [ host, nil ] unless i + [ host[0..i-1], host[i..-1] ] + end + end - # - # Returns a pair host/domain, note that the domain starts with a dot. - # - # split_host('localhost') --> [ 'localhost', nil ] - # split_host('benz.car.co.nz') --> [ 'benz', '.car.co.nz' ] - # split_host('127.0.0.1') --> [ '127.0.0.1', nil ] - # split_host('::1') --> [ '::1', nil ] - # - def split_host (host) + # + # The container for cookies. Features methods for storing and retrieving + # cookies easily. + # + class CookieJar + include HostMixin - return [ host, nil ] if IP_REGEX.match host - i = host.index('.') - return [ host, nil ] unless i - [ host[0..i-1], host[i..-1] ] - end + def initialize (jar_size) + + @per_domain = LruHash.new jar_size end # - # The container for cookies. Features methods for storing and retrieving - # cookies easily. + # Returns the count of cookies currently stored in this jar. # - class CookieJar - include HostMixin + def size - def initialize (jar_size) + @per_domain.keys.inject(0) { |i, d| i + @per_domain[d].size } + end - @per_domain = LruHash.new jar_size - end + def add_cookie (domain, path, cookie) - # - # Returns the count of cookies currently stored in this jar. - # - def size + (@per_domain[domain] ||= {})[CookieKey.new(path, cookie)] = cookie + end - @per_domain.keys.inject(0) { |i, d| i + @per_domain[d].size } - end + def remove_cookie (domain, path, cookie) - def add_cookie (domain, path, cookie) + (d = @per_domain[domain]) + return unless d + d.delete CookieKey.new(path, cookie) + end - (@per_domain[domain] ||= {})[CookieKey.new(path, cookie)] = cookie - end + # + # Retrieves the cookies that matches the combination host/path. + # If the retrieved cookie is expired, will remove it from the jar + # and return nil. + # + def fetch_cookies (host, path) - def remove_cookie (domain, path, cookie) + c = do_fetch(@per_domain[host], path) - (d = @per_domain[domain]) - return unless d - d.delete CookieKey.new(path, cookie) - end + h, d = split_host host + c += do_fetch(@per_domain[d], path) if d - # - # Retrieves the cookies that matches the combination host/path. - # If the retrieved cookie is expired, will remove it from the jar - # and return nil. - # - def fetch_cookies (host, path) + c + end - c = do_fetch(@per_domain[host], path) + private - h, d = split_host host - c += do_fetch(@per_domain[d], path) if d + # + # Returns all the cookies that match a domain (host) and a path. + # + def do_fetch (dh, path) - c - end + return [] unless dh - private - - # - # Returns all the cookies that match a domain (host) and a path. - # - def do_fetch (dh, path) - - return [] unless dh - - keys = dh.keys.sort.find_all do |k| - path[0..k.path.length-1] == k.path - end - keys.inject([]) do |r, k| - r << dh[k] - r - end - end + keys = dh.keys.sort.find_all do |k| + path[0..k.path.length-1] == k.path + end + keys.inject([]) do |r, k| + r << dh[k] + r + end end + end end end