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. lib/httpx/plugins/upgrade/h2.rb
  6. 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

family [RW]
io [R]
options [R]
origin [R]
origins [R]
pending [R]
ssl_session [R]
state [R]
timers [W]
type [R]

Public Class methods

new(uri, options)
[show source]
   # File lib/httpx/connection.rb
50 def initialize(uri, options)
51   @origins = [uri.origin]
52   @origin = Utils.to_uri(uri.origin)
53   @options = Options.new(options)
54   @type = initialize_type(uri, @options)
55   @window_size = @options.window_size
56   @read_buffer = Buffer.new(@options.buffer_size)
57   @write_buffer = Buffer.new(@options.buffer_size)
58   @pending = []
59   on(:error, &method(:on_error))
60   if @options.io
61     # if there's an already open IO, get its
62     # peer address, and force-initiate the parser
63     transition(:already_open)
64     @io = build_socket
65     parser
66   else
67     transition(:idle)
68   end
69 
70   @inflight = 0
71   @keep_alive_timeout = @options.timeout[:keep_alive_timeout]
72 
73   @intervals = []
74 
75   self.addresses = @options.addresses if @options.addresses
76 end

Public Instance methods

addresses()
[show source]
   # File lib/httpx/connection.rb
88 def addresses
89   @io && @io.addresses
90 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
80 def addresses=(addrs)
81   if @io
82     @io.add_addresses(addrs)
83   else
84     @io = build_socket(addrs)
85   end
86 end
call()
[show source]
    # File lib/httpx/connection.rb
192 def call
193   case @state
194   when :idle
195     connect
196     consume
197   when :closed
198     return
199   when :closing
200     consume
201     transition(:closed)
202   when :open
203     consume
204   end
205   nil
206 end
close()
[show source]
    # File lib/httpx/connection.rb
208 def close
209   transition(:active) if @state == :inactive
210 
211   @parser.close if @parser
212 end
coalescable?(connection)

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

[show source]
    # File lib/httpx/connection.rb
124 def coalescable?(connection)
125   if @io.protocol == "h2" &&
126      @origin.scheme == "https" &&
127      connection.origin.scheme == "https" &&
128      @io.can_verify_peer?
129     @io.verify_hostname(connection.origin.host)
130   else
131     @origin == connection.origin
132   end
133 end
connecting?()
[show source]
    # File lib/httpx/connection.rb
164 def connecting?
165   @state == :idle
166 end
create_idle(options = {})
[show source]
    # File lib/httpx/connection.rb
135 def create_idle(options = {})
136   self.class.new(@origin, @options.merge(options))
137 end
deactivate()
[show source]
    # File lib/httpx/connection.rb
274 def deactivate
275   transition(:inactive)
276 end
expired?()
[show source]
    # File lib/httpx/connection.rb
105 def expired?
106   return false unless @io
107 
108   @io.expired?
109 end
force_reset()

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

[show source]
    # File lib/httpx/connection.rb
222 def force_reset
223   @state = :closing
224   transition(:closed)
225 end
handle_socket_timeout(interval)
[show source]
    # File lib/httpx/connection.rb
282 def handle_socket_timeout(interval)
283   @intervals.delete_if(&:elapsed?)
284 
285   unless @intervals.empty?
286     # remove the intervals which will elapse
287 
288     return
289   end
290 
291   error = HTTPX::TimeoutError.new(interval, "timed out while waiting on select")
292   error.set_backtrace(caller)
293   on_error(error)
294 end
idling()
[show source]
    # File lib/httpx/connection.rb
263 def idling
264   purge_after_closed
265   @write_buffer.clear
266   transition(:idle)
267   @parser = nil if @parser
268 end
inflight?()
[show source]
    # File lib/httpx/connection.rb
168 def inflight?
169   @parser && !@parser.empty? && !@write_buffer.empty?
170 end
interests()
[show source]
    # File lib/httpx/connection.rb
172 def interests
173   # connecting
174   if connecting?
175     connect
176 
177     return @io.interests if connecting?
178   end
179 
180   # if the write buffer is full, we drain it
181   return :w unless @write_buffer.empty?
182 
183   return @parser.interests if @parser
184 
185   nil
186 end
match?(uri, options)
[show source]
    # File lib/httpx/connection.rb
 92 def match?(uri, options)
 93   return false if !used? && (@state == :closing || @state == :closed)
 94 
 95   (
 96     @origins.include?(uri.origin) &&
 97     # if there is more than one origin to match, it means that this connection
 98     # was the result of coalescing. To prevent blind trust in the case where the
 99     # origin came from an ORIGIN frame, we're going to verify the hostname with the
100     # SSL certificate
101     (@origins.size == 1 || @origin == uri.origin || (@io.is_a?(SSL) && @io.verify_hostname(uri.host)))
102   ) && @options == options
103 end
merge(connection)
[show source]
    # File lib/httpx/connection.rb
139 def merge(connection)
140   @origins |= connection.instance_variable_get(:@origins)
141   if connection.ssl_session
142     @ssl_session = connection.ssl_session
143     @io.session_new_cb do |sess|
144       @ssl_session = sess
145     end if @io
146   end
147   connection.purge_pending do |req|
148     send(req)
149   end
150 end
mergeable?(connection)
[show source]
    # File lib/httpx/connection.rb
111 def mergeable?(connection)
112   return false if @state == :closing || @state == :closed || !@io
113 
114   return false unless connection.addresses
115 
116   (
117     (open? && @origin == connection.origin) ||
118     !(@io.addresses & (connection.addresses || [])).empty?
119   ) && @options == connection.options
120 end
open?()
[show source]
    # File lib/httpx/connection.rb
278 def open?
279   @state == :open || @state == :inactive
280 end
purge_pending(&block)
[show source]
    # File lib/httpx/connection.rb
152 def purge_pending(&block)
153   pendings = []
154   if @parser
155     @inflight -= @parser.pending.size
156     pendings << @parser.pending
157   end
158   pendings << @pending
159   pendings.each do |pending|
160     pending.reject!(&block)
161   end
162 end
reset()
[show source]
    # File lib/httpx/connection.rb
227 def reset
228   return if @state == :closing || @state == :closed
229 
230   transition(:closing)
231 
232   transition(:closed)
233 end
send(request)
[show source]
    # File lib/httpx/connection.rb
235 def send(request)
236   if @parser && !@write_buffer.full?
237     if @response_received_at && @keep_alive_timeout &&
238        Utils.elapsed_time(@response_received_at) > @keep_alive_timeout
239       # when pushing a request into an existing connection, we have to check whether there
240       # is the possibility that the connection might have extended the keep alive timeout.
241       # for such cases, we want to ping for availability before deciding to shovel requests.
242       log(level: 3) { "keep alive timeout expired, pinging connection..." }
243       @pending << request
244       transition(:active) if @state == :inactive
245       parser.ping
246       return
247     end
248 
249     send_request_to_parser(request)
250   else
251     @pending << request
252   end
253 end
terminate()
[show source]
    # File lib/httpx/connection.rb
214 def terminate
215   @connected_at = nil if @state == :closed
216 
217   close
218 end
timeout()
[show source]
    # File lib/httpx/connection.rb
255 def timeout
256   return @timeout if @timeout
257 
258   return @options.timeout[:connect_timeout] if @state == :idle
259 
260   @options.timeout[:operation_timeout]
261 end
to_io()
[show source]
    # File lib/httpx/connection.rb
188 def to_io
189   @io.to_io
190 end
used?()
[show source]
    # File lib/httpx/connection.rb
270 def used?
271   @connected_at
272 end