mirror of
https://github.com/HoneyryderChuck/httpx.git
synced 2025-12-08 00:00:58 -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 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
|
||||
@altsvcs = Hash.new { |h, k| h[k] = [] }
|
||||
|
||||
@ -99,7 +149,10 @@ module HTTPX
|
||||
end
|
||||
|
||||
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?("\"")
|
||||
|
||||
URI.parse("#{alt_scheme}://#{alt_origin}")
|
||||
|
||||
@ -95,15 +95,13 @@ module HTTPX
|
||||
return false if exhausted?
|
||||
|
||||
(
|
||||
(
|
||||
@origins.include?(uri.origin) &&
|
||||
# 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
|
||||
# origin came from an ORIGIN frame, we're going to verify the hostname with the
|
||||
# SSL certificate
|
||||
(@origins.size == 1 || @origin == uri.origin || (@io.is_a?(SSL) && @io.verify_hostname(uri.host)))
|
||||
) && @options == options
|
||||
) || (match_altsvcs?(uri) && match_altsvc_options?(uri, options))
|
||||
@origins.include?(uri.origin) &&
|
||||
# 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
|
||||
# origin came from an ORIGIN frame, we're going to verify the hostname with the
|
||||
# SSL certificate
|
||||
(@origins.size == 1 || @origin == uri.origin || (@io.is_a?(SSL) && @io.verify_hostname(uri.host)))
|
||||
) && @options == options
|
||||
end
|
||||
|
||||
def expired?
|
||||
@ -167,24 +165,6 @@ module HTTPX
|
||||
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?
|
||||
@state == :idle
|
||||
end
|
||||
@ -252,8 +232,6 @@ module HTTPX
|
||||
|
||||
def send(request)
|
||||
if @parser && !@write_buffer.full?
|
||||
request.headers["alt-used"] = @origin.authority if match_altsvcs?(request.uri)
|
||||
|
||||
if @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
|
||||
|
||||
@ -54,21 +54,6 @@ module HTTPX
|
||||
def origin
|
||||
"#{scheme}://#{authority}"
|
||||
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
|
||||
|
||||
@ -203,9 +203,12 @@ module HTTPX
|
||||
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)
|
||||
|
||||
# advertised altsvc is the same origin being used, ignore
|
||||
return if connection == existing_connection
|
||||
|
||||
connection.extend(AltSvc::ConnectionMixin) unless connection.is_a?(AltSvc::ConnectionMixin)
|
||||
|
||||
set_connection_callbacks(connection, connections, alt_options)
|
||||
|
||||
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]) -> void
|
||||
def addresses=: (Array[ipaddr] addresses) -> void
|
||||
|
||||
def send: (Request request) -> void
|
||||
|
||||
def match?: (URI::Generic uri, Options options) -> bool
|
||||
|
||||
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 merge: (Connection) -> void
|
||||
def merge: (Connection connection) -> void
|
||||
|
||||
def purge_pending: () { (Request) -> void } -> void
|
||||
|
||||
def match_altsvcs?: (URI::Generic uri) -> bool
|
||||
|
||||
def match_altsvc_options?: (URI::Generic uri, Options options) -> bool
|
||||
def purge_pending: () { (Request request) -> void } -> void
|
||||
|
||||
def connecting?: () -> bool
|
||||
|
||||
@ -78,8 +76,6 @@ module HTTPX
|
||||
|
||||
def reset: () -> void
|
||||
|
||||
def send: (Request) -> void
|
||||
|
||||
def timeout: () -> Numeric?
|
||||
|
||||
def idling: () -> void
|
||||
@ -112,15 +108,15 @@ module HTTPX
|
||||
|
||||
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 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
|
||||
|
||||
|
||||
@ -33,7 +33,7 @@ module HTTPX
|
||||
|
||||
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]
|
||||
| (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/")
|
||||
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")
|
||||
assert entries.empty?
|
||||
|
||||
@ -6,7 +6,7 @@ module Requests
|
||||
altsvc_host = ENV["HTTPBIN_ALTSVC_HOST"]
|
||||
altsvc_origin = origin(altsvc_host)
|
||||
|
||||
HTTPX.wrap do |http|
|
||||
HTTPX.plugin(SessionWithPool).wrap do |http|
|
||||
altsvc_uri = build_uri("/get", altsvc_origin)
|
||||
response = http.get(altsvc_uri)
|
||||
verify_status(response, 200)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user