class HTTPX::Connection

  1. lib/httpx/connection.rb
  2. lib/httpx/connection/http1.rb
  3. lib/httpx/connection/http2.rb
  4. lib/httpx/plugins/h2c.rb
  5. show all
Superclass: Object

The Connection can be watched for IO events.

It contains the io object to read/write from, and knows what to do when it can.

It defers connecting until absolutely necessary. Connection should be triggered from the IO selector (until then, any request will be queued).

A connection boots up its parser after connection is established. All pending requests will be redirected there after connection.

A connection can be prevented from closing by the parser, that is, if there are pending requests. This will signal that the connection was prematurely closed, due to a possible number of conditions:

  • Remote peer closed the connection (“Connection: close”);

  • Remote peer doesn’t support pipelining;

A connection may also route requests for a different host for which the io was connected to, provided that the IP is the same and the port and scheme as well. This will allow to share the same socket to send HTTP/2 requests to different hosts.

Included modules

  1. Loggable
  2. Callbacks

Attributes

current_selector [W]
current_session [RW]
family [RW]
io [R]
options [R]
origin [R]
origins [R]
pending [R]
sibling [R]
ssl_session [R]
state [R]
type [R]

Public Class methods

new(uri, options)
[show source]
    # File lib/httpx/connection.rb
 49 def initialize(uri, options)
 50   @current_session = @current_selector =
 51     @parser = @sibling = @coalesced_connection =
 52                 @family = @io = @ssl_session = @timeout =
 53                                   @connected_at = @response_received_at = nil
 54 
 55   @exhausted = @cloned = @main_sibling = false
 56 
 57   @options = Options.new(options)
 58   @type = initialize_type(uri, @options)
 59   @origins = [uri.origin]
 60   @origin = Utils.to_uri(uri.origin)
 61   @window_size = @options.window_size
 62   @read_buffer = Buffer.new(@options.buffer_size)
 63   @write_buffer = Buffer.new(@options.buffer_size)
 64   @pending = []
 65   @inflight = 0
 66   @keep_alive_timeout = @options.timeout[:keep_alive_timeout]
 67 
 68   on(:error, &method(:on_error))
 69   if @options.io
 70     # if there's an already open IO, get its
 71     # peer address, and force-initiate the parser
 72     transition(:already_open)
 73     @io = build_socket
 74     parser
 75   else
 76     transition(:idle)
 77   end
 78   on(:close) do
 79     next if @exhausted # it'll reset
 80 
 81     # may be called after ":close" above, so after the connection has been checked back in.
 82     # next unless @current_session
 83 
 84     next unless @current_session
 85 
 86     @current_session.deselect_connection(self, @current_selector, @cloned)
 87   end
 88   on(:terminate) do
 89     next if @exhausted # it'll reset
 90 
 91     current_session = @current_session
 92     current_selector = @current_selector
 93 
 94     # may be called after ":close" above, so after the connection has been checked back in.
 95     next unless current_session && current_selector
 96 
 97     current_session.deselect_connection(self, current_selector)
 98   end
 99 
100   on(:altsvc) do |alt_origin, origin, alt_params|
101     build_altsvc_connection(alt_origin, origin, alt_params)
102   end
103 
104   self.addresses = @options.addresses if @options.addresses
105 end

Public Instance methods

addresses()
[show source]
    # File lib/httpx/connection.rb
121 def addresses
122   @io && @io.addresses
123 end
addresses=(addrs)

this is a semi-private method, to be used by the resolver to initiate the io object.

[show source]
    # File lib/httpx/connection.rb
113 def addresses=(addrs)
114   if @io
115     @io.add_addresses(addrs)
116   else
117     @io = build_socket(addrs)
118   end
119 end
addresses?()
[show source]
    # File lib/httpx/connection.rb
125 def addresses?
126   @io && @io.addresses?
127 end
call()
[show source]
    # File lib/httpx/connection.rb
242 def call
243   case @state
244   when :idle
245     connect
246 
247     # when opening the tcp or ssl socket fails
248     return if @state == :closed
249 
250     consume
251   when :closed
252     return
253   when :closing
254     consume
255     transition(:closed)
256   when :open
257     consume
258   end
259   nil
260 rescue StandardError => e
261   @write_buffer.clear
262   emit(:error, e)
263   raise e
264 end
close()
[show source]
    # File lib/httpx/connection.rb
266 def close
267   transition(:active) if @state == :inactive
268 
269   @parser.close if @parser
270 end
coalescable?(connection)

coalescable connections need to be mergeable! but internally, mergeable? is called before coalescable?

[show source]
    # File lib/httpx/connection.rb
163 def coalescable?(connection)
164   if @io.protocol == "h2" &&
165      @origin.scheme == "https" &&
166      connection.origin.scheme == "https" &&
167      @io.can_verify_peer?
168     @io.verify_hostname(connection.origin.host)
169   else
170     @origin == connection.origin
171   end
172 end
coalesce!(connection)

coalesces self into connection.

[show source]
    # File lib/httpx/connection.rb
154 def coalesce!(connection)
155   @coalesced_connection = connection
156 
157   close_sibling
158   connection.merge(self)
159 end
connecting?()
[show source]
    # File lib/httpx/connection.rb
209 def connecting?
210   @state == :idle
211 end
create_idle(options = {})
[show source]
    # File lib/httpx/connection.rb
174 def create_idle(options = {})
175   self.class.new(@origin, @options.merge(options))
176 end
deactivate()
[show source]
    # File lib/httpx/connection.rb
344 def deactivate
345   transition(:inactive)
346 end
disconnect()
[show source]
    # File lib/httpx/connection.rb
378 def disconnect
379   return unless @current_session && @current_selector
380 
381   emit(:close)
382   @current_session = @current_selector = nil
383 end
force_reset(cloned = false)

bypasses the state machine to force closing of connections still connecting. only used for Happy Eyeballs v2.

[show source]
    # File lib/httpx/connection.rb
286 def force_reset(cloned = false)
287   @state = :closing
288   @cloned = cloned
289   transition(:closed)
290 end
handle_connect_error(error)
[show source]
    # File lib/httpx/connection.rb
370 def handle_connect_error(error)
371   return handle_error(error) unless @sibling && @sibling.connecting?
372 
373   @sibling.merge(self)
374 
375   force_reset(true)
376 end
handle_socket_timeout(interval)
[show source]
    # File lib/httpx/connection.rb
352 def handle_socket_timeout(interval)
353   error = OperationTimeoutError.new(interval, "timed out while waiting on select")
354   error.set_backtrace(caller)
355   on_error(error)
356 end
idling()
[show source]
    # File lib/httpx/connection.rb
333 def idling
334   purge_after_closed
335   @write_buffer.clear
336   transition(:idle)
337   @parser = nil if @parser
338 end
inflight?()
[show source]
    # File lib/httpx/connection.rb
213 def inflight?
214   @parser && (
215     # parser may be dealing with other requests (possibly started from a different fiber)
216     !@parser.empty? ||
217     # connection may be doing connection termination handshake
218     !@write_buffer.empty?
219   )
220 end
inspect()

:nocov:

[show source]
    # File lib/httpx/connection.rb
386 def inspect
387   "#<#{self.class}:#{object_id} " \
388     "@origin=#{@origin} " \
389     "@state=#{@state} " \
390     "@pending=#{@pending.size} " \
391     "@io=#{@io}>"
392 end
interests()
[show source]
    # File lib/httpx/connection.rb
222 def interests
223   # connecting
224   if connecting?
225     connect
226 
227     return @io.interests if connecting?
228   end
229 
230   return @parser.interests if @parser
231 
232   nil
233 rescue StandardError => e
234   emit(:error, e)
235   nil
236 end
io_connected?()
[show source]
    # File lib/httpx/connection.rb
203 def io_connected?
204   return @coalesced_connection.io_connected? if @coalesced_connection
205 
206   @io && @io.state == :connected
207 end
match?(uri, options)
[show source]
    # File lib/httpx/connection.rb
129 def match?(uri, options)
130   return false if !used? && (@state == :closing || @state == :closed)
131 
132   (
133     @origins.include?(uri.origin) &&
134     # if there is more than one origin to match, it means that this connection
135     # was the result of coalescing. To prevent blind trust in the case where the
136     # origin came from an ORIGIN frame, we're going to verify the hostname with the
137     # SSL certificate
138     (@origins.size == 1 || @origin == uri.origin || (@io.is_a?(SSL) && @io.verify_hostname(uri.host)))
139   ) && @options == options
140 end
merge(connection)
[show source]
    # File lib/httpx/connection.rb
178 def merge(connection)
179   @origins |= connection.instance_variable_get(:@origins)
180   if @ssl_session.nil? && connection.ssl_session
181     @ssl_session = connection.ssl_session
182     @io.session_new_cb do |sess|
183       @ssl_session = sess
184     end if @io
185   end
186   connection.purge_pending do |req|
187     send(req)
188   end
189 end
mergeable?(connection)
[show source]
    # File lib/httpx/connection.rb
142 def mergeable?(connection)
143   return false if @state == :closing || @state == :closed || !@io
144 
145   return false unless connection.addresses
146 
147   (
148     (open? && @origin == connection.origin) ||
149     !(@io.addresses & (connection.addresses || [])).empty?
150   ) && @options == connection.options
151 end
open?()
[show source]
    # File lib/httpx/connection.rb
348 def open?
349   @state == :open || @state == :inactive
350 end
peer()
[show source]
    # File lib/httpx/connection.rb
107 def peer
108   @origin
109 end
purge_pending(&block)
[show source]
    # File lib/httpx/connection.rb
191 def purge_pending(&block)
192   pendings = []
193   if @parser
194     @inflight -= @parser.pending.size
195     pendings << @parser.pending
196   end
197   pendings << @pending
198   pendings.each do |pending|
199     pending.reject!(&block)
200   end
201 end
reset()
[show source]
    # File lib/httpx/connection.rb
292 def reset
293   return if @state == :closing || @state == :closed
294 
295   transition(:closing)
296 
297   transition(:closed)
298 end
send(request)
[show source]
    # File lib/httpx/connection.rb
300 def send(request)
301   return @coalesced_connection.send(request) if @coalesced_connection
302 
303   if @parser && !@write_buffer.full?
304     if @response_received_at && @keep_alive_timeout &&
305        Utils.elapsed_time(@response_received_at) > @keep_alive_timeout
306       # when pushing a request into an existing connection, we have to check whether there
307       # is the possibility that the connection might have extended the keep alive timeout.
308       # for such cases, we want to ping for availability before deciding to shovel requests.
309       log(level: 3) { "keep alive timeout expired, pinging connection..." }
310       @pending << request
311       transition(:active) if @state == :inactive
312       parser.ping
313       request.ping!
314       return
315     end
316 
317     send_request_to_parser(request)
318   else
319     @pending << request
320   end
321 end
sibling=(connection)
[show source]
    # File lib/httpx/connection.rb
358 def sibling=(connection)
359   @sibling = connection
360 
361   return unless connection
362 
363   @main_sibling = connection.sibling.nil?
364 
365   return unless @main_sibling
366 
367   connection.sibling = self
368 end
terminate()
[show source]
    # File lib/httpx/connection.rb
272 def terminate
273   case @state
274   when :idle
275     purge_after_closed
276     emit(:terminate)
277   when :closed
278     @connected_at = nil
279   end
280 
281   close
282 end
timeout()
[show source]
    # File lib/httpx/connection.rb
323 def timeout
324   return if @state == :closed || @state == :inactive
325 
326   return @timeout if @timeout
327 
328   return @options.timeout[:connect_timeout] if @state == :idle
329 
330   @options.timeout[:operation_timeout]
331 end
to_io()
[show source]
    # File lib/httpx/connection.rb
238 def to_io
239   @io.to_io
240 end
used?()
[show source]
    # File lib/httpx/connection.rb
340 def used?
341   @connected_at
342 end