mirror of
https://github.com/HoneyryderChuck/httpx.git
synced 2025-08-10 00:01:27 -04:00
moved all altsvc-specific logic to AltSvc module; renamed uri to origin where possible; fixed the creation of the alternative channel
This commit is contained in:
parent
4fb10d9eb8
commit
94f335fd2f
@ -5,7 +5,6 @@ require "httpx/version"
|
||||
require "httpx/extensions"
|
||||
|
||||
require "httpx/errors"
|
||||
require "httpx/utils"
|
||||
require "httpx/altsvc"
|
||||
require "httpx/callbacks"
|
||||
require "httpx/loggable"
|
||||
|
@ -2,35 +2,56 @@
|
||||
|
||||
module HTTPX
|
||||
module AltSvc
|
||||
@lookup_mutex = Mutex.new
|
||||
@lookups = Hash.new { |h, k| h[k] = [] }
|
||||
@altsvc_mutex = Mutex.new
|
||||
@altsvcs = Hash.new { |h, k| h[k] = [] }
|
||||
|
||||
module_function
|
||||
|
||||
def cached_lookup(origin)
|
||||
def cached_altsvc(origin)
|
||||
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
||||
@lookup_mutex.synchronize do
|
||||
@altsvc_mutex.synchronize do
|
||||
lookup(origin, now)
|
||||
end
|
||||
end
|
||||
|
||||
def cached_lookup_set(origin, entry)
|
||||
def cached_altsvc_set(origin, entry)
|
||||
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
||||
entry["TTL"] = Integer(entry["ma"]) + now if entry.key?("ma")
|
||||
@lookup_mutex.synchronize do
|
||||
return if @lookups[origin].any? { |svc| svc["origin"] == entry["origin"] }
|
||||
@lookups[origin] << entry
|
||||
@altsvc_mutex.synchronize do
|
||||
return if @altsvcs[origin].any? { |altsvc| altsvc["origin"] == entry["origin"] }
|
||||
entry["TTL"] = Integer(entry["ma"]) + now if entry.key?("ma")
|
||||
@altsvcs[origin] << entry
|
||||
entry
|
||||
end
|
||||
end
|
||||
|
||||
def lookup(origin, ttl)
|
||||
return [] unless @lookups.key?(origin)
|
||||
@lookups[origin] = @lookups[origin].select do |entry|
|
||||
return [] unless @altsvcs.key?(origin)
|
||||
@altsvcs[origin] = @altsvcs[origin].select do |entry|
|
||||
!entry.key?("TTL") || entry["TTL"] > ttl
|
||||
end
|
||||
@lookups[origin].reject { |entry| entry["noop"] }
|
||||
.find { |entry| entry["origin"] == origin }
|
||||
@altsvcs[origin].reject { |entry| entry["noop"] }
|
||||
end
|
||||
|
||||
def emit(request, response)
|
||||
# Alt-Svc
|
||||
return unless response.headers.key?("alt-svc")
|
||||
origin = request.origin
|
||||
host = request.uri.host
|
||||
parse(response.headers["alt-svc"]) do |alt_origin, alt_params|
|
||||
alt_origin.host ||= host
|
||||
yield(alt_origin, origin, alt_params)
|
||||
end
|
||||
end
|
||||
|
||||
def parse(altsvc)
|
||||
alt_origins, *alt_params = altsvc.split(/ *; */)
|
||||
alt_params = Hash[alt_params.map { |field| field.split("=") }]
|
||||
alt_origins.split(/ *, */).each do |alt_origin|
|
||||
alt_proto, alt_origin = alt_origin.split("=")
|
||||
alt_origin = alt_origin[1..-2] if alt_origin.start_with?("\"") && alt_origin.end_with?("\"")
|
||||
alt_origin = URI.parse("#{alt_proto}://#{alt_origin}")
|
||||
yield(alt_origin, alt_params)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -123,11 +123,13 @@ module HTTPX
|
||||
def match?(uri)
|
||||
return false if @state == :closing
|
||||
|
||||
@origins.include?(uri.origin) || match_alternative_service?(uri)
|
||||
@origins.include?(uri.origin) || match_altsvcs?(uri)
|
||||
end
|
||||
|
||||
def match_alternative_service?(uri)
|
||||
AltSvc.cached_lookup(@uri.host).any? do |altsvc|
|
||||
# checks if this is channel is an alternative service of
|
||||
# +uri+
|
||||
def match_altsvcs?(uri)
|
||||
AltSvc.cached_altsvc(@uri.origin).any? do |altsvc|
|
||||
origin = altsvc["origin"]
|
||||
uri.origin == origin.to_s
|
||||
end
|
||||
@ -248,7 +250,9 @@ module HTTPX
|
||||
def build_parser(protocol = @io.protocol)
|
||||
parser = registry(protocol).new(@write_buffer, @options)
|
||||
parser.on(:response) do |*args|
|
||||
parse_alternative_services(*args)
|
||||
AltSvc.emit(*args) do |alt_origin, origin, alt_params|
|
||||
emit(:altsvc, alt_origin, origin, alt_params)
|
||||
end
|
||||
emit(:response, *args)
|
||||
end
|
||||
parser.on(:promise) do |*args|
|
||||
@ -312,17 +316,6 @@ module HTTPX
|
||||
emit(:close)
|
||||
end
|
||||
|
||||
def parse_alternative_services(request, response)
|
||||
# Alt-Svc
|
||||
return unless response.headers.key?("alt-svc")
|
||||
origin = request.origin
|
||||
host = request.authority
|
||||
Utils.parse_altsvc(response.headers["alt-svc"]) do |alt_uri, params|
|
||||
alt_uri.host ||= host
|
||||
emit(:alternative_service, alt_uri, origin, params)
|
||||
end
|
||||
end
|
||||
|
||||
def on_error(ex)
|
||||
handle_error(ex)
|
||||
reset
|
||||
|
@ -65,8 +65,8 @@ module HTTPX
|
||||
other_channel = build_channel(uncoalesced_uri, options)
|
||||
channel.unmerge(other_channel)
|
||||
end
|
||||
channel.on(:alternative_service) do |alt_uri, origin, params|
|
||||
build_alternative_channel(channel, alt_uri, origin, params, options)
|
||||
channel.on(:altsvc) do |alt_origin, origin, alt_params|
|
||||
build_altsvc_channel(channel, alt_origin, origin, alt_params, options)
|
||||
end
|
||||
end
|
||||
|
||||
@ -76,22 +76,28 @@ module HTTPX
|
||||
channel
|
||||
end
|
||||
|
||||
def build_alternative_channel(existing_channel, alt_uri, origin, params, options)
|
||||
altsvc = AltSvc.cached_lookup(origin) ||
|
||||
AltSvc.cached_lookup_set(origin, params.merge("origin" => alt_uri))
|
||||
def build_altsvc_channel(existing_channel, alt_origin, origin, alt_params, options)
|
||||
altsvc = AltSvc.cached_altsvc_set(origin, alt_params.merge("origin" => alt_origin))
|
||||
|
||||
# altsvc already exists, somehow it wasn't advertised, probably noop
|
||||
return unless altsvc
|
||||
|
||||
channel = build_channel(alt_uri, options)
|
||||
channel = build_channel(alt_origin, options)
|
||||
# advertised altsvc is the same origin being used, ignore
|
||||
return if channel == existing_channel
|
||||
|
||||
log(level: 1) { "#{origin} alt-svc: #{alt_uri}" }
|
||||
log(level: 1) { "#{origin} alt-svc: #{alt_origin}" }
|
||||
|
||||
# get uninitialized requests
|
||||
existing_channel.pending.reject do |request, _|
|
||||
request.origin == origin && request.state == :idle
|
||||
end.each do |request, args|
|
||||
channel.send(request, args)
|
||||
# incidentally, all requests will be re-routed to the first
|
||||
# advertised alt-svc, which incidentally follows the spec.
|
||||
existing_channel.pending.delete_if do |request, args|
|
||||
is_idle = request.origin == origin && request.state == :idle
|
||||
if is_idle
|
||||
log(level: 1) { "#{origin} alt-svc: sending #{request.uri} to #{alt_origin}" }
|
||||
channel.send(request, args)
|
||||
end
|
||||
is_idle
|
||||
end
|
||||
rescue UnsupportedSchemeError
|
||||
altsvc["noop"] = true
|
||||
|
@ -1,18 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module HTTPX
|
||||
module Utils
|
||||
module_function
|
||||
|
||||
def parse_altsvc(altsvc)
|
||||
alt_uris, *params = altsvc.split(/ *; */)
|
||||
params = Hash[params.map { |field| field.split("=") }]
|
||||
alt_uris.split(/ *, */).each do |alt_uri|
|
||||
alt_proto, alt_uri = alt_uri.split("=")
|
||||
alt_uri = alt_uri[1..-2] if alt_uri.start_with?("\"") && alt_uri.end_with?("\"")
|
||||
alt_uri = URI.parse("#{alt_proto}://#{alt_uri}")
|
||||
yield(alt_uri, params)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user