lib/httpx/connection.rb in httpx-1.1.5 vs lib/httpx/connection.rb in httpx-1.2.0
- old
+ new
@@ -45,15 +45,15 @@
attr_writer :timers
attr_accessor :family
- def initialize(type, uri, options)
- @type = type
+ def initialize(uri, options)
@origins = [uri.origin]
@origin = Utils.to_uri(uri.origin)
@options = Options.new(options)
+ @type = initialize_type(uri, @options)
@window_size = @options.window_size
@read_buffer = Buffer.new(@options.buffer_size)
@write_buffer = Buffer.new(@options.buffer_size)
@pending = []
on(:error, &method(:on_error))
@@ -90,22 +90,18 @@
end
def match?(uri, options)
return false if !used? && (@state == :closing || @state == :closed)
- 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?
return false unless @io
@@ -113,12 +109,10 @@
end
def mergeable?(connection)
return false if @state == :closing || @state == :closed || !@io
- return false if exhausted?
-
return false unless connection.addresses
(
(open? && @origin == connection.origin) ||
!(@io.addresses & (connection.addresses || [])).empty?
@@ -137,11 +131,11 @@
@origin == connection.origin
end
end
def create_idle(options = {})
- self.class.new(@type, @origin, @options.merge(options))
+ self.class.new(@origin, @options.merge(options))
end
def merge(connection)
@origins |= connection.instance_variable_get(:@origins)
if connection.ssl_session
@@ -165,28 +159,10 @@
pendings.each do |pending|
pending.reject!(&block)
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
def inflight?
@@ -221,11 +197,10 @@
when :closed
return
when :closing
consume
transition(:closed)
- emit(:close)
when :open
consume
end
nil
end
@@ -234,28 +209,37 @@
transition(:active) if @state == :inactive
@parser.close if @parser
end
+ def terminate
+ @connected_at = nil if @state == :closed
+
+ close
+ end
+
# bypasses the state machine to force closing of connections still connecting.
# **only** used for Happy Eyeballs v2.
def force_reset
@state = :closing
transition(:closed)
- emit(:close)
end
def reset
+ return if @state == :closing || @state == :closed
+
transition(:closing)
+ unless @write_buffer.empty?
+ # handshakes, try sending
+ consume
+ @write_buffer.clear
+ end
transition(:closed)
- emit(:close)
end
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
# is the possibility that the connection might have extended the keep alive timeout.
# for such cases, we want to ping for availability before deciding to shovel requests.
@@ -317,14 +301,10 @@
def connect
transition(:open)
end
- def exhausted?
- @parser && parser.exhausted?
- end
-
def consume
return unless @io
catch(:called) do
epiped = false
@@ -495,38 +475,29 @@
parser.on(:promise) do |request, stream|
request.emit(:promise, parser, stream)
end
parser.on(:exhausted) do
+ @pending.concat(parser.pending)
emit(:exhausted)
end
parser.on(:origin) do |origin|
@origins |= [origin]
end
parser.on(:close) do |force|
- if @state != :closed
- transition(:closing)
- if force || @state == :idle
- transition(:closed)
- emit(:close)
- end
+ if force
+ reset
+ emit(:terminate)
end
end
parser.on(:close_handshake) do
consume
end
parser.on(:reset) do
- if parser.empty?
- reset
- else
- transition(:closing)
- transition(:closed)
-
- @parser.reset if @parser
- transition(:idle)
- transition(:open)
- end
+ @pending.concat(parser.pending) unless parser.empty?
+ reset
+ idling unless @pending.empty?
end
parser.on(:current_timeout) do
@current_timeout = @timeout = parser.timeout
end
parser.on(:timeout) do |tout|
@@ -591,17 +562,18 @@
@timeout = @current_timeout = parser.timeout
emit(:open)
when :inactive
return unless @state == :open
when :closing
- return unless @state == :open
+ return unless @state == :idle || @state == :open
when :closed
return unless @state == :closing
return unless @write_buffer.empty?
purge_after_closed
+ emit(:close) if @pending.empty?
when :already_open
nextstate = :open
# the first check for given io readiness must still use a timeout.
# connect is the reasonable choice in such a case.
@timeout = @options.timeout[:connect_timeout]
@@ -617,9 +589,22 @@
def purge_after_closed
@io.close if @io
@read_buffer.clear
@timeout = nil
+ end
+
+ def initialize_type(uri, options)
+ options.transport || begin
+ case uri.scheme
+ when "http"
+ "tcp"
+ when "https"
+ "ssl"
+ else
+ raise UnsupportedSchemeError, "#{uri}: #{uri.scheme}: unsupported URI scheme"
+ end
+ end
end
def build_socket(addrs = nil)
case @type
when "tcp"