mirror of
https://github.com/HoneyryderChuck/httpx.git
synced 2025-08-10 00:01:27 -04:00
request exposes options: this allows responses to be created with the proper set of options, instead of the connection options, which is wrong
This commit is contained in:
parent
d46a9aaab2
commit
ae859f743f
@ -10,7 +10,8 @@ services:
|
||||
cache:
|
||||
key: ${CI_COMMIT_REF_SLUG}
|
||||
paths:
|
||||
- vendor/ruby
|
||||
- .bundle/ruby
|
||||
- .bundle/jruby
|
||||
|
||||
before_script:
|
||||
- docker info
|
||||
|
@ -20,7 +20,7 @@ AllCops:
|
||||
Metrics/LineLength:
|
||||
Exclude:
|
||||
- 'test/resolver/native_test.rb'
|
||||
Max: 120
|
||||
Max: 140
|
||||
|
||||
Metrics/MethodLength:
|
||||
Max: 200
|
||||
|
50
doc/release_notes/0_4_0.md
Normal file
50
doc/release_notes/0_4_0.md
Normal file
@ -0,0 +1,50 @@
|
||||
# 0.4.0
|
||||
|
||||
* Feature: SSH proxy plugin -> send requests over ssh gateway;
|
||||
|
||||
```ruby
|
||||
HTTPX.plugin(:"proxy/ssh").
|
||||
with_proxy(uri: "ssh://localhost:2222",
|
||||
username: "root",
|
||||
auth_methods: %w[publickey],
|
||||
host_key: "ssh-rsa",
|
||||
keys: %w[test/support/ssh/ssh_host_ed25519_key]).get(URI)
|
||||
```
|
||||
|
||||
* Feature: Faraday Adapter
|
||||
|
||||
* refactoring: cookies plugin API simplification (this is a breaking change!):
|
||||
|
||||
```ruby
|
||||
session = HTTPX.plugin(:cookies)
|
||||
session.with_cookies("a" => "b").get(...
|
||||
session.cookies #=> session current cookie store, persists/updates session cookies as requests are processed
|
||||
session.wrap do |session|
|
||||
session.get(..) #=> "Set-Cookie"
|
||||
...
|
||||
end #=> after this, cookie store resets to the state previous to wrap
|
||||
```
|
||||
|
||||
Removed `Session#cookie_store`
|
||||
|
||||
```ruby
|
||||
client = HTTPX.plugin(:cookies)
|
||||
redirect_response = client.get(URI) #=> ... 302 ... Set-Cookie: "blablalba" ...
|
||||
# this sets the cookies
|
||||
# GET .... Cookie: "blablabla" ....
|
||||
response = client.get(URI) #=> ... 200 ...
|
||||
# also, setting cookies:
|
||||
|
||||
client.cookies("a" => "b").get(URI) # ... Cookie: "a=b" ...
|
||||
|
||||
#also seamlessly integrates with redirect follows
|
||||
client = HTTPX.plugins(:follow_redirects, :cookies)
|
||||
response = client.get(URI) #=> ... 200 ...
|
||||
|
||||
* refactoring: connection pool now thread-local, improves thread-safety;
|
||||
|
||||
* bugfix: leaking dns query when passing IO object as option;
|
||||
|
||||
* bugfix: now multiple different resolvers are supported;
|
||||
|
||||
* support: JRuby is again supported (as usual, only latest stable is guaranteed)
|
@ -142,7 +142,7 @@ module HTTPX
|
||||
end
|
||||
|
||||
def inflight?
|
||||
@parser && !@parser.empty?
|
||||
@parser && !@parser.empty? && !@write_buffer.empty?
|
||||
end
|
||||
|
||||
def interests
|
||||
|
@ -79,11 +79,11 @@ module HTTPX
|
||||
return if @request.response
|
||||
|
||||
log(level: 2) { "headers received" }
|
||||
headers = @options.headers_class.new(h)
|
||||
response = @options.response_class.new(@request,
|
||||
@parser.status_code,
|
||||
@parser.http_version.join("."),
|
||||
headers, @options)
|
||||
headers = @request.options.headers_class.new(h)
|
||||
response = @request.options.response_class.new(@request,
|
||||
@parser.status_code,
|
||||
@parser.http_version.join("."),
|
||||
headers)
|
||||
log(color: :yellow) { "-> HEADLINE: #{response.status} HTTP/#{@parser.http_version.join(".")}" }
|
||||
log(color: :yellow) { response.headers.each.map { |f, v| "-> HEADER: #{f}: #{v}" }.join("\n") }
|
||||
|
||||
|
@ -183,8 +183,8 @@ module HTTPX
|
||||
h.map { |k, v| "<- HEADER: #{k}: #{v}" }.join("\n")
|
||||
end
|
||||
_, status = h.shift
|
||||
headers = @options.headers_class.new(h)
|
||||
response = @options.response_class.new(request, status, "2.0", headers, @options)
|
||||
headers = request.options.headers_class.new(h)
|
||||
response = request.options.response_class.new(request, status, "2.0", headers)
|
||||
request.response = response
|
||||
@streams[request] = stream
|
||||
end
|
||||
|
@ -28,6 +28,9 @@ module HTTPX
|
||||
module ResponseBodyMethods
|
||||
def initialize(*)
|
||||
super
|
||||
|
||||
return unless @headers.key?("content-encoding")
|
||||
|
||||
@_decoders = @headers.get("content-encoding").map do |encoding|
|
||||
Compression.registry(encoding).decoder
|
||||
end
|
||||
@ -39,6 +42,8 @@ module HTTPX
|
||||
end
|
||||
|
||||
def write(chunk)
|
||||
return super unless defined?(@_compressed_length)
|
||||
|
||||
@_compressed_length -= chunk.bytesize
|
||||
chunk = decompress(chunk)
|
||||
super(chunk)
|
||||
@ -46,6 +51,9 @@ module HTTPX
|
||||
|
||||
def close
|
||||
super
|
||||
|
||||
return unless defined?(@_decoders)
|
||||
|
||||
@_decoders.each(&:close)
|
||||
end
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "forwardable"
|
||||
|
||||
module HTTPX
|
||||
module Plugins
|
||||
module Cookies
|
||||
@ -53,11 +55,12 @@ module HTTPX
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
attr_reader :cookies_store
|
||||
extend Forwardable
|
||||
|
||||
def initialize(*)
|
||||
super
|
||||
@cookies_store = @options.cookies || Store.new
|
||||
def_delegator :@options, :cookies
|
||||
|
||||
def initialize(options = {}, &blk)
|
||||
super({ cookies: Store.new }.merge(options), &blk)
|
||||
end
|
||||
|
||||
def with_cookies(cookies)
|
||||
@ -65,15 +68,14 @@ module HTTPX
|
||||
end
|
||||
|
||||
def wrap
|
||||
return unless block_given?
|
||||
return super unless block_given?
|
||||
|
||||
super do |session|
|
||||
old_cookies_store = @cookies_store
|
||||
@cookies_store = old_cookies_store.dup
|
||||
old_cookies_store = @options.cookies.dup
|
||||
begin
|
||||
yield session
|
||||
ensure
|
||||
@cookies_store = old_cookies_store
|
||||
@options = @options.with_cookies(old_cookies_store)
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -81,22 +83,22 @@ module HTTPX
|
||||
private
|
||||
|
||||
def on_response(request, response)
|
||||
@cookies_store.set(request.origin, response.headers["set-cookie"])
|
||||
@options.cookies.set(request.origin, response.headers["set-cookie"])
|
||||
super
|
||||
end
|
||||
|
||||
def __build_req(*, _)
|
||||
request = super
|
||||
request.headers.cookies(@cookies_store[request.uri], request)
|
||||
request.headers.set_cookie(@options.cookies[request.uri])
|
||||
request
|
||||
end
|
||||
end
|
||||
|
||||
module HeadersMethods
|
||||
def cookies(jar, request)
|
||||
def set_cookie(jar)
|
||||
return unless jar
|
||||
|
||||
cookie_value = HTTP::Cookie.cookie_value(jar.cookies(request.uri))
|
||||
cookie_value = HTTP::Cookie.cookie_value(jar.cookies)
|
||||
return if cookie_value.empty?
|
||||
|
||||
add("cookie", cookie_value)
|
||||
|
@ -5,6 +5,16 @@ module HTTPX
|
||||
module DigestAuthentication
|
||||
DigestError = Class.new(Error)
|
||||
|
||||
def self.extra_options(options)
|
||||
Class.new(options.class) do
|
||||
def_option(:digest) do |digest|
|
||||
raise Error, ":digest must be a Digest" unless digest.is_a?(Digest)
|
||||
|
||||
digest
|
||||
end
|
||||
end.new(options)
|
||||
end
|
||||
|
||||
def self.load_dependencies(*)
|
||||
require "securerandom"
|
||||
require "digest"
|
||||
@ -12,28 +22,28 @@ module HTTPX
|
||||
|
||||
module InstanceMethods
|
||||
def digest_authentication(user, password)
|
||||
@_digest = Digest.new(user, password)
|
||||
self
|
||||
branch(default_options.with_digest(Digest.new(user, password)))
|
||||
end
|
||||
|
||||
alias_method :digest_auth, :digest_authentication
|
||||
|
||||
def request(*args, **options)
|
||||
return super unless @_digest
|
||||
|
||||
requests = __build_reqs(*args, options)
|
||||
probe_request = requests.first
|
||||
digest = probe_request.options.digest
|
||||
|
||||
return super unless digest
|
||||
|
||||
prev_response = wrap { __send_reqs(*probe_request, options).first }
|
||||
|
||||
unless prev_response.status == 401
|
||||
raise Error, "request doesn't require authentication (status: #{prev_response})"
|
||||
end
|
||||
raise Error, "request doesn't require authentication (status: #{prev_response.status})" unless prev_response.status == 401
|
||||
|
||||
probe_request.transition(:idle)
|
||||
|
||||
responses = []
|
||||
|
||||
while (request = requests.shift)
|
||||
token = @_digest.generate_header(request, prev_response)
|
||||
token = digest.generate_header(request, prev_response)
|
||||
request.headers["authorization"] = "Digest #{token}"
|
||||
response = if requests.empty?
|
||||
__send_reqs(*request, options).first
|
||||
@ -58,7 +68,7 @@ module HTTPX
|
||||
end
|
||||
|
||||
def generate_header(request, response, _iis = false)
|
||||
method = request.verb.to_s.upcase
|
||||
meth = request.verb.to_s.upcase
|
||||
www = response.headers["www-authenticate"]
|
||||
|
||||
# discard first token, it's Digest
|
||||
@ -104,7 +114,7 @@ module HTTPX
|
||||
end
|
||||
|
||||
ha1 = algorithm.hexdigest(a1)
|
||||
ha2 = algorithm.hexdigest("#{method}:#{uri}")
|
||||
ha2 = algorithm.hexdigest("#{meth}:#{uri}")
|
||||
request_digest = [ha1, nonce]
|
||||
request_digest.push(nc, cnonce, qop) if qop
|
||||
request_digest << ha2
|
||||
@ -140,6 +150,7 @@ module HTTPX
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
register_plugin :digest_authentication, DigestAuthentication
|
||||
end
|
||||
end
|
||||
|
@ -19,7 +19,7 @@ module HTTPX
|
||||
upgrade_request.headers["upgrade"] = "h2c"
|
||||
upgrade_request.headers.add("connection", "upgrade")
|
||||
upgrade_request.headers.add("connection", "http2-settings")
|
||||
upgrade_request.headers["http2-settings"] = HTTP2::Client.settings_header(upgrade_request.http2_settings)
|
||||
upgrade_request.headers["http2-settings"] = HTTP2::Client.settings_header(upgrade_request.options.http2_settings)
|
||||
upgrade_response = wrap { __send_reqs(*upgrade_request, h2c_options).first }
|
||||
|
||||
if upgrade_response.status == 101
|
||||
@ -54,13 +54,6 @@ module HTTPX
|
||||
end
|
||||
end
|
||||
|
||||
module RequestMethods
|
||||
def self.included(klass)
|
||||
klass.__send__(:attr_reader, :options)
|
||||
klass.def_delegator :@options, :http2_settings
|
||||
end
|
||||
end
|
||||
|
||||
class H2CParser < Connection::HTTP2
|
||||
def upgrade(request, response)
|
||||
@connection.send_connection_preface
|
||||
@ -78,10 +71,20 @@ module HTTPX
|
||||
end
|
||||
|
||||
module ConnectionMethods
|
||||
using URIExtensions
|
||||
|
||||
def match?(uri, options)
|
||||
return super unless uri.scheme == "http" && @options.fallback_protocol == "h2c"
|
||||
|
||||
super && options.fallback_protocol == "h2c"
|
||||
end
|
||||
|
||||
def coalescable?(connection)
|
||||
return super unless @options.fallback_protocol == "h2c" && @uri.scheme == "http"
|
||||
|
||||
@uri.origin == connection.uri.origin && connection.options.fallback_protocol == "h2c"
|
||||
end
|
||||
|
||||
def upgrade(request, response)
|
||||
@parser.reset if @parser
|
||||
@parser = H2CParser.new(@write_buffer, @options)
|
||||
|
@ -37,18 +37,20 @@ module HTTPX
|
||||
end
|
||||
end
|
||||
|
||||
def self.configure(klass, *)
|
||||
klass.plugin(:"proxy/http")
|
||||
klass.plugin(:"proxy/socks4")
|
||||
klass.plugin(:"proxy/socks5")
|
||||
end
|
||||
class << self
|
||||
def configure(klass, *)
|
||||
klass.plugin(:"proxy/http")
|
||||
klass.plugin(:"proxy/socks4")
|
||||
klass.plugin(:"proxy/socks5")
|
||||
end
|
||||
|
||||
def self.extra_options(options)
|
||||
Class.new(options.class) do
|
||||
def_option(:proxy) do |pr|
|
||||
Hash[pr]
|
||||
end
|
||||
end.new(options)
|
||||
def extra_options(options)
|
||||
Class.new(options.class) do
|
||||
def_option(:proxy) do |pr|
|
||||
Hash[pr]
|
||||
end
|
||||
end.new(options)
|
||||
end
|
||||
end
|
||||
|
||||
module InstanceMethods
|
||||
@ -71,19 +73,21 @@ module HTTPX
|
||||
end
|
||||
|
||||
def find_connection(request, options)
|
||||
return super unless options.respond_to?(:proxy)
|
||||
|
||||
uri = URI(request.uri)
|
||||
next_proxy = proxy_uris(uri, options)
|
||||
raise Error, "Failed to connect to proxy" unless next_proxy
|
||||
|
||||
proxy_options = options.merge(proxy: Parameters.new(**next_proxy))
|
||||
@pool.find_connection(uri, proxy_options) || build_connection(uri, proxy_options)
|
||||
pool.find_connection(uri, proxy_options) || build_connection(uri, proxy_options)
|
||||
end
|
||||
|
||||
def __build_connection(uri, options)
|
||||
proxy = options.proxy
|
||||
return super unless proxy
|
||||
|
||||
options.connection_class.new(uri, "tcp", proxy.uri, options)
|
||||
options.connection_class.new("tcp", uri, options)
|
||||
end
|
||||
|
||||
def fetch_response(request, connections, options)
|
||||
@ -107,9 +111,13 @@ module HTTPX
|
||||
module ConnectionMethods
|
||||
using URIExtensions
|
||||
|
||||
def initialize(request_uri, *args)
|
||||
super(*args)
|
||||
@origins = [request_uri.origin]
|
||||
def initialize(*)
|
||||
super
|
||||
return unless @options.proxy
|
||||
|
||||
# redefining the connection uri as the proxy's URI,
|
||||
# as this will be used as the tcp peer ip.
|
||||
@uri = @options.proxy.uri
|
||||
end
|
||||
|
||||
def match?(uri, options)
|
||||
@ -118,6 +126,13 @@ module HTTPX
|
||||
super && @options.proxy == options.proxy
|
||||
end
|
||||
|
||||
# should not coalesce connections here, as the IP is the IP of the proxy
|
||||
def coalescable?(*)
|
||||
return super unless @options.proxy
|
||||
|
||||
false
|
||||
end
|
||||
|
||||
def send(request, **args)
|
||||
return super unless @options.proxy
|
||||
return super unless connecting?
|
||||
|
@ -47,12 +47,8 @@ module HTTPX
|
||||
# and therefore, will share the connection.
|
||||
#
|
||||
if req.uri.scheme == "https"
|
||||
connect_request = ConnectRequest.new(req.uri)
|
||||
connect_request = ConnectRequest.new(req.uri, @options)
|
||||
|
||||
proxy_params = @options.proxy
|
||||
if proxy_params.authenticated?
|
||||
connect_request.headers["proxy-authentication"] = "Basic #{proxy_params.token_authentication}"
|
||||
end
|
||||
parser.send(connect_request)
|
||||
else
|
||||
transition(:connected)
|
||||
@ -106,8 +102,10 @@ module HTTPX
|
||||
end
|
||||
|
||||
class ConnectRequest < Request
|
||||
def initialize(uri, options = {})
|
||||
super(:connect, uri, options)
|
||||
def initialize(uri, options)
|
||||
super(:connect, uri, {})
|
||||
proxy_params = options.proxy
|
||||
@headers["proxy-authentication"] = "Basic #{proxy_params.token_authentication}" if proxy_params.authenticated?
|
||||
@headers.delete("accept")
|
||||
end
|
||||
|
||||
|
@ -10,6 +10,7 @@ module HTTPX
|
||||
VERSION = 4
|
||||
CONNECT = 1
|
||||
GRANTED = 90
|
||||
PROTOCOLS = %w[socks4 socks4a].freeze
|
||||
|
||||
Error = Class.new(Error)
|
||||
|
||||
@ -17,8 +18,7 @@ module HTTPX
|
||||
private
|
||||
|
||||
def transition(nextstate)
|
||||
return super unless @options.proxy &&
|
||||
(@options.proxy.uri.scheme == "socks4" || @options.proxy.uri.scheme == "socks4a")
|
||||
return super unless @options.proxy && PROTOCOLS.include?(@options.proxy.uri.scheme)
|
||||
|
||||
case nextstate
|
||||
when :connecting
|
||||
|
@ -79,7 +79,7 @@ module HTTPX
|
||||
transition(:authenticating)
|
||||
return
|
||||
when NONE
|
||||
on_socks5_error("no supported authorization methods")
|
||||
__on_socks5_error("no supported authorization methods")
|
||||
else
|
||||
transition(:negotiating)
|
||||
end
|
||||
@ -88,11 +88,11 @@ module HTTPX
|
||||
__socks5_check_version(version)
|
||||
return transition(:negotiating) if status == SUCCESS
|
||||
|
||||
on_socks5_error("socks authentication error: #{status}")
|
||||
__on_socks5_error("socks authentication error: #{status}")
|
||||
when :negotiating
|
||||
version, reply, = packet.unpack("CC")
|
||||
__socks5_check_version(version)
|
||||
on_socks5_error("socks5 negotiation error: #{reply}") unless reply == SUCCESS
|
||||
__on_socks5_error("socks5 negotiation error: #{reply}") unless reply == SUCCESS
|
||||
req, _ = @pending.first
|
||||
request_uri = req.uri
|
||||
@io = ProxySSL.new(@io, request_uri, @options) if request_uri.scheme == "https"
|
||||
@ -102,10 +102,10 @@ module HTTPX
|
||||
end
|
||||
|
||||
def __socks5_check_version(version)
|
||||
on_socks5_error("invalid SOCKS version (#{version})") if version != 5
|
||||
__on_socks5_error("invalid SOCKS version (#{version})") if version != 5
|
||||
end
|
||||
|
||||
def on_socks5_error(message)
|
||||
def __on_socks5_error(message)
|
||||
ex = Error.new(message)
|
||||
ex.set_backtrace(caller)
|
||||
on_error(ex)
|
||||
|
@ -29,10 +29,15 @@ module HTTPX
|
||||
def __send_reqs(*requests, options)
|
||||
request_options = @options.merge(options)
|
||||
|
||||
return super unless request_options.proxy
|
||||
|
||||
ssh_options = request_options.proxy
|
||||
ssh_uris = ssh_options.delete(:uri)
|
||||
ssh_username = ssh_options.delete(:username)
|
||||
ssh_uri = URI.parse(ssh_uris.shift)
|
||||
|
||||
return super unless ssh_uri.scheme == "ssh"
|
||||
|
||||
ssh_username = ssh_options.delete(:username)
|
||||
ssh_options[:port] ||= ssh_uri.port || 22
|
||||
if request_options.debug
|
||||
ssh_options[:verbose] = request_options.debug_level == 2 ? :debug : :info
|
||||
|
@ -161,7 +161,7 @@ module HTTPX
|
||||
resolver_type = Resolver.registry(resolver_type) if resolver_type.is_a?(Symbol)
|
||||
|
||||
@resolvers[resolver_type] ||= begin
|
||||
resolver = resolver_type.new(self, connection_options)
|
||||
resolver = resolver_type.new(connection_options)
|
||||
resolver.on(:resolve, &method(:on_resolver_connection))
|
||||
resolver.on(:error, &method(:on_resolver_error))
|
||||
resolver.on(:close) { on_resolver_close(resolver) }
|
||||
|
@ -34,6 +34,8 @@ module HTTPX
|
||||
|
||||
attr_reader :verb, :uri, :headers, :body, :state
|
||||
|
||||
attr_reader :options
|
||||
|
||||
attr_accessor :response
|
||||
|
||||
def_delegator :@body, :<<
|
||||
|
@ -26,8 +26,7 @@ module HTTPX
|
||||
|
||||
def_delegators :@resolver_connection, :to_io, :call, :interests, :close
|
||||
|
||||
def initialize(pool, options)
|
||||
@pool = pool
|
||||
def initialize(options)
|
||||
@options = Options.new(options)
|
||||
@resolver_options = Resolver::Options.new(DEFAULTS.merge(@options.resolver_options || {}))
|
||||
@_record_types = Hash.new { |types, host| types[host] = RECORD_TYPES.keys.dup }
|
||||
@ -61,11 +60,15 @@ module HTTPX
|
||||
|
||||
private
|
||||
|
||||
def pool
|
||||
Thread.current[:httpx_connection_pool] ||= Pool.new
|
||||
end
|
||||
|
||||
def resolver_connection
|
||||
@resolver_connection ||= @pool.find_connection(@uri, @options) || begin
|
||||
@resolver_connection ||= pool.find_connection(@uri, @options) || begin
|
||||
@building_connection = true
|
||||
connection = @options.connection_class.new("ssl", @uri, @options.merge(ssl: { alpn_protocols: %w[h2] }))
|
||||
@pool.init_connection(connection, @options)
|
||||
pool.init_connection(connection, @options)
|
||||
emit_addresses(connection, @uri_addresses)
|
||||
set_connection_callbacks(connection)
|
||||
@building_connection = false
|
||||
|
@ -35,7 +35,7 @@ module HTTPX
|
||||
|
||||
def_delegator :@connections, :empty?
|
||||
|
||||
def initialize(_, options)
|
||||
def initialize(options)
|
||||
@options = Options.new(options)
|
||||
@ns_index = 0
|
||||
@resolver_options = Resolver::Options.new(DEFAULTS.merge(@options.resolver_options || {}))
|
||||
|
@ -12,7 +12,7 @@ module HTTPX
|
||||
Resolv::DNS::EncodeError,
|
||||
Resolv::DNS::DecodeError].freeze
|
||||
|
||||
def initialize(_, options)
|
||||
def initialize(options)
|
||||
@options = Options.new(options)
|
||||
roptions = @options.resolver_options
|
||||
@state = :idle
|
||||
|
@ -21,10 +21,10 @@ module HTTPX
|
||||
|
||||
def_delegator :@request, :uri
|
||||
|
||||
def initialize(request, status, version, headers, options = {})
|
||||
@options = Options.new(options)
|
||||
@version = version
|
||||
def initialize(request, status, version, headers)
|
||||
@request = request
|
||||
@options = request.options
|
||||
@version = version
|
||||
@status = Integer(status)
|
||||
@headers = @options.headers_class.new(headers)
|
||||
@body = @options.response_body_class.new(self, threshold_size: @options.body_threshold_size,
|
||||
@ -256,9 +256,7 @@ module HTTPX
|
||||
|
||||
# rubocop:disable Style/MissingRespondToMissing
|
||||
def method_missing(meth, *, &block)
|
||||
if Response.public_method_defined?(meth)
|
||||
raise NoMethodError, "undefined response method `#{meth}' for error response"
|
||||
end
|
||||
raise NoMethodError, "undefined response method `#{meth}' for error response" if Response.public_method_defined?(meth)
|
||||
|
||||
super
|
||||
end
|
||||
|
@ -50,49 +50,44 @@ class HTTPX::Selector
|
||||
def initialize
|
||||
@readers = {}
|
||||
@writers = {}
|
||||
@lock = Mutex.new
|
||||
@__r__, @__w__ = IO.pipe
|
||||
@closed = false
|
||||
end
|
||||
|
||||
# deregisters +io+ from selectables.
|
||||
def deregister(io)
|
||||
@lock.synchronize do
|
||||
rmonitor = @readers.delete(io)
|
||||
wmonitor = @writers.delete(io)
|
||||
monitor = rmonitor || wmonitor
|
||||
monitor.close(false) if monitor
|
||||
end
|
||||
rmonitor = @readers.delete(io)
|
||||
wmonitor = @writers.delete(io)
|
||||
monitor = rmonitor || wmonitor
|
||||
monitor.close(false) if monitor
|
||||
end
|
||||
|
||||
# register +io+ for +interests+ events.
|
||||
def register(io, interests)
|
||||
readable = READABLE.include?(interests)
|
||||
writable = WRITABLE.include?(interests)
|
||||
@lock.synchronize do
|
||||
if readable
|
||||
monitor = @readers[io]
|
||||
if monitor
|
||||
monitor.interests = interests
|
||||
else
|
||||
monitor = Monitor.new(io, interests, self)
|
||||
end
|
||||
@readers[io] = monitor
|
||||
@writers.delete(io) unless writable
|
||||
if readable
|
||||
monitor = @readers[io]
|
||||
if monitor
|
||||
monitor.interests = interests
|
||||
else
|
||||
monitor = Monitor.new(io, interests, self)
|
||||
end
|
||||
if writable
|
||||
monitor = @writers[io]
|
||||
if monitor
|
||||
monitor.interests = interests
|
||||
else
|
||||
# reuse object
|
||||
monitor = readable ? @readers[io] : Monitor.new(io, interests, self)
|
||||
end
|
||||
@writers[io] = monitor
|
||||
@readers.delete(io) unless readable
|
||||
end
|
||||
monitor
|
||||
@readers[io] = monitor
|
||||
@writers.delete(io) unless writable
|
||||
end
|
||||
if writable
|
||||
monitor = @writers[io]
|
||||
if monitor
|
||||
monitor.interests = interests
|
||||
else
|
||||
# reuse object
|
||||
monitor = readable ? @readers[io] : Monitor.new(io, interests, self)
|
||||
end
|
||||
@writers[io] = monitor
|
||||
@readers.delete(io) unless readable
|
||||
end
|
||||
monitor
|
||||
end
|
||||
|
||||
# waits for read/write events for +interval+. Yields for monitors of
|
||||
@ -100,22 +95,16 @@ class HTTPX::Selector
|
||||
#
|
||||
def select(interval)
|
||||
begin
|
||||
r = nil
|
||||
w = nil
|
||||
@lock.synchronize do
|
||||
r = @readers.keys
|
||||
w = @writers.keys
|
||||
end
|
||||
r = @readers.keys
|
||||
w = @writers.keys
|
||||
r.unshift(@__r__)
|
||||
|
||||
readers, writers = IO.select(r, w, nil, interval)
|
||||
|
||||
raise HTTPX::TimeoutError.new(interval, "timed out while waiting on select") if readers.nil? && writers.nil?
|
||||
rescue IOError, SystemCallError
|
||||
@lock.synchronize do
|
||||
@readers.reject! { |io, _| io.closed? }
|
||||
@writers.reject! { |io, _| io.closed? }
|
||||
end
|
||||
@readers.reject! { |io, _| io.closed? }
|
||||
@writers.reject! { |io, _| io.closed? }
|
||||
retry
|
||||
end
|
||||
|
||||
|
@ -7,7 +7,6 @@ module HTTPX
|
||||
|
||||
def initialize(options = {}, &blk)
|
||||
@options = self.class.default_options.merge(options)
|
||||
@pool = Pool.new
|
||||
@responses = {}
|
||||
@keep_open = false
|
||||
wrap(&blk) if block_given?
|
||||
@ -26,7 +25,7 @@ module HTTPX
|
||||
end
|
||||
|
||||
def close(*args)
|
||||
@pool.close(*args)
|
||||
pool.close(*args)
|
||||
end
|
||||
|
||||
def request(*args, **options)
|
||||
@ -39,6 +38,10 @@ module HTTPX
|
||||
|
||||
private
|
||||
|
||||
def pool
|
||||
Thread.current[:httpx_connection_pool] ||= Pool.new
|
||||
end
|
||||
|
||||
def on_response(request, response)
|
||||
@responses[request] = response
|
||||
end
|
||||
@ -54,7 +57,7 @@ module HTTPX
|
||||
|
||||
def find_connection(request, options)
|
||||
uri = URI(request.uri)
|
||||
@pool.find_connection(uri, options) || build_connection(uri, options)
|
||||
pool.find_connection(uri, options) || build_connection(uri, options)
|
||||
end
|
||||
|
||||
def set_connection_callbacks(connection, options)
|
||||
@ -71,7 +74,7 @@ module HTTPX
|
||||
|
||||
def build_connection(uri, options)
|
||||
connection = __build_connection(uri, options)
|
||||
@pool.init_connection(connection, options)
|
||||
pool.init_connection(connection, options)
|
||||
set_connection_callbacks(connection, options)
|
||||
connection
|
||||
end
|
||||
@ -82,7 +85,7 @@ module HTTPX
|
||||
# altsvc already exists, somehow it wasn't advertised, probably noop
|
||||
return unless altsvc
|
||||
|
||||
connection = @pool.find_connection(alt_origin, options) || build_connection(alt_origin, options)
|
||||
connection = pool.find_connection(alt_origin, options) || build_connection(alt_origin, options)
|
||||
# advertised altsvc is the same origin being used, ignore
|
||||
return if connection == existing_connection
|
||||
|
||||
@ -166,12 +169,12 @@ module HTTPX
|
||||
loop do
|
||||
begin
|
||||
request = requests.first
|
||||
@pool.next_tick(timeout) until (response = fetch_response(request, connections, request_options))
|
||||
pool.next_tick(timeout) until (response = fetch_response(request, connections, request_options))
|
||||
|
||||
responses << response
|
||||
requests.shift
|
||||
|
||||
break if requests.empty? || @pool.empty?
|
||||
break if requests.empty? || pool.empty?
|
||||
end
|
||||
end
|
||||
responses
|
||||
|
@ -51,22 +51,20 @@ class HTTPSResolverTest < Minitest::Test
|
||||
|
||||
def build_connection(*)
|
||||
connection = super
|
||||
pool.expect(:find_connection, connection, [URI::HTTP, HTTPX::Options])
|
||||
resolver.instance_variable_set(:@resolver_connection, connection)
|
||||
connection
|
||||
end
|
||||
|
||||
def resolver(options = Options.new)
|
||||
@resolver ||= begin
|
||||
resolver = Resolver::HTTPS.new(pool, options)
|
||||
resolver = Resolver::HTTPS.new(options)
|
||||
resolver.extend(ResolverHelpers::ResolverExtensions)
|
||||
resolver.singleton_class.send(:attr_accessor, :pool)
|
||||
resolver.pool = @pool
|
||||
resolver
|
||||
end
|
||||
end
|
||||
|
||||
def pool
|
||||
@pool ||= Minitest::Mock.new
|
||||
end
|
||||
|
||||
def write_buffer
|
||||
resolver.instance_variable_get(:@resolver_connection)
|
||||
.instance_variable_get(:@pending)
|
||||
|
@ -52,16 +52,12 @@ class NativeResolverTest < Minitest::Test
|
||||
|
||||
def resolver(options = Options.new)
|
||||
@resolver ||= begin
|
||||
resolver = Resolver::Native.new(connection, options)
|
||||
resolver = Resolver::Native.new(options)
|
||||
resolver.extend(ResolverHelpers::ResolverExtensions)
|
||||
resolver
|
||||
end
|
||||
end
|
||||
|
||||
def connection
|
||||
@connection ||= Minitest::Mock.new
|
||||
end
|
||||
|
||||
def write_buffer
|
||||
resolver.instance_variable_get(:@write_buffer)
|
||||
end
|
||||
|
@ -16,9 +16,6 @@ class SystemResolverTest < Minitest::Test
|
||||
private
|
||||
|
||||
def resolver(options = Options.new)
|
||||
@resolver ||= begin
|
||||
connection = Minitest::Mock.new
|
||||
Resolver::System.new(connection, options)
|
||||
end
|
||||
@resolver ||= Resolver::System.new(options)
|
||||
end
|
||||
end
|
||||
|
@ -37,7 +37,7 @@ class SessionTest < Minitest::Test
|
||||
assert req_body.respond_to?(:foo), "request body methods haven't been added"
|
||||
assert req_body.foo == "request-body-foo", "request body method is unexpected"
|
||||
|
||||
response = session.response(nil, 200, "2.0", {})
|
||||
response = session.response(request, 200, "2.0", {})
|
||||
assert response.respond_to?(:foo), "response methods haven't been added"
|
||||
assert response.foo == "response-foo", "response method is unexpected"
|
||||
assert request.headers.respond_to?(:foo), "headers methods haven't been added"
|
||||
@ -62,7 +62,7 @@ class SessionTest < Minitest::Test
|
||||
attr_reader :options
|
||||
|
||||
def response(*args)
|
||||
@options.response_class.new(*args, @options)
|
||||
@options.response_class.new(*args)
|
||||
end
|
||||
end
|
||||
self::RequestClassMethods = Module.new do
|
||||
|
@ -11,7 +11,7 @@ fi
|
||||
export PATH=$GEM_HOME/bin:$BUNDLE_PATH/gems/bin:$PATH
|
||||
mkdir -p "$GEM_HOME" && chmod 777 "$GEM_HOME"
|
||||
gem install bundler -v="1.17.3" --no-doc --conservative
|
||||
cd /home && bundle install --jobs 4 && \
|
||||
cd /home && bundle install --jobs 4 --path .bundle && \
|
||||
bundle exec rake test:ci
|
||||
|
||||
RET=$?
|
||||
@ -24,7 +24,7 @@ fi
|
||||
|
||||
if [[ $RET = 0 ]] && [[ ${RUBY_VERSION:0:3} = "2.6" ]]; then
|
||||
bundle exec rake website_rdoc && \
|
||||
cd www && bundle install && \
|
||||
cd www && bundle install --jobs 4 --path ../vendor && \
|
||||
bundle exec jekyll build -d public
|
||||
fi
|
||||
|
||||
|
@ -22,7 +22,7 @@ module Requests
|
||||
session_uri = cookies_set_uri(session_cookies)
|
||||
session_response = session.get(session_uri)
|
||||
verify_status(session_response, 302)
|
||||
verify_cookies(session.cookies_store[URI(session_uri)], session_cookies)
|
||||
verify_cookies(session.cookies[URI(session_uri)], session_cookies)
|
||||
|
||||
# first request sets the session
|
||||
response = session.get(cookies_uri)
|
||||
|
Loading…
x
Reference in New Issue
Block a user