Loading lib/httpx/plugins/cookies.rb +7 −3 Original line number Diff line number Diff line Loading @@ -7,8 +7,6 @@ module HTTPX # # This plugin implements a persistent cookie jar for the duration of a session. # # It also adds a *#cookies* helper, so that you can pre-fill the cookies of a session. # # https://gitlab.com/os85/httpx/wikis/Cookies # module Cookies Loading Loading @@ -46,6 +44,12 @@ module HTTPX request end # factory method to return a Jar to the user, which can then manipulate # externally to the session. def make_jar(*args) Jar.new(*args) end private def set_request_callbacks(request) Loading Loading @@ -96,7 +100,7 @@ module HTTPX cookies.each do |ck| ck.split(/ *; */).each do |cookie| name, value = cookie.split("=", 2) jar.add(Cookie.new(name, value)) jar.set(name, value) end end end Loading lib/httpx/plugins/cookies/cookie.rb +34 −11 Original line number Diff line number Diff line Loading @@ -14,12 +14,14 @@ module HTTPX attr_reader :domain, :path, :name, :value, :created_at # assigns a new +path+ to this cookie. def path=(path) path = String(path) @for_domain = false @path = path.start_with?("/") ? path : "/" end # See #domain. # assigns a new +domain+ to this cookie. def domain=(domain) domain = String(domain) Loading @@ -37,6 +39,13 @@ module HTTPX @domain = @domain_name.hostname end # checks whether +other+ is the same cookie, i.e. name, value, domain and path are # the same. def ==(other) @name == other.name && @value == other.value && @path == other.path && @domain == other.domain 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) Loading @@ -47,12 +56,30 @@ module HTTPX (@created_at <=> other.created_at).nonzero? || 0 end def match?(name_or_options) case name_or_options when String @name == name_or_options when Hash, Array name_or_options.all? { |k, v| respond_to?(k) && send(k) == v } else false end end class << self def new(cookie, *args) return cookie if cookie.is_a?(self) case cookie when self cookie when Array, Hash options = Hash[cookie] #: cookie_attributes super(options[:name], options[:value], options) else super end end # Tests if +target_path+ is under +base_path+ as described in RFC # 6265 5.1.4. +base_path+ must be an absolute path. Loading Loading @@ -84,16 +111,12 @@ module HTTPX end end def initialize(arg, *attrs) def initialize(arg, value, attrs = nil) @created_at = Time.now if attrs.empty? attr_hash = Hash.try_convert(arg) else @name = arg @value, attr_hash = attrs attr_hash = Hash.try_convert(attr_hash) end @value = value attr_hash = Hash.try_convert(attrs) attr_hash.each do |key, val| key = key.downcase.tr("-", "_").to_sym unless key.is_a?(Symbol) Loading lib/httpx/plugins/cookies/jar.rb +69 −10 Original line number Diff line number Diff line Loading @@ -4,7 +4,12 @@ module HTTPX module Plugins::Cookies # The Cookie Jar # # It holds a bunch of cookies. # It stores and manages cookies for a session, such as i.e. evicting when expired, access methods, or # initialization from parsing `Set-Cookie` HTTP header values. # # It closely follows the [CookieStore API](https://developer.mozilla.org/en-US/docs/Web/API/CookieStore), # by implementing the same methods, with a few specific conveniences for this non-browser manipulation use-case. # class Jar using URIExtensions Loading @@ -15,6 +20,8 @@ module HTTPX @cookies = orig.instance_variable_get(:@cookies).dup end # initializes the cookie store, either empty, or with whatever is passed as +cookies+, which # can be an array of HTTPX::Plugins::Cookies::Cookie objects or hashes-or-tuples of cookie attributes. def initialize(cookies = nil) @cookies = [] Loading @@ -32,28 +39,80 @@ module HTTPX end if cookies end # parses the `Set-Cookie` header value as +set_cookie+ and does the corresponding updates. def parse(set_cookie) SetCookieParser.call(set_cookie) do |name, value, attrs| add(Cookie.new(name, value, attrs)) set(Cookie.new(name, value, attrs)) end end def add(cookie, path = nil) c = cookie.dup # returns the first HTTPX::Plugins::Cookie::Cookie instance in the store which matches either the name # (when String) or all attributes (when a Hash or array of tuples) passed to +name_or_options+ def get(name_or_options) each.find { |ck| ck.match?(name_or_options) } end c.path = path if path && c.path == "/" # returns all HTTPX::Plugins::Cookie::Cookie instances in the store which match either the name # (when String) or all attributes (when a Hash or array of tuples) passed to +name_or_options+ def get_all(name_or_options) each.select { |ck| ck.match?(name_or_options) } # rubocop:disable Style/SelectByRegexp end # when +name+ is a HTTPX::Plugins::Cookie::Cookie, it stores it internally; when +name+ is a String, # it creates a cookie with it and the value-or-attributes passed to +value_or_options+. # optionally, +name+ can also be the attributes hash-or-array as long it contains a <tt>:name</tt> field). def set(name, value_or_options = nil) cookie = case name when Cookie raise ArgumentError, "there should not be a second argument" if value_or_options name when Array, Hash raise ArgumentError, "there should not be a second argument" if value_or_options Cookie.new(name) else raise ArgumentError, "the second argument is required" unless value_or_options Cookie.new(name, value_or_options) end # If the user agent receives a new cookie with the same cookie-name, domain-value, and path-value # as a cookie that it has already stored, the existing cookie is evicted and replaced with the new cookie. @cookies.delete_if { |ck| ck.name == c.name && ck.domain == c.domain && ck.path == c.path } @cookies.delete_if { |ck| ck.name == cookie.name && ck.domain == cookie.domain && ck.path == cookie.path } @cookies << cookie end @cookies << c # @deprecated def add(cookie, path = nil) warn "DEPRECATION WARNING: calling `##{__method__}` is deprecated. Use `#set` instead." c = cookie.dup c.path = path if path && c.path == "/" set(c) end # deletes all cookies in the store which match either the name (when String) or all attributes (when a Hash # or array of tuples) passed to +name_or_options+. # # alternatively, of +name_or_options+ is an instance of HTTPX::Plugins::Cookies::Cookiem, it deletes it from the store. def delete(name_or_options) case name_or_options when Cookie @cookies.delete(name_or_options) else @cookies.delete_if { |ck| ck.match?(name_or_options) } end end # returns the list of valid cookies which matdh the domain and path from the URI object passed to +uri+. def [](uri) each(uri).sort end # enumerates over all stored cookies. if +uri+ is passed, it'll filter out expired cookies and # only yield cookies which match its domain and path. def each(uri = nil, &blk) return enum_for(__method__, uri) unless blk Loading @@ -73,7 +132,7 @@ module HTTPX end def merge(other) cookies_dup = dup jar_dup = dup other.each do |elem| cookie = case elem Loading @@ -85,10 +144,10 @@ module HTTPX Cookie.new(elem) end cookies_dup.add(cookie) jar_dup.set(cookie) end cookies_dup jar_dup end end end Loading sig/plugins/cookies.rbs +2 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ module HTTPX module InstanceMethods def cookies: () -> Jar def make_jar: (*untyped) -> Jar end module HeadersMethods Loading sig/plugins/cookies/cookie.rbs +3 −2 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ module HTTPX def cookie_value: () -> String alias to_s cookie_value def match?: (String | cookie_attributes name_or_options) -> bool def valid_for_uri?: (http_uri uri) -> bool def self.new: (Cookie) -> instance Loading @@ -41,8 +43,7 @@ module HTTPX private def initialize: (cookie_attributes) -> untyped | (_ToS, _ToS, ?cookie_attributes) -> untyped def initialize: (_ToS name, _ToS value, ?cookie_attributes) -> void def acceptable_from_uri?: (uri) -> bool Loading Loading
lib/httpx/plugins/cookies.rb +7 −3 Original line number Diff line number Diff line Loading @@ -7,8 +7,6 @@ module HTTPX # # This plugin implements a persistent cookie jar for the duration of a session. # # It also adds a *#cookies* helper, so that you can pre-fill the cookies of a session. # # https://gitlab.com/os85/httpx/wikis/Cookies # module Cookies Loading Loading @@ -46,6 +44,12 @@ module HTTPX request end # factory method to return a Jar to the user, which can then manipulate # externally to the session. def make_jar(*args) Jar.new(*args) end private def set_request_callbacks(request) Loading Loading @@ -96,7 +100,7 @@ module HTTPX cookies.each do |ck| ck.split(/ *; */).each do |cookie| name, value = cookie.split("=", 2) jar.add(Cookie.new(name, value)) jar.set(name, value) end end end Loading
lib/httpx/plugins/cookies/cookie.rb +34 −11 Original line number Diff line number Diff line Loading @@ -14,12 +14,14 @@ module HTTPX attr_reader :domain, :path, :name, :value, :created_at # assigns a new +path+ to this cookie. def path=(path) path = String(path) @for_domain = false @path = path.start_with?("/") ? path : "/" end # See #domain. # assigns a new +domain+ to this cookie. def domain=(domain) domain = String(domain) Loading @@ -37,6 +39,13 @@ module HTTPX @domain = @domain_name.hostname end # checks whether +other+ is the same cookie, i.e. name, value, domain and path are # the same. def ==(other) @name == other.name && @value == other.value && @path == other.path && @domain == other.domain 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) Loading @@ -47,12 +56,30 @@ module HTTPX (@created_at <=> other.created_at).nonzero? || 0 end def match?(name_or_options) case name_or_options when String @name == name_or_options when Hash, Array name_or_options.all? { |k, v| respond_to?(k) && send(k) == v } else false end end class << self def new(cookie, *args) return cookie if cookie.is_a?(self) case cookie when self cookie when Array, Hash options = Hash[cookie] #: cookie_attributes super(options[:name], options[:value], options) else super end end # Tests if +target_path+ is under +base_path+ as described in RFC # 6265 5.1.4. +base_path+ must be an absolute path. Loading Loading @@ -84,16 +111,12 @@ module HTTPX end end def initialize(arg, *attrs) def initialize(arg, value, attrs = nil) @created_at = Time.now if attrs.empty? attr_hash = Hash.try_convert(arg) else @name = arg @value, attr_hash = attrs attr_hash = Hash.try_convert(attr_hash) end @value = value attr_hash = Hash.try_convert(attrs) attr_hash.each do |key, val| key = key.downcase.tr("-", "_").to_sym unless key.is_a?(Symbol) Loading
lib/httpx/plugins/cookies/jar.rb +69 −10 Original line number Diff line number Diff line Loading @@ -4,7 +4,12 @@ module HTTPX module Plugins::Cookies # The Cookie Jar # # It holds a bunch of cookies. # It stores and manages cookies for a session, such as i.e. evicting when expired, access methods, or # initialization from parsing `Set-Cookie` HTTP header values. # # It closely follows the [CookieStore API](https://developer.mozilla.org/en-US/docs/Web/API/CookieStore), # by implementing the same methods, with a few specific conveniences for this non-browser manipulation use-case. # class Jar using URIExtensions Loading @@ -15,6 +20,8 @@ module HTTPX @cookies = orig.instance_variable_get(:@cookies).dup end # initializes the cookie store, either empty, or with whatever is passed as +cookies+, which # can be an array of HTTPX::Plugins::Cookies::Cookie objects or hashes-or-tuples of cookie attributes. def initialize(cookies = nil) @cookies = [] Loading @@ -32,28 +39,80 @@ module HTTPX end if cookies end # parses the `Set-Cookie` header value as +set_cookie+ and does the corresponding updates. def parse(set_cookie) SetCookieParser.call(set_cookie) do |name, value, attrs| add(Cookie.new(name, value, attrs)) set(Cookie.new(name, value, attrs)) end end def add(cookie, path = nil) c = cookie.dup # returns the first HTTPX::Plugins::Cookie::Cookie instance in the store which matches either the name # (when String) or all attributes (when a Hash or array of tuples) passed to +name_or_options+ def get(name_or_options) each.find { |ck| ck.match?(name_or_options) } end c.path = path if path && c.path == "/" # returns all HTTPX::Plugins::Cookie::Cookie instances in the store which match either the name # (when String) or all attributes (when a Hash or array of tuples) passed to +name_or_options+ def get_all(name_or_options) each.select { |ck| ck.match?(name_or_options) } # rubocop:disable Style/SelectByRegexp end # when +name+ is a HTTPX::Plugins::Cookie::Cookie, it stores it internally; when +name+ is a String, # it creates a cookie with it and the value-or-attributes passed to +value_or_options+. # optionally, +name+ can also be the attributes hash-or-array as long it contains a <tt>:name</tt> field). def set(name, value_or_options = nil) cookie = case name when Cookie raise ArgumentError, "there should not be a second argument" if value_or_options name when Array, Hash raise ArgumentError, "there should not be a second argument" if value_or_options Cookie.new(name) else raise ArgumentError, "the second argument is required" unless value_or_options Cookie.new(name, value_or_options) end # If the user agent receives a new cookie with the same cookie-name, domain-value, and path-value # as a cookie that it has already stored, the existing cookie is evicted and replaced with the new cookie. @cookies.delete_if { |ck| ck.name == c.name && ck.domain == c.domain && ck.path == c.path } @cookies.delete_if { |ck| ck.name == cookie.name && ck.domain == cookie.domain && ck.path == cookie.path } @cookies << cookie end @cookies << c # @deprecated def add(cookie, path = nil) warn "DEPRECATION WARNING: calling `##{__method__}` is deprecated. Use `#set` instead." c = cookie.dup c.path = path if path && c.path == "/" set(c) end # deletes all cookies in the store which match either the name (when String) or all attributes (when a Hash # or array of tuples) passed to +name_or_options+. # # alternatively, of +name_or_options+ is an instance of HTTPX::Plugins::Cookies::Cookiem, it deletes it from the store. def delete(name_or_options) case name_or_options when Cookie @cookies.delete(name_or_options) else @cookies.delete_if { |ck| ck.match?(name_or_options) } end end # returns the list of valid cookies which matdh the domain and path from the URI object passed to +uri+. def [](uri) each(uri).sort end # enumerates over all stored cookies. if +uri+ is passed, it'll filter out expired cookies and # only yield cookies which match its domain and path. def each(uri = nil, &blk) return enum_for(__method__, uri) unless blk Loading @@ -73,7 +132,7 @@ module HTTPX end def merge(other) cookies_dup = dup jar_dup = dup other.each do |elem| cookie = case elem Loading @@ -85,10 +144,10 @@ module HTTPX Cookie.new(elem) end cookies_dup.add(cookie) jar_dup.set(cookie) end cookies_dup jar_dup end end end Loading
sig/plugins/cookies.rbs +2 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,8 @@ module HTTPX module InstanceMethods def cookies: () -> Jar def make_jar: (*untyped) -> Jar end module HeadersMethods Loading
sig/plugins/cookies/cookie.rbs +3 −2 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ module HTTPX def cookie_value: () -> String alias to_s cookie_value def match?: (String | cookie_attributes name_or_options) -> bool def valid_for_uri?: (http_uri uri) -> bool def self.new: (Cookie) -> instance Loading @@ -41,8 +43,7 @@ module HTTPX private def initialize: (cookie_attributes) -> untyped | (_ToS, _ToS, ?cookie_attributes) -> untyped def initialize: (_ToS name, _ToS value, ?cookie_attributes) -> void def acceptable_from_uri?: (uri) -> bool Loading