Compare commits

..

4 Commits

Author SHA1 Message Date
HoneyryderChuck
7ed7eec2dc bumped version to 0.22.0 2022-12-21 20:34:13 +00:00
HoneyryderChuck
777b505900 Merge branch 'issue-206' into 'master'
Finishing Happy Eyeballs v2

Closes #206

See merge request os85/httpx!225
2022-12-21 20:27:49 +00:00
HoneyryderChuck
1070cfa0ae limiting spy version due to regression 2022-12-21 20:06:29 +00:00
HoneyryderChuck
c9527f962d finishing Happy Eyeballs v2
Until now, httpx was issuing concurrent DNS requests, but it'd only
start connecting to the first, and then on the following by the right
order, but sequentially.

With this change, httpx will now continue the process by connecting
concurrently to both IPv6 and IPv4, and close the other connection once
one is established. This means both TCP and TLS (when applicable) need
to succeed before the second connection is cancelled.
2022-12-21 01:04:05 +00:00
8 changed files with 40 additions and 10 deletions

View File

@ -17,7 +17,7 @@ group :test do
gem "minitest-proveit"
gem "ruby-ntlm"
gem "sentry-ruby" if RUBY_VERSION >= "2.4.0"
gem "spy"
gem "spy", "< 1.0.4" # TODO: remove this once upstream fixes bug
if RUBY_VERSION < "2.3.0"
gem "webmock", "< 3.15.0"
else

View File

@ -0,0 +1,13 @@
# 0.22.0
## Improvements
### Happy Eyeballs v2 finalized
Until now, httpx was issuing concurrent DNS requests, but it'd only start connecting to the first, and then on the following by the right order, but sequentially.
`httpx` will now establish connections concurrently to both IPv6 and IPv4 addresses of a given domain; the first one to succeed terminates the other. Successful connection means completion of both TCP and TLS (when applicable) handshakes.
### HTTPX::Response::Body#encoding
A new method, `#encoding`, can be called on response bodies. It'll return the encoding of the response payload.

View File

@ -76,6 +76,13 @@ module HTTPX
self.addresses = @options.addresses if @options.addresses
end
def clone_new_connection
new_conn = self.class.new(@type, @origin, @options)
once(:open, &new_conn.method(:reset))
new_conn.once(:open, &method(:close))
new_conn
end
# this is a semi-private method, to be used by the resolver
# to initiate the io object.
def addresses=(addrs)

View File

@ -129,6 +129,7 @@ module HTTPX
end
def on_resolver_connection(connection)
@connections << connection unless @connections.include?(connection)
found_connection = @connections.find do |ch|
ch != connection && ch.mergeable?(connection)
end

View File

@ -69,7 +69,8 @@ module HTTPX
@building_connection = true
connection = @options.connection_class.new("ssl", @uri, @options.merge(ssl: { alpn_protocols: %w[h2] }))
@pool.init_connection(connection, @options)
emit_addresses(connection, @family, @uri_addresses)
# only explicity emit addresses if connection didn't pre-resolve, i.e. it's not an IP.
emit_addresses(connection, @family, @uri_addresses) unless connection.addresses
@building_connection = false
connection
end

View File

@ -63,20 +63,26 @@ module HTTPX
log { "resolver: A response, applying resolution delay..." }
@pool.after(0.05) do
# double emission check
unless connection.addresses && addresses.intersect?(connection.addresses)
connection.addresses = addresses
emit(:resolve, connection)
end
emit_resolved_connection(connection, addresses) unless connection.addresses && addresses.intersect?(connection.addresses)
end
else
connection.addresses = addresses
emit(:resolve, connection)
emit_resolved_connection(connection, addresses)
end
end
private
def emit_resolved_connection(connection, addresses)
if connection.io && connection.connecting? && @pool
new_connection = connection.clone_new_connection
@pool.init_connection(new_connection, connection.options)
connection = new_connection
end
connection.addresses = addresses
emit(:resolve, connection)
end
def early_resolve(connection, hostname: connection.origin.host)
addresses = @resolver_options[:cache] && (connection.addresses || HTTPX::Resolver.nolookup_resolve(hostname))

View File

@ -1,5 +1,5 @@
# frozen_string_literal: true
module HTTPX
VERSION = "0.21.1"
VERSION = "0.22.0"
end

View File

@ -36,6 +36,8 @@ module HTTPX
@keep_alive_timeout: Numeric?
@total_timeout: Numeric?
def clone_new_connection: () -> instance
def addresses: () -> Array[ipaddr]?
def addresses=: (Array[ipaddr]) -> void