Merge branch 'fix-cookies' into 'master'

Fix cookies

See merge request honeyryderchuck/httpx!36
This commit is contained in:
HoneyryderChuck 2019-03-16 05:07:44 +00:00
commit c1d592433f
3 changed files with 120 additions and 47 deletions

View File

@ -3,20 +3,82 @@
module HTTPX module HTTPX
module Plugins module Plugins
module Cookies module Cookies
using URIExtensions
class Store
def initialize(cookies = nil)
@store = Hash.new { |hash, origin| hash[origin] = HTTP::CookieJar.new }
return unless cookies
cookies = cookies.split(/ *; */) if cookies.is_a?(String)
@default_cookies = cookies.map do |cookie, v|
if cookie.is_a?(HTTP::Cookie)
cookie
else
HTTP::Cookie.new(cookie.to_s, v.to_s)
end
end
end
def set(origin, cookies)
return unless cookies
@store[origin].parse(cookies, origin)
end
def [](uri)
store = @store[uri.origin]
@default_cookies.each do |cookie|
c = cookie.dup
c.domain ||= uri.authority
c.path ||= uri.path
store.add(c)
end if @default_cookies
store
end
end
def self.load_dependencies(*) def self.load_dependencies(*)
require "http/cookie" require "http/cookie"
end end
module InstanceMethods module InstanceMethods
def cookies(cookies) attr_reader :cookies_store
def initialize(*)
super
@cookies_store = @options.cookies || Store.new
end
def with_cookies(cookies)
branch(default_options.with_cookies(cookies)) branch(default_options.with_cookies(cookies))
end end
def wrap
return unless block_given?
super do |client|
old_cookies_store = @cookies_store
@cookies_store = old_cookies_store.dup
begin
yield client
ensure
@cookies_store = old_cookies_store
end
end
end end
module RequestMethods private
def initialize(*)
def on_response(request, response)
@cookies_store.set(request.origin, response.headers["set-cookie"])
super super
@headers.cookies(@options.cookies, self) end
def __build_req(*)
request = super
request.headers.cookies(@cookies_store[request.uri], request)
request
end end
end end
@ -24,38 +86,20 @@ module HTTPX
def cookies(jar, request) def cookies(jar, request)
return unless jar return unless jar
unless jar.is_a?(HTTP::CookieJar) cookie_value = HTTP::Cookie.cookie_value(jar.cookies(request.uri))
jar = jar.each_with_object(HTTP::CookieJar.new) do |(k, v), j| return if cookie_value.empty?
cookie = k.is_a?(HTTP::Cookie) ? v : HTTP::Cookie.new(k.to_s, v.to_s)
cookie.domain = request.authority
cookie.path = request.path
j.add(cookie)
end
end
self["cookie"] = HTTP::Cookie.cookie_value(jar.cookies)
end
end
module ResponseMethods add("cookie", cookie_value)
def cookie_jar
return @cookie_jar if defined?(@cookie_jar)
return nil unless headers.key?("set-cookie")
@cookie_jar ||= begin
jar = HTTP::CookieJar.new
jar.parse(headers["set-cookie"], @request.uri)
jar
end end
end end
alias_method :cookies, :cookie_jar
end
module OptionsMethods module OptionsMethods
def self.included(klass) def self.included(klass)
super super
klass.def_option(:cookies) do |cookies| klass.def_option(:cookies) do |cookies|
cookies.split(/ *; */) if cookies.is_a?(String) return cookies if cookies.is_a?(Store)
cookies
Store.new(cookies)
end end
end end
end end

View File

@ -5,14 +5,12 @@ module Requests
module Cookies module Cookies
def test_plugin_cookies_get def test_plugin_cookies_get
client = HTTPX.plugin(:cookies) client = HTTPX.plugin(:cookies)
assert client.respond_to?(:cookies), "client should be cookie-enabled"
response = client.get(cookies_uri) response = client.get(cookies_uri)
assert response.respond_to?(:cookies), "response should have cookies"
body = json_body(response) body = json_body(response)
assert body.key?("cookies") assert body.key?("cookies")
assert body["cookies"].empty? assert body["cookies"].empty?
session_response = client.cookies("abc" => "def").get(cookies_uri) session_response = client.with_cookies("abc" => "def").get(cookies_uri)
body = json_body(session_response) body = json_body(session_response)
assert body.key?("cookies") assert body.key?("cookies")
assert body["cookies"]["abc"] == "def", "abc wasn't properly set" assert body["cookies"]["abc"] == "def", "abc wasn't properly set"
@ -22,21 +20,39 @@ module Requests
client = HTTPX.plugin(:cookies) client = HTTPX.plugin(:cookies)
session_cookies = { "a" => "b", "c" => "d" } session_cookies = { "a" => "b", "c" => "d" }
session_uri = cookies_set_uri(session_cookies) session_uri = cookies_set_uri(session_cookies)
session_response = client.get(cookies_set_uri(session_cookies)) session_response = client.get(session_uri)
assert session_response.status == 302, "response should redirect" verify_status(session_response, 302)
verify_cookies(client.cookies_store[URI(session_uri)], session_cookies)
assert !session_response.cookies.nil?, "there should be cookies in the response" # first request sets the session
response_cookies = session_response.cookie_jar response = client.get(cookies_uri)
assert !response_cookies.empty?
response_cookies.cookies(session_uri).each do |cookie|
assert(session_cookies.one? { |k, v| k == cookie.name && v == cookie.value })
end
response = client.cookies(response_cookies).get(cookies_uri)
body = json_body(response) body = json_body(response)
assert body.key?("cookies") assert body.key?("cookies")
assert body["cookies"]["a"] == "b" verify_cookies(body["cookies"], session_cookies)
assert body["cookies"]["c"] == "d"
# second request reuses the session
extra_cookie_response = client.with_cookies("e" => "f").get(cookies_uri)
body = json_body(extra_cookie_response)
assert body.key?("cookies")
verify_cookies(body["cookies"], session_cookies.merge("e" => "f"))
# redirect to a different origin only uses the option cookies
other_origin_response = client.with_cookies("e" => "f").get(redirect_uri(origin("google.com")))
verify_status(other_origin_response, 302)
assert !other_origin_response.headers.key?("set-cookie"), "cookies should not transition to next origin"
end
def test_plugin_cookies_follow
client = HTTPX.plugins(:follow_redirects, :cookies)
session_cookies = { "a" => "b", "c" => "d" }
session_uri = cookies_set_uri(session_cookies)
response = client.get(session_uri)
verify_status(response, 200)
assert response.uri.to_s == cookies_uri
body = json_body(response)
assert body.key?("cookies")
verify_cookies(body["cookies"], session_cookies)
end end
private private
@ -48,6 +64,19 @@ module Requests
def cookies_set_uri(cookies) def cookies_set_uri(cookies)
build_uri("/cookies/set?" + URI.encode_www_form(cookies)) build_uri("/cookies/set?" + URI.encode_www_form(cookies))
end end
def verify_cookies(jar, cookies)
assert !jar.nil? && !jar.empty?, "there should be cookies in the response"
assert jar.all? { |cookie|
case cookie
when HTTP::Cookie
cookies.one? { |k, v| k == cookie.name && v == cookie.value }
else
cookie_name, cookie_value = cookie
cookies.one? { |k, v| k == cookie_name && v == cookie_value }
end
}, "jar should contain all expected cookies"
end
end end
end end
end end

View File

@ -52,8 +52,8 @@ module Requests
private private
def redirect_uri def redirect_uri(redirect_uri = redirect_location)
build_uri("/redirect-to?url=" + redirect_location) build_uri("/redirect-to?url=" + redirect_uri)
end end
def max_redirect_uri(n) def max_redirect_uri(n)