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"