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:
HoneyryderChuck 2018-11-03 01:18:35 +00:00
parent 4fb10d9eb8
commit 94f335fd2f
5 changed files with 59 additions and 58 deletions

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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