mirror of
https://github.com/HoneyryderChuck/httpx.git
synced 2025-12-10 00:03:00 -05:00
moved altsvc-specific connection behaviour to mixin
this mixin applies only for connections built via Session#build_altsvc_connection. This moves out logic which was always being called on the hot path for connections which hadn't been alt-svc enabled, which improves the Connection#match? bottleneck.
This commit is contained in:
parent
a27f735eb8
commit
cce68bcd98
@ -4,6 +4,56 @@ require "strscan"
|
|||||||
|
|
||||||
module HTTPX
|
module HTTPX
|
||||||
module AltSvc
|
module AltSvc
|
||||||
|
# makes connections able to accept requests destined to primary service.
|
||||||
|
module ConnectionMixin
|
||||||
|
def send(request)
|
||||||
|
request.headers["alt-used"] = @origin.authority if @parser && !@write_buffer.full? && match_altsvcs?(request.uri)
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
|
||||||
|
def match?(uri, options)
|
||||||
|
return false if !used? && (@state == :closing || @state == :closed)
|
||||||
|
|
||||||
|
match_altsvcs?(uri) && match_altsvc_options?(uri, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# checks if this is connection is an alternative service of
|
||||||
|
# +uri+
|
||||||
|
def match_altsvcs?(uri)
|
||||||
|
@origins.any? { |origin| altsvc_match?(uri, origin) } ||
|
||||||
|
AltSvc.cached_altsvc(@origin).any? do |altsvc|
|
||||||
|
origin = altsvc["origin"]
|
||||||
|
altsvc_match?(origin, uri.origin)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def match_altsvc_options?(uri, options)
|
||||||
|
return @options == options unless @options.ssl.all? do |k, v|
|
||||||
|
v == (k == :hostname ? uri.host : options.ssl[k])
|
||||||
|
end
|
||||||
|
|
||||||
|
@options.options_equals?(options, Options::REQUEST_BODY_IVARS + %i[@ssl])
|
||||||
|
end
|
||||||
|
|
||||||
|
def altsvc_match?(uri, other_uri)
|
||||||
|
other_uri = URI(other_uri)
|
||||||
|
|
||||||
|
uri.origin == other_uri.origin || begin
|
||||||
|
case uri.scheme
|
||||||
|
when "h2"
|
||||||
|
(other_uri.scheme == "https" || other_uri.scheme == "h2") &&
|
||||||
|
uri.host == other_uri.host &&
|
||||||
|
uri.port == other_uri.port
|
||||||
|
else
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@altsvc_mutex = Thread::Mutex.new
|
@altsvc_mutex = Thread::Mutex.new
|
||||||
@altsvcs = Hash.new { |h, k| h[k] = [] }
|
@altsvcs = Hash.new { |h, k| h[k] = [] }
|
||||||
|
|
||||||
@ -99,7 +149,10 @@ module HTTPX
|
|||||||
end
|
end
|
||||||
|
|
||||||
def parse_altsvc_origin(alt_proto, alt_origin)
|
def parse_altsvc_origin(alt_proto, alt_origin)
|
||||||
alt_scheme = parse_altsvc_scheme(alt_proto) or return
|
alt_scheme = parse_altsvc_scheme(alt_proto)
|
||||||
|
|
||||||
|
return unless alt_scheme
|
||||||
|
|
||||||
alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
|
alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
|
||||||
|
|
||||||
URI.parse("#{alt_scheme}://#{alt_origin}")
|
URI.parse("#{alt_scheme}://#{alt_origin}")
|
||||||
|
|||||||
@ -95,15 +95,13 @@ module HTTPX
|
|||||||
return false if exhausted?
|
return false if exhausted?
|
||||||
|
|
||||||
(
|
(
|
||||||
(
|
@origins.include?(uri.origin) &&
|
||||||
@origins.include?(uri.origin) &&
|
# if there is more than one origin to match, it means that this connection
|
||||||
# if there is more than one origin to match, it means that this connection
|
# was the result of coalescing. To prevent blind trust in the case where the
|
||||||
# was the result of coalescing. To prevent blind trust in the case where the
|
# origin came from an ORIGIN frame, we're going to verify the hostname with the
|
||||||
# origin came from an ORIGIN frame, we're going to verify the hostname with the
|
# SSL certificate
|
||||||
# SSL certificate
|
(@origins.size == 1 || @origin == uri.origin || (@io.is_a?(SSL) && @io.verify_hostname(uri.host)))
|
||||||
(@origins.size == 1 || @origin == uri.origin || (@io.is_a?(SSL) && @io.verify_hostname(uri.host)))
|
) && @options == options
|
||||||
) && @options == options
|
|
||||||
) || (match_altsvcs?(uri) && match_altsvc_options?(uri, options))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def expired?
|
def expired?
|
||||||
@ -167,24 +165,6 @@ module HTTPX
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# checks if this is connection is an alternative service of
|
|
||||||
# +uri+
|
|
||||||
def match_altsvcs?(uri)
|
|
||||||
@origins.any? { |origin| uri.altsvc_match?(origin) } ||
|
|
||||||
AltSvc.cached_altsvc(@origin).any? do |altsvc|
|
|
||||||
origin = altsvc["origin"]
|
|
||||||
origin.altsvc_match?(uri.origin)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def match_altsvc_options?(uri, options)
|
|
||||||
return @options == options unless @options.ssl[:hostname] == uri.host
|
|
||||||
|
|
||||||
dup_options = @options.merge(ssl: { hostname: nil })
|
|
||||||
dup_options.ssl.delete(:hostname)
|
|
||||||
dup_options == options
|
|
||||||
end
|
|
||||||
|
|
||||||
def connecting?
|
def connecting?
|
||||||
@state == :idle
|
@state == :idle
|
||||||
end
|
end
|
||||||
@ -252,8 +232,6 @@ module HTTPX
|
|||||||
|
|
||||||
def send(request)
|
def send(request)
|
||||||
if @parser && !@write_buffer.full?
|
if @parser && !@write_buffer.full?
|
||||||
request.headers["alt-used"] = @origin.authority if match_altsvcs?(request.uri)
|
|
||||||
|
|
||||||
if @response_received_at && @keep_alive_timeout &&
|
if @response_received_at && @keep_alive_timeout &&
|
||||||
Utils.elapsed_time(@response_received_at) > @keep_alive_timeout
|
Utils.elapsed_time(@response_received_at) > @keep_alive_timeout
|
||||||
# when pushing a request into an existing connection, we have to check whether there
|
# when pushing a request into an existing connection, we have to check whether there
|
||||||
|
|||||||
@ -54,21 +54,6 @@ module HTTPX
|
|||||||
def origin
|
def origin
|
||||||
"#{scheme}://#{authority}"
|
"#{scheme}://#{authority}"
|
||||||
end unless URI::HTTP.method_defined?(:origin)
|
end unless URI::HTTP.method_defined?(:origin)
|
||||||
|
|
||||||
def altsvc_match?(uri)
|
|
||||||
uri = URI.parse(uri)
|
|
||||||
|
|
||||||
origin == uri.origin || begin
|
|
||||||
case scheme
|
|
||||||
when "h2"
|
|
||||||
(uri.scheme == "https" || uri.scheme == "h2") &&
|
|
||||||
host == uri.host &&
|
|
||||||
(port || default_port) == (uri.port || uri.default_port)
|
|
||||||
else
|
|
||||||
false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -203,9 +203,12 @@ module HTTPX
|
|||||||
alt_options = options.merge(ssl: options.ssl.merge(hostname: URI(origin).host))
|
alt_options = options.merge(ssl: options.ssl.merge(hostname: URI(origin).host))
|
||||||
|
|
||||||
connection = pool.find_connection(alt_origin, alt_options) || build_connection(alt_origin, alt_options)
|
connection = pool.find_connection(alt_origin, alt_options) || build_connection(alt_origin, alt_options)
|
||||||
|
|
||||||
# advertised altsvc is the same origin being used, ignore
|
# advertised altsvc is the same origin being used, ignore
|
||||||
return if connection == existing_connection
|
return if connection == existing_connection
|
||||||
|
|
||||||
|
connection.extend(AltSvc::ConnectionMixin) unless connection.is_a?(AltSvc::ConnectionMixin)
|
||||||
|
|
||||||
set_connection_callbacks(connection, connections, alt_options)
|
set_connection_callbacks(connection, connections, alt_options)
|
||||||
|
|
||||||
log(level: 1) { "#{origin} alt-svc: #{alt_origin}" }
|
log(level: 1) { "#{origin} alt-svc: #{alt_origin}" }
|
||||||
|
|||||||
33
sig/altsvc.rbs
Normal file
33
sig/altsvc.rbs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
module HTTPX
|
||||||
|
module AltSvc
|
||||||
|
module ConnectionMixin
|
||||||
|
|
||||||
|
def send: (Request request) -> void
|
||||||
|
|
||||||
|
def match?: (URI::Generic uri, Options options) -> bool
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def match_altsvcs?: (URI::Generic uri) -> bool
|
||||||
|
|
||||||
|
def match_altsvc_options?: (URI::Generic uri, Options options) -> bool
|
||||||
|
end
|
||||||
|
|
||||||
|
type altsvc_params = Hash[String, untyped]
|
||||||
|
|
||||||
|
def self?.cached_altsvc: (String origin) -> Array[altsvc_params]
|
||||||
|
|
||||||
|
def self?.cached_altsvc_set: (String origin, altsvc_params) -> void
|
||||||
|
|
||||||
|
def self?.lookup: (String origin, Integer | Float ttl) -> Array[altsvc_params]
|
||||||
|
|
||||||
|
def self?.emit: (Request request, response response) { (http_uri alt_origin, String origin, altsvc_params alt_params) -> void } -> void
|
||||||
|
|
||||||
|
def self?.parse: (String altsvc) { (http_uri alt_origin, altsvc_params alt_params) -> void } -> void
|
||||||
|
| (String altsvc) -> Enumerable[[http_uri, altsvc_params]]
|
||||||
|
|
||||||
|
def self?.parse_altsvc_scheme: (String alt_proto) -> String?
|
||||||
|
|
||||||
|
def self.parse_altsvc_origin: (string alt_proto, String alt_origin) -> http_uri?
|
||||||
|
end
|
||||||
|
end
|
||||||
@ -44,25 +44,23 @@ module HTTPX
|
|||||||
|
|
||||||
def addresses: () -> Array[ipaddr]?
|
def addresses: () -> Array[ipaddr]?
|
||||||
|
|
||||||
def addresses=: (Array[ipaddr]) -> void
|
def addresses=: (Array[ipaddr] addresses) -> void
|
||||||
|
|
||||||
|
def send: (Request request) -> void
|
||||||
|
|
||||||
def match?: (URI::Generic uri, Options options) -> bool
|
def match?: (URI::Generic uri, Options options) -> bool
|
||||||
|
|
||||||
def expired?: () -> boolish
|
def expired?: () -> boolish
|
||||||
|
|
||||||
def mergeable?: (Connection) -> bool
|
def mergeable?: (Connection connection) -> bool
|
||||||
|
|
||||||
def coalescable?: (Connection) -> bool
|
def coalescable?: (Connection connection) -> bool
|
||||||
|
|
||||||
def create_idle: (?Hash[Symbol, untyped] options) -> Connection
|
def create_idle: (?Hash[Symbol, untyped] options) -> Connection
|
||||||
|
|
||||||
def merge: (Connection) -> void
|
def merge: (Connection connection) -> void
|
||||||
|
|
||||||
def purge_pending: () { (Request) -> void } -> void
|
def purge_pending: () { (Request request) -> void } -> void
|
||||||
|
|
||||||
def match_altsvcs?: (URI::Generic uri) -> bool
|
|
||||||
|
|
||||||
def match_altsvc_options?: (URI::Generic uri, Options options) -> bool
|
|
||||||
|
|
||||||
def connecting?: () -> bool
|
def connecting?: () -> bool
|
||||||
|
|
||||||
@ -78,8 +76,6 @@ module HTTPX
|
|||||||
|
|
||||||
def reset: () -> void
|
def reset: () -> void
|
||||||
|
|
||||||
def send: (Request) -> void
|
|
||||||
|
|
||||||
def timeout: () -> Numeric?
|
def timeout: () -> Numeric?
|
||||||
|
|
||||||
def idling: () -> void
|
def idling: () -> void
|
||||||
@ -112,15 +108,15 @@ module HTTPX
|
|||||||
|
|
||||||
def set_parser_callbacks: (HTTP1 | HTTP2 parser) -> void
|
def set_parser_callbacks: (HTTP1 | HTTP2 parser) -> void
|
||||||
|
|
||||||
def transition: (Symbol) -> void
|
def transition: (Symbol nextstate) -> void
|
||||||
|
|
||||||
def handle_transition: (Symbol) -> void
|
def handle_transition: (Symbol nextstate) -> void
|
||||||
|
|
||||||
def build_socket: (?Array[ipaddr]? addrs) -> (TCP | SSL | UNIX)
|
def build_socket: (?Array[ipaddr]? addrs) -> (TCP | SSL | UNIX)
|
||||||
|
|
||||||
def on_error: (HTTPX::TimeoutError | Error | StandardError) -> void
|
def on_error: (HTTPX::TimeoutError | Error | StandardError error) -> void
|
||||||
|
|
||||||
def handle_error: (StandardError) -> void
|
def handle_error: (StandardError error) -> void
|
||||||
|
|
||||||
def purge_after_closed: () -> void
|
def purge_after_closed: () -> void
|
||||||
|
|
||||||
|
|||||||
@ -33,7 +33,7 @@ module HTTPX
|
|||||||
|
|
||||||
def set_connection_callbacks: (Connection connection, Array[Connection] connections, Options options) -> void
|
def set_connection_callbacks: (Connection connection, Array[Connection] connections, Options options) -> void
|
||||||
|
|
||||||
def build_altsvc_connection: (Connection existing_connection, Array[Connection] connections, URI::Generic alt_origin, String origin, Hash[String, String] alt_params, Options options) -> Connection?
|
def build_altsvc_connection: (Connection existing_connection, Array[Connection] connections, URI::Generic alt_origin, String origin, Hash[String, String] alt_params, Options options) -> (Connection & AltSvc::ConnectionMixin)?
|
||||||
|
|
||||||
def build_requests: (verb, uri, options) -> Array[Request]
|
def build_requests: (verb, uri, options) -> Array[Request]
|
||||||
| (Array[[verb, uri, options]], options) -> Array[Request]
|
| (Array[[verb, uri, options]], options) -> Array[Request]
|
||||||
|
|||||||
@ -65,7 +65,7 @@ class AltSvcTest < Minitest::Test
|
|||||||
req = Request.new("GET", "http://www.example-clear-cache.com/")
|
req = Request.new("GET", "http://www.example-clear-cache.com/")
|
||||||
res = Response.new(req, 200, "2.0", { "alt-svc" => "clear" })
|
res = Response.new(req, 200, "2.0", { "alt-svc" => "clear" })
|
||||||
|
|
||||||
AltSvc.emit(req, res)
|
AltSvc.emit(req, res) {}
|
||||||
|
|
||||||
entries = AltSvc.cached_altsvc("http://www.example-clear-cache.com")
|
entries = AltSvc.cached_altsvc("http://www.example-clear-cache.com")
|
||||||
assert entries.empty?
|
assert entries.empty?
|
||||||
|
|||||||
@ -6,7 +6,7 @@ module Requests
|
|||||||
altsvc_host = ENV["HTTPBIN_ALTSVC_HOST"]
|
altsvc_host = ENV["HTTPBIN_ALTSVC_HOST"]
|
||||||
altsvc_origin = origin(altsvc_host)
|
altsvc_origin = origin(altsvc_host)
|
||||||
|
|
||||||
HTTPX.wrap do |http|
|
HTTPX.plugin(SessionWithPool).wrap do |http|
|
||||||
altsvc_uri = build_uri("/get", altsvc_origin)
|
altsvc_uri = build_uri("/get", altsvc_origin)
|
||||||
response = http.get(altsvc_uri)
|
response = http.get(altsvc_uri)
|
||||||
verify_status(response, 200)
|
verify_status(response, 200)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user