diff --git a/lib/httpx/connection/http2.rb b/lib/httpx/connection/http2.rb index 261480d0..ac02e9a9 100644 --- a/lib/httpx/connection/http2.rb +++ b/lib/httpx/connection/http2.rb @@ -11,8 +11,8 @@ module HTTPX MAX_CONCURRENT_REQUESTS = ::HTTP2::DEFAULT_MAX_CONCURRENT_STREAMS class Error < Error - def initialize(id, code) - super("stream #{id} closed with error: #{code}") + def initialize(id, error) + super("stream #{id} closed with error: #{error}") end end diff --git a/lib/httpx/io/ssl.rb b/lib/httpx/io/ssl.rb index fe6eafe5..53862410 100644 --- a/lib/httpx/io/ssl.rb +++ b/lib/httpx/io/ssl.rb @@ -9,7 +9,8 @@ module HTTPX # rubocop:disable Style/MutableConstant TLS_OPTIONS = { alpn_protocols: %w[h2 http/1.1].freeze } # https://github.com/jruby/jruby-openssl/issues/284 - TLS_OPTIONS[:verify_hostname] = true if RUBY_ENGINE == "jruby" + # TODO: remove when dropping support for jruby-openssl < 0.15.4 + TLS_OPTIONS[:verify_hostname] = true if RUBY_ENGINE == "jruby" && JOpenSSL::VERSION < "0.15.4" # rubocop:enable Style/MutableConstant TLS_OPTIONS.freeze diff --git a/lib/httpx/options.rb b/lib/httpx/options.rb index 0350869d..31ad12ab 100644 --- a/lib/httpx/options.rb +++ b/lib/httpx/options.rb @@ -147,13 +147,18 @@ module HTTPX end def freeze - super @origin.freeze @base_path.freeze @timeout.freeze @headers.freeze @addresses.freeze @supported_compression_formats.freeze + @ssl.freeze + @http2_settings.freeze + @pool_options.freeze + @resolver_options.freeze + @ip_families.freeze + super end def option_origin(value) @@ -226,17 +231,42 @@ module HTTPX Array(value) end + # number options + %i[ + max_concurrent_requests max_requests window_size buffer_size + body_threshold_size debug_level + ].each do |option| + class_eval(<<-OUT, __FILE__, __LINE__ + 1) + # converts +v+ into an Integer before setting the +#{option}+ option. + def option_#{option}(value) # def option_max_requests(v) + value = Integer(value) unless value.infinite? + raise TypeError, ":#{option} must be positive" unless value.positive? # raise TypeError, ":max_requests must be positive" unless value.positive? + + value + end + OUT + end + + # hashable options + %i[ssl http2_settings resolver_options pool_options].each do |option| + class_eval(<<-OUT, __FILE__, __LINE__ + 1) + # converts +v+ into an Hash before setting the +#{option}+ option. + def option_#{option}(value) # def option_ssl(v) + Hash[value] + end + OUT + end + %i[ - ssl http2_settings request_class response_class headers_class request_body_class response_body_class connection_class options_class pool_class pool_options - io fallback_protocol debug debug_level resolver_class resolver_options + io fallback_protocol debug resolver_class compress_request_body decompress_response_body persistent close_on_fork ].each do |method_name| class_eval(<<-OUT, __FILE__, __LINE__ + 1) - # sets +v+ as the value of #{method_name} + # sets +v+ as the value of the +#{method_name}+ option def option_#{method_name}(v); v; end # def option_smth(v); v; end OUT end diff --git a/lib/httpx/plugins/cookies/jar.rb b/lib/httpx/plugins/cookies/jar.rb index c9eddbe1..561a8aba 100644 --- a/lib/httpx/plugins/cookies/jar.rb +++ b/lib/httpx/plugins/cookies/jar.rb @@ -59,8 +59,6 @@ module HTTPX return @cookies.each(&blk) unless uri - uri = URI(uri) - now = Time.now tpath = uri.path diff --git a/lib/httpx/plugins/cookies/set_cookie_parser.rb b/lib/httpx/plugins/cookies/set_cookie_parser.rb index 894954e8..d0fe76fd 100644 --- a/lib/httpx/plugins/cookies/set_cookie_parser.rb +++ b/lib/httpx/plugins/cookies/set_cookie_parser.rb @@ -83,7 +83,7 @@ module HTTPX scanner.skip(RE_WSP) name, value = scan_name_value(scanner, true) - value = nil if name.empty? + value = nil if name && name.empty? attrs = {} @@ -98,15 +98,18 @@ module HTTPX aname, avalue = scan_name_value(scanner, true) - next if aname.empty? || value.nil? + next if (aname.nil? || aname.empty?) || value.nil? aname.downcase! case aname when "expires" + next unless avalue + # RFC 6265 5.2.1 - (avalue &&= Time.parse(avalue)) || next + (avalue = Time.parse(avalue)) || next when "max-age" + next unless avalue # RFC 6265 5.2.2 next unless /\A-?\d+\z/.match?(avalue) @@ -119,7 +122,7 @@ module HTTPX # RFC 6265 5.2.4 # A relative path must be ignored rather than normalizing it # to "/". - next unless avalue.start_with?("/") + next unless avalue && avalue.start_with?("/") when "secure", "httponly" # RFC 6265 5.2.5, 5.2.6 avalue = true diff --git a/lib/httpx/plugins/stream_bidi.rb b/lib/httpx/plugins/stream_bidi.rb index b497d54e..7aff2d9d 100644 --- a/lib/httpx/plugins/stream_bidi.rb +++ b/lib/httpx/plugins/stream_bidi.rb @@ -155,6 +155,9 @@ module HTTPX @pipe_read.close @closed = true end + + # noop (the owner connection will take of it) + def handle_socket_timeout(interval); end end class << self diff --git a/lib/httpx/resolver.rb b/lib/httpx/resolver.rb index 4837be6b..ff9efe8b 100644 --- a/lib/httpx/resolver.rb +++ b/lib/httpx/resolver.rb @@ -92,8 +92,8 @@ module HTTPX end ips = entries.flat_map do |address| - if address.key?("alias") - lookup(address["alias"], lookups, ttl) + if (als = address["alias"]) + lookup(als, lookups, ttl) else IPAddr.new(address["data"]) end diff --git a/lib/httpx/resolver/https.rb b/lib/httpx/resolver/https.rb index 05b1a9e1..376b11ef 100644 --- a/lib/httpx/resolver/https.rb +++ b/lib/httpx/resolver/https.rb @@ -6,6 +6,10 @@ require "forwardable" require "httpx/base64" module HTTPX + # Implementation of a DoH name resolver (https://www.youtube.com/watch?v=unMXvnY2FNM). + # It wraps an HTTPX::Connection object which integrates with the main session in the + # same manner as other performed HTTP requests. + # class Resolver::HTTPS < Resolver::Resolver extend Forwardable using URIExtensions @@ -26,14 +30,13 @@ module HTTPX use_get: false, }.freeze - def_delegators :@resolver_connection, :state, :connecting?, :to_io, :call, :close, :terminate, :inflight? + def_delegators :@resolver_connection, :state, :connecting?, :to_io, :call, :close, :terminate, :inflight?, :handle_socket_timeout def initialize(_, options) super @resolver_options = DEFAULTS.merge(@options.resolver_options) @queries = {} @requests = {} - @connections = [] @uri = URI(@resolver_options[:uri]) @uri_addresses = nil @resolver = Resolv::DNS.new @@ -74,7 +77,11 @@ module HTTPX private - def resolve(connection = @connections.first, hostname = nil) + def resolve(connection = nil, hostname = nil) + @connections.shift until @connections.empty? || @connections.first.state != :closed + + connection ||= @connections.first + return unless connection hostname ||= @queries.key(connection) diff --git a/lib/httpx/resolver/native.rb b/lib/httpx/resolver/native.rb index 61d6366e..cbdac28a 100644 --- a/lib/httpx/resolver/native.rb +++ b/lib/httpx/resolver/native.rb @@ -4,6 +4,9 @@ require "forwardable" require "resolv" module HTTPX + # Implements a pure ruby name resolver, which abides by the Selectable API. + # It delegates DNS payload encoding/decoding to the +resolv+ stlid gem. + # class Resolver::Native < Resolver::Resolver extend Forwardable using URIExtensions @@ -34,7 +37,6 @@ module HTTPX @search = Array(@resolver_options[:search]).map { |srch| srch.scan(/[^.]+/) } @_timeouts = Array(@resolver_options[:timeouts]) @timeouts = Hash.new { |timeouts, host| timeouts[host] = @_timeouts.dup } - @connections = [] @name = nil @queries = {} @read_buffer = "".b @@ -505,7 +507,7 @@ module HTTPX end while (connection = @connections.shift) - emit_resolve_error(connection, host, error) + emit_resolve_error(connection, connection.peer.host, error) end end end diff --git a/lib/httpx/resolver/resolver.rb b/lib/httpx/resolver/resolver.rb index 3dab43f7..ce9d7f30 100644 --- a/lib/httpx/resolver/resolver.rb +++ b/lib/httpx/resolver/resolver.rb @@ -4,6 +4,9 @@ require "resolv" require "ipaddr" module HTTPX + # Base class for all internal internet name resolvers. It handles basic blocks + # from the Selectable API. + # class Resolver::Resolver include Callbacks include Loggable @@ -36,6 +39,7 @@ module HTTPX @family = family @record_type = RECORD_TYPES[family] @options = options + @connections = [] set_resolver_callbacks end diff --git a/lib/httpx/resolver/system.rb b/lib/httpx/resolver/system.rb index 9b25a708..2cad9f58 100644 --- a/lib/httpx/resolver/system.rb +++ b/lib/httpx/resolver/system.rb @@ -3,6 +3,15 @@ require "resolv" module HTTPX + # Implementation of a synchronous name resolver which relies on the system resolver, + # which is lib'c getaddrinfo function (abstracted in ruby via Addrinfo.getaddrinfo). + # + # Its main advantage is relying on the reference implementation for name resolution + # across most/all OSs which deploy ruby (it's what TCPSocket also uses), its main + # disadvantage is the inability to set timeouts / check socket for readiness events, + # hence why it relies on using the Timeout module, which poses a lot of problems for + # the selector loop, specially when network is unstable. + # class Resolver::System < Resolver::Resolver using URIExtensions @@ -23,14 +32,13 @@ module HTTPX attr_reader :state def initialize(options) - super(nil, options) + super(0, options) @resolver_options = @options.resolver_options resolv_options = @resolver_options.dup timeouts = resolv_options.delete(:timeouts) || Resolver::RESOLVE_TIMEOUT @_timeouts = Array(timeouts) @timeouts = Hash.new { |tims, host| tims[host] = @_timeouts.dup } resolv_options.delete(:cache) - @connections = [] @queries = [] @ips = [] @pipe_mutex = Thread::Mutex.new @@ -100,7 +108,14 @@ module HTTPX def handle_socket_timeout(interval) error = HTTPX::ResolveTimeoutError.new(interval, "timed out while waiting on select") error.set_backtrace(caller) - on_error(error) + @queries.each do |host, connection| + @connections.delete(connection) + emit_resolve_error(connection, host, error) + end + + while (connection = @connections.shift) + emit_resolve_error(connection, connection.peer.host, error) + end end private @@ -131,19 +146,22 @@ module HTTPX case event when DONE *pair, addrs = @pipe_mutex.synchronize { @ips.pop } - @queries.delete(pair) - _, connection = pair - @connections.delete(connection) + if pair + @queries.delete(pair) + family, connection = pair + @connections.delete(connection) - family, connection = pair - catch(:coalesced) { emit_addresses(connection, family, addrs) } + catch(:coalesced) { emit_addresses(connection, family, addrs) } + end when ERROR *pair, error = @pipe_mutex.synchronize { @ips.pop } - @queries.delete(pair) - @connections.delete(connection) + if pair && error + @queries.delete(pair) + @connections.delete(connection) - _, connection = pair - emit_resolve_error(connection, connection.peer.host, error) + _, connection = pair + emit_resolve_error(connection, connection.peer.host, error) + end end end @@ -152,11 +170,16 @@ module HTTPX resolve end - def resolve(connection = @connections.first) + def resolve(connection = nil, hostname = nil) + @connections.shift until @connections.empty? || @connections.first.state != :closed + + connection ||= @connections.first + raise Error, "no URI to resolve" unless connection + return unless @queries.empty? - hostname = connection.peer.host + hostname ||= connection.peer.host scheme = connection.origin.scheme log do "resolver: resolve IDN #{connection.peer.non_ascii_hostname} as #{hostname}" diff --git a/sig/buffer.rbs b/sig/buffer.rbs index 3356dc05..560e4ef7 100644 --- a/sig/buffer.rbs +++ b/sig/buffer.rbs @@ -14,7 +14,7 @@ module HTTPX def capacity: () -> Integer # delegated - def <<: (string data) -> String + def <<: (String data) -> String def empty?: () -> bool def bytesize: () -> (Integer | Float) def clear: () -> void diff --git a/sig/chainable.rbs b/sig/chainable.rbs index 8a9d0faa..087e8fc5 100644 --- a/sig/chainable.rbs +++ b/sig/chainable.rbs @@ -21,8 +21,9 @@ module HTTPX | (:cookies, ?options) -> Plugins::sessionCookies | (:expect, ?options) -> Session | (:follow_redirects, ?options) -> Plugins::sessionFollowRedirects - | (:upgrade, ?options) -> Session - | (:h2c, ?options) -> Session + | (:upgrade, ?options) -> Plugins::sessionUpgrade + | (:h2c, ?options) -> Plugins::sessionUpgrade + | (:h2, ?options) -> Plugins::sessionUpgrade | (:persistent, ?options) -> Plugins::sessionPersistent | (:proxy, ?options) -> (Plugins::sessionProxy & Plugins::httpProxy) | (:push_promise, ?options) -> Plugins::sessionPushPromise diff --git a/sig/connection.rbs b/sig/connection.rbs index ef72295b..b1ab6ee2 100644 --- a/sig/connection.rbs +++ b/sig/connection.rbs @@ -20,6 +20,7 @@ module HTTPX attr_reader type: io_type + attr_reader io: TCP | SSL | UNIX | nil attr_reader origin: http_uri attr_reader origins: Array[String] attr_reader state: Symbol @@ -39,7 +40,6 @@ module HTTPX @keep_alive_timeout: Numeric? @timeout: Numeric? @current_timeout: Numeric? - @io: TCP | SSL | UNIX @parser: Object & _Parser @connected_at: Float @response_received_at: Float diff --git a/sig/connection/http2.rbs b/sig/connection/http2.rbs index 84943802..99c4905b 100644 --- a/sig/connection/http2.rbs +++ b/sig/connection/http2.rbs @@ -16,6 +16,8 @@ module HTTPX @drains: Hash[Request, String] @pings: Array[String] @buffer: Buffer + @handshake_completed: bool + @wait_for_handshake: bool def interests: () -> io_interests? @@ -96,12 +98,15 @@ module HTTPX def on_pong: (string ping) -> void class Error < ::HTTPX::Error + def initialize: (Integer id, Symbol | StandardError error) -> void end class GoawayError < Error + def initialize: () -> void end class PingError < Error + def initialize: () -> void end end end \ No newline at end of file diff --git a/sig/options.rbs b/sig/options.rbs index 789968d6..eaddff8a 100644 --- a/sig/options.rbs +++ b/sig/options.rbs @@ -13,6 +13,7 @@ module HTTPX KEEP_ALIVE_TIMEOUT: Integer SETTINGS_TIMEOUT: Integer CLOSE_HANDSHAKE_TIMEOUT: Integer + SET_TEMPORARY_NAME: ^(Module mod, ?Symbol pl) -> void DEFAULT_OPTIONS: Hash[Symbol, untyped] REQUEST_BODY_IVARS: Array[Symbol] @@ -89,6 +90,8 @@ module HTTPX attr_reader response_body_class: singleton(Response::Body) + attr_reader options_class: singleton(Options) + attr_reader resolver_class: Symbol | Class attr_reader ssl: Hash[Symbol, untyped] diff --git a/sig/plugins/cookies.rbs b/sig/plugins/cookies.rbs index 765c9c03..fd146293 100644 --- a/sig/plugins/cookies.rbs +++ b/sig/plugins/cookies.rbs @@ -1,6 +1,8 @@ module HTTPX module Plugins module Cookies + type cookie_attributes = Hash[Symbol | String, top] + type jar = Jar | _Each[Jar::cookie] interface _CookieOptions diff --git a/sig/plugins/cookies/cookie.rbs b/sig/plugins/cookies/cookie.rbs index 47e9cb06..d3dbfd11 100644 --- a/sig/plugins/cookies/cookie.rbs +++ b/sig/plugins/cookies/cookie.rbs @@ -1,7 +1,5 @@ module HTTPX module Plugins::Cookies - type cookie_attributes = Hash[Symbol | String, top] - class Cookie include Comparable @@ -33,7 +31,7 @@ module HTTPX def cookie_value: () -> String alias to_s cookie_value - def valid_for_uri?: (uri) -> bool + def valid_for_uri?: (http_uri uri) -> bool def self.new: (Cookie) -> instance | (cookie_attributes) -> instance diff --git a/sig/plugins/cookies/jar.rbs b/sig/plugins/cookies/jar.rbs index 463702a2..80c881eb 100644 --- a/sig/plugins/cookies/jar.rbs +++ b/sig/plugins/cookies/jar.rbs @@ -11,12 +11,12 @@ module HTTPX def add: (Cookie name, ?String path) -> void - def []: (uri) -> Array[Cookie] + def []: (http_uri) -> Array[Cookie] - def each: (?uri?) { (Cookie) -> void } -> void - | (?uri?) -> Enumerable[Cookie] + def each: (?http_uri?) { (Cookie) -> void } -> void + | (?http_uri?) -> Enumerable[Cookie] - def merge: (_Each[cookie] cookies) -> instance + def merge: (_Each[cookie] cookies) -> self private diff --git a/sig/plugins/cookies/set_cookie_parser.rbs b/sig/plugins/cookies/set_cookie_parser.rbs new file mode 100644 index 00000000..7499ce3e --- /dev/null +++ b/sig/plugins/cookies/set_cookie_parser.rbs @@ -0,0 +1,22 @@ +module HTTPX + module Plugins::Cookies + module SetCookieParser + RE_WSP: Regexp + + RE_NAME: Regexp + + RE_BAD_CHAR: Regexp + + RE_COOKIE_COMMA: Regexp + + def self?.call: (String set_cookie) { (String name, String value, cookie_attributes attrs) -> void } -> void + + def self?.scan_dquoted: (StringScanner scanner) -> String + + def self?.scan_value: (StringScanner scanner, ?bool comma_as_separator) -> String + + def self?.scan_name_value: (StringScanner scanner, ?bool comma_as_separator) -> [String?, String?] + + end + end +end \ No newline at end of file diff --git a/sig/plugins/proxy.rbs b/sig/plugins/proxy.rbs index 31b433b8..60d6b7bb 100644 --- a/sig/plugins/proxy.rbs +++ b/sig/plugins/proxy.rbs @@ -49,6 +49,10 @@ module HTTPX def self.extra_options: (Options) -> (Options & _ProxyOptions) + module ConnectionMethods + @proxy_uri: generic_uri + end + module InstanceMethods @__proxy_uris: Array[generic_uri] diff --git a/sig/plugins/proxy/http.rbs b/sig/plugins/proxy/http.rbs index a0977188..1ee67c45 100644 --- a/sig/plugins/proxy/http.rbs +++ b/sig/plugins/proxy/http.rbs @@ -16,6 +16,9 @@ module HTTPX def __http_on_connect: (top, Response) -> void end + module ProxyParser + end + class ConnectRequest < Request def initialize: (generic_uri uri, Options options) -> void end diff --git a/sig/plugins/stream.rbs b/sig/plugins/stream.rbs index abbf73ad..4dfb5358 100644 --- a/sig/plugins/stream.rbs +++ b/sig/plugins/stream.rbs @@ -16,6 +16,9 @@ module HTTPX def stream: () -> StreamResponse? end + module ResponseBodyMethods + @stream: StreamResponse? + end end type sessionStream = Session & Stream::InstanceMethods diff --git a/sig/plugins/upgrade.rbs b/sig/plugins/upgrade.rbs index 61a549fc..85a3497b 100644 --- a/sig/plugins/upgrade.rbs +++ b/sig/plugins/upgrade.rbs @@ -13,6 +13,9 @@ module HTTPX def self.extra_options: (Options) -> (Options & _UpgradeOptions) + module InstanceMethods + end + module ConnectionMethods attr_reader upgrade_protocol: String? attr_reader hijacked: boolish @@ -20,5 +23,7 @@ module HTTPX def hijack_io: () -> void end end + + type sessionUpgrade = Session & Upgrade::InstanceMethods end end diff --git a/sig/plugins/upgrade/h2.rbs b/sig/plugins/upgrade/h2.rbs new file mode 100644 index 00000000..bc590fc2 --- /dev/null +++ b/sig/plugins/upgrade/h2.rbs @@ -0,0 +1,9 @@ +module HTTPX + module Plugins + module H2 + module ConnectionMethods + def upgrade_to_h2: () -> void + end + end + end +end \ No newline at end of file diff --git a/sig/punycode.rbs b/sig/punycode.rbs new file mode 100644 index 00000000..790c236f --- /dev/null +++ b/sig/punycode.rbs @@ -0,0 +1,5 @@ +module HTTPX + module Punycode + def self?.encode_hostname: (String) -> String + end +end \ No newline at end of file diff --git a/sig/resolver.rbs b/sig/resolver.rbs index 8ec7afaa..0353aa81 100644 --- a/sig/resolver.rbs +++ b/sig/resolver.rbs @@ -2,22 +2,26 @@ module HTTPX type ipaddr = IPAddr | String module Resolver - RESOLVE_TIMEOUT: Array[Integer] - - @lookup_mutex: Thread::Mutex - type dns_resource = singleton(Resolv::DNS::Resource) type dns_result = { "name" => String, "TTL" => Numeric, "alias" => String } | { "name" => String, "TTL" => Numeric, "data" => String } + RESOLVE_TIMEOUT: Array[Integer] + + self.@lookup_mutex: Thread::Mutex + self.@lookups: Hash[String, Array[dns_result]] + self.@identifier_mutex: Thread::Mutex + self.@identifier: Integer + self.@system_resolver: Resolv::Hosts + type dns_decoding_response = [:ok, Array[dns_result]] | [:decode_error, Resolv::DNS::DecodeError] | [:dns_error, Integer] | Symbol - def nolookup_resolve: (String hostname) -> Array[IPAddr] + def self?.nolookup_resolve: (String hostname) -> Array[IPAddr]? - def ip_resolve: (String hostname) -> Array[IPAddr]? + def self?.ip_resolve: (String hostname) -> Array[IPAddr]? - def system_resolve: (String hostname) -> Array[IPAddr]? + def self?.system_resolve: (String hostname) -> Array[IPAddr]? def self?.resolver_for: (:native resolver_type) -> singleton(Native) | (:system resolver_type) -> singleton(System) | diff --git a/sig/resolver/https.rbs b/sig/resolver/https.rbs index 4aca2513..0d5ecf3c 100644 --- a/sig/resolver/https.rbs +++ b/sig/resolver/https.rbs @@ -1,6 +1,8 @@ module HTTPX module Resolver class HTTPS < Resolver + extend Forwardable + NAMESERVER: String DEFAULTS: Hash[Symbol, untyped] @@ -9,6 +11,7 @@ module HTTPX attr_reader family: ip_family @options: Options + @queries: Hash[String, Connection] @requests: Hash[Request, String] @connections: Array[Connection] @uri: http_uri @@ -26,8 +29,6 @@ module HTTPX def resolver_connection: () -> Connection - def resolve: (?Connection connection, ?String? hostname) -> void - def on_response: (Request, response) -> void def parse: (Request request, Response response) -> void diff --git a/sig/resolver/native.rbs b/sig/resolver/native.rbs index 7c4f679c..458b4935 100644 --- a/sig/resolver/native.rbs +++ b/sig/resolver/native.rbs @@ -16,6 +16,7 @@ module HTTPX @search: Array[String] @_timeouts: Array[Numeric] @timeouts: Hash[String, Array[Numeric]] + @queries: Hash[String, Connection] @connections: Array[Connection] @read_buffer: String @write_buffer: Buffer @@ -54,8 +55,6 @@ module HTTPX def parse: (String) -> void - def resolve: (?Connection connection, ?String hostname) -> void - def generate_candidates: (String) -> Array[String] def build_socket: () -> (UDP | TCP) diff --git a/sig/resolver/resolver.rbs b/sig/resolver/resolver.rbs index 9029d8aa..2799156e 100644 --- a/sig/resolver/resolver.rbs +++ b/sig/resolver/resolver.rbs @@ -4,7 +4,11 @@ module HTTPX include Callbacks include Loggable - RECORD_TYPES: Hash[Integer, singleton(Resolv::DNS::Resource)] + include _Selectable + + RECORD_TYPES: Hash[ip_family, singleton(Resolv::DNS::Resource)] + + FAMILY_TYPES: Hash[singleton(Resolv::DNS::Resource), String] attr_reader family: ip_family @@ -18,8 +22,8 @@ module HTTPX @record_type: singleton(Resolv::DNS::Resource) @resolver_options: Hash[Symbol, untyped] - @queries: Hash[String, Connection] @system_resolver: Resolv::Hosts + @connections: Array[Connection] def close: () -> void @@ -33,11 +37,15 @@ module HTTPX def emit_addresses: (Connection connection, ip_family family, Array[IPAddr], ?bool early_resolve) -> void + def self.multi?: () -> bool + private + def resolve: (?Connection connection, ?String hostname) -> void + def emit_resolved_connection: (Connection connection, Array[IPAddr] addresses, bool early_resolve) -> void - def initialize: (ip_family? family, Options options) -> void + def initialize: (ip_family family, Options options) -> void def early_resolve: (Connection connection, ?hostname: String) -> bool diff --git a/sig/resolver/system.rbs b/sig/resolver/system.rbs index e8cffecc..ba174931 100644 --- a/sig/resolver/system.rbs +++ b/sig/resolver/system.rbs @@ -2,16 +2,33 @@ module HTTPX module Resolver class System < Resolver RESOLV_ERRORS: Array[singleton(StandardError)] # ResolvError + DONE: 1 + ERROR: 2 @resolver: Resolv::DNS + @_timeouts: Array[Numeric] + @timeouts: Hash[String, Array[Numeric]] + @queries: Array[[ip_family, Connection]] + @ips: Array[[ip_family, Connection, (Array[Addrinfo] | StandardError)]] + @pipe_mutex: Thread::Mutex + @pipe_read: ::IO + @pipe_write: ::IO - attr_reader family: nil + attr_reader state: Symbol def <<: (Connection) -> void private - def initialize: (options options) -> void + def transition: (Symbol nextstate) -> void + + def consume: () -> void + + def async_resolve: (Connection connection, String hostname, String scheme) -> void + + def __addrinfo_resolve: (String host, String scheme) -> Array[Addrinfo] + + def initialize: (Options options) -> void end end end \ No newline at end of file diff --git a/sig/selector.rbs b/sig/selector.rbs index 31925e14..238a3210 100644 --- a/sig/selector.rbs +++ b/sig/selector.rbs @@ -9,6 +9,8 @@ module HTTPX def interests: () -> io_interests? def timeout: () -> Numeric? + + def handle_socket_timeout: (Numeric interval) -> void end class Selector diff --git a/sig/session.rbs b/sig/session.rbs index 6d9bb8c8..548bae8a 100644 --- a/sig/session.rbs +++ b/sig/session.rbs @@ -96,5 +96,5 @@ module HTTPX end end - OriginalSession: singleton(Session) + S: singleton(Session) end \ No newline at end of file diff --git a/test/cookie_jar_test.rb b/test/cookie_jar_test.rb index 54fd25ca..33aba143 100644 --- a/test/cookie_jar_test.rb +++ b/test/cookie_jar_test.rb @@ -47,17 +47,17 @@ class CookieJarTest < Minitest::Test domain_jar = HTTPX::Plugins::Cookies::Jar.new domain_jar.parse(%(a=b; Path=/; Domain=.google.com)) assert domain_jar[jar_cookies_uri].empty? - assert !domain_jar["http://www.google.com/"].empty? + assert !domain_jar[URI("http://www.google.com/")].empty? ipv4_domain_jar = HTTPX::Plugins::Cookies::Jar.new ipv4_domain_jar.parse(%(a=b; Path=/; Domain=137.1.0.12)) - assert ipv4_domain_jar["http://www.google.com/"].empty? - assert !ipv4_domain_jar["http://137.1.0.12/"].empty? + assert ipv4_domain_jar[URI("http://www.google.com/")].empty? + assert !ipv4_domain_jar[URI("http://137.1.0.12/")].empty? ipv6_domain_jar = HTTPX::Plugins::Cookies::Jar.new ipv6_domain_jar.parse(%(a=b; Path=/; Domain=[fe80::1])) - assert ipv6_domain_jar["http://www.google.com/"].empty? - assert !ipv6_domain_jar["http://[fe80::1]/"].empty? + assert ipv6_domain_jar[URI("http://www.google.com/")].empty? + assert !ipv6_domain_jar[URI("http://[fe80::1]/")].empty? # Test duplicate dup_jar = HTTPX::Plugins::Cookies::Jar.new @@ -110,6 +110,6 @@ class CookieJarTest < Minitest::Test private def jar_cookies_uri(path = "/cookies", scheme: "http") - "#{scheme}://example.com#{path}" + URI("#{scheme}://example.com#{path}") end end diff --git a/test/support/ci/build.sh b/test/support/ci/build.sh index 313693ed..7ff30099 100755 --- a/test/support/ci/build.sh +++ b/test/support/ci/build.sh @@ -66,7 +66,7 @@ if [[ "$RUBY_ENGINE" = "ruby" ]] && [[ ${RUBY_VERSION:0:3} = "3.4" ]] && [[ ! $R export RUBYOPT="$RUBYOPT -rbundler/setup -rrbs/test/setup" export RBS_TEST_RAISE=true export RBS_TEST_LOGLEVEL=error - export RBS_TEST_OPT="-Isig -rset -rforwardable -ruri -rjson -ripaddr -rpathname -rtime -rtimeout -rresolv -rsocket -ropenssl -rbase64 -rzlib -rcgi -rdigest -rhttp-2" + export RBS_TEST_OPT="-Isig -rset -rforwardable -ruri -rjson -ripaddr -rpathname -rtime -rtimeout -rresolv -rsocket -ropenssl -rbase64 -rzlib -rcgi -rdigest -rstrscan -rhttp-2" export RBS_TEST_TARGET="HTTP*" fi diff --git a/test/support/requests/plugins/cookies.rb b/test/support/requests/plugins/cookies.rb index 1a4e69b3..7d37206f 100644 --- a/test/support/requests/plugins/cookies.rb +++ b/test/support/requests/plugins/cookies.rb @@ -130,7 +130,7 @@ module Requests end def cookies_set_uri(cookies) - build_uri("/cookies/set?#{URI.encode_www_form(cookies)}") + URI(build_uri("/cookies/set?#{URI.encode_www_form(cookies)}")) end def verify_cookies(jar, cookies)