Class: HTTPX::Connection
Overview
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:
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.
Defined Under Namespace
Classes: HTTP1, HTTP2
Constant Summary
Constants included
from Loggable
Loggable::COLORS, Loggable::USE_DEBUG_LOG
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
Methods included from Callbacks
#callbacks_for?, #emit, #on, #once
Methods included from Loggable
#log, #log_exception
Constructor Details
#initialize(uri, options) ⇒ Connection
Returns a new instance of Connection.
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
|
# File 'lib/httpx/connection.rb', line 52
def initialize(uri, options)
@current_session = @current_selector = @sibling = @coalesced_connection = nil
@exhausted = @cloned = @main_sibling = false
@options = Options.new(options)
@type = initialize_type(uri, @options)
@origins = [uri.origin]
@origin = Utils.to_uri(uri.origin)
@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))
if @options.io
transition(:already_open)
@io = build_socket
parser
else
transition(:idle)
end
on(:close) do
next if @exhausted
next unless @current_session
@current_session.deselect_connection(self, @current_selector, @cloned)
end
on(:terminate) do
next if @exhausted
current_session = @current_session
current_selector = @current_selector
next unless current_session && current_selector
current_session.deselect_connection(self, current_selector)
end
on(:altsvc) do |alt_origin, origin, alt_params|
build_altsvc_connection(alt_origin, origin, alt_params)
end
@inflight = 0
@keep_alive_timeout = @options.timeout[:keep_alive_timeout]
self.addresses = @options.addresses if @options.addresses
end
|
Instance Attribute Details
#current_selector=(value) ⇒ Object
Sets the attribute current_selector
46
47
48
|
# File 'lib/httpx/connection.rb', line 46
def current_selector=(value)
@current_selector = value
end
|
#current_session ⇒ Object
Returns the value of attribute current_session.
48
49
50
|
# File 'lib/httpx/connection.rb', line 48
def current_session
@current_session
end
|
#family ⇒ Object
Returns the value of attribute family.
48
49
50
|
# File 'lib/httpx/connection.rb', line 48
def family
@family
end
|
#io ⇒ Object
Returns the value of attribute io.
44
45
46
|
# File 'lib/httpx/connection.rb', line 44
def io
@io
end
|
#options ⇒ Object
Returns the value of attribute options.
44
45
46
|
# File 'lib/httpx/connection.rb', line 44
def options
@options
end
|
#origin ⇒ Object
Returns the value of attribute origin.
44
45
46
|
# File 'lib/httpx/connection.rb', line 44
def origin
@origin
end
|
#origins ⇒ Object
Returns the value of attribute origins.
44
45
46
|
# File 'lib/httpx/connection.rb', line 44
def origins
@origins
end
|
#pending ⇒ Object
Returns the value of attribute pending.
44
45
46
|
# File 'lib/httpx/connection.rb', line 44
def pending
@pending
end
|
#sibling=(connection) ⇒ Object
351
352
353
354
355
356
357
358
359
360
361
|
# File 'lib/httpx/connection.rb', line 351
def sibling=(connection)
@sibling = connection
return unless connection
@main_sibling = connection.sibling.nil?
return unless @main_sibling
connection.sibling = self
end
|
#ssl_session ⇒ Object
Returns the value of attribute ssl_session.
44
45
46
|
# File 'lib/httpx/connection.rb', line 44
def ssl_session
@ssl_session
end
|
#state ⇒ Object
Returns the value of attribute state.
44
45
46
|
# File 'lib/httpx/connection.rb', line 44
def state
@state
end
|
#type ⇒ Object
Returns the value of attribute type.
44
45
46
|
# File 'lib/httpx/connection.rb', line 44
def type
@type
end
|
Class Method Details
.parser_type(protocol) ⇒ Object
926
927
928
929
930
931
932
933
|
# File 'lib/httpx/connection.rb', line 926
def parser_type(protocol)
case protocol
when "h2" then HTTP2
when "http/1.1" then HTTP1
else
raise Error, "unsupported protocol (##{protocol})"
end
end
|
Instance Method Details
#addresses ⇒ Object
121
122
123
|
# File 'lib/httpx/connection.rb', line 121
def addresses
@io && @io.addresses
end
|
#addresses=(addrs) ⇒ Object
this is a semi-private method, to be used by the resolver to initiate the io object.
113
114
115
116
117
118
119
|
# File 'lib/httpx/connection.rb', line 113
def addresses=(addrs)
if @io
@io.add_addresses(addrs)
else
@io = build_socket(addrs)
end
end
|
#call ⇒ Object
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
|
# File 'lib/httpx/connection.rb', line 239
def call
case @state
when :idle
connect
consume
when :closed
return
when :closing
consume
transition(:closed)
when :open
consume
end
nil
rescue StandardError => e
emit(:error, e)
raise e
end
|
#close ⇒ Object
258
259
260
261
262
|
# File 'lib/httpx/connection.rb', line 258
def close
transition(:active) if @state == :inactive
@parser.close if @parser
end
|
#coalescable?(connection) ⇒ Boolean
coalescable connections need to be mergeable! but internally, #mergeable? is called before #coalescable?
157
158
159
160
161
162
163
164
165
166
|
# File 'lib/httpx/connection.rb', line 157
def coalescable?(connection)
if @io.protocol == "h2" &&
@origin.scheme == "https" &&
connection.origin.scheme == "https" &&
@io.can_verify_peer?
@io.verify_hostname(connection.origin.host)
else
@origin == connection.origin
end
end
|
#coalesced_connection=(connection) ⇒ Object
344
345
346
347
348
349
|
# File 'lib/httpx/connection.rb', line 344
def coalesced_connection=(connection)
@coalesced_connection = connection
close_sibling
connection.merge(self)
end
|
#connecting? ⇒ Boolean
203
204
205
|
# File 'lib/httpx/connection.rb', line 203
def connecting?
@state == :idle
end
|
#create_idle(options = {}) ⇒ Object
168
169
170
|
# File 'lib/httpx/connection.rb', line 168
def create_idle(options = {})
self.class.new(@origin, @options.merge(options))
end
|
#deactivate ⇒ Object
330
331
332
|
# File 'lib/httpx/connection.rb', line 330
def deactivate
transition(:inactive)
end
|
#disconnect ⇒ Object
373
374
375
376
377
378
379
|
# File 'lib/httpx/connection.rb', line 373
def disconnect
return unless @current_session && @current_selector
emit(:close)
@current_session = nil
@current_selector = nil
end
|
#expired? ⇒ Boolean
138
139
140
141
142
|
# File 'lib/httpx/connection.rb', line 138
def expired?
return false unless @io
@io.expired?
end
|
#force_reset(cloned = false) ⇒ Object
bypasses the state machine to force closing of connections still connecting. only used for Happy Eyeballs v2.
272
273
274
275
276
|
# File 'lib/httpx/connection.rb', line 272
def force_reset(cloned = false)
@state = :closing
@cloned = cloned
transition(:closed)
end
|
#handle_connect_error(error) ⇒ Object
363
364
365
366
367
368
369
370
371
|
# File 'lib/httpx/connection.rb', line 363
def handle_connect_error(error)
@connect_error = error
return handle_error(error) unless @sibling && @sibling.connecting?
@sibling.merge(self)
force_reset(true)
end
|
#handle_socket_timeout(interval) ⇒ Object
338
339
340
341
342
|
# File 'lib/httpx/connection.rb', line 338
def handle_socket_timeout(interval)
error = OperationTimeoutError.new(interval, "timed out while waiting on select")
error.set_backtrace(caller)
on_error(error)
end
|
#idling ⇒ Object
319
320
321
322
323
324
|
# File 'lib/httpx/connection.rb', line 319
def idling
purge_after_closed
@write_buffer.clear
transition(:idle)
@parser = nil if @parser
end
|
#inflight? ⇒ Boolean
207
208
209
210
211
212
213
214
|
# File 'lib/httpx/connection.rb', line 207
def inflight?
@parser && (
!@parser.empty? ||
!@write_buffer.empty?
)
end
|
#interests ⇒ Object
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
# File 'lib/httpx/connection.rb', line 216
def interests
if connecting?
connect
return @io.interests if connecting?
end
return :w unless @write_buffer.empty?
return @parser.interests if @parser
nil
rescue StandardError => e
emit(:error, e)
nil
end
|
#io_connected? ⇒ Boolean
197
198
199
200
201
|
# File 'lib/httpx/connection.rb', line 197
def io_connected?
return @coalesced_connection.io_connected? if @coalesced_connection
@io && @io.state == :connected
end
|
#match?(uri, options) ⇒ Boolean
125
126
127
128
129
130
131
132
133
134
135
136
|
# File 'lib/httpx/connection.rb', line 125
def match?(uri, options)
return false if !used? && (@state == :closing || @state == :closed)
(
@origins.include?(uri.origin) &&
(@origins.size == 1 || @origin == uri.origin || (@io.is_a?(SSL) && @io.verify_hostname(uri.host)))
) && @options == options
end
|
#merge(connection) ⇒ Object
172
173
174
175
176
177
178
179
180
181
182
183
|
# File 'lib/httpx/connection.rb', line 172
def merge(connection)
@origins |= connection.instance_variable_get(:@origins)
if connection.ssl_session
@ssl_session = connection.ssl_session
@io.session_new_cb do |sess|
@ssl_session = sess
end if @io
end
connection.purge_pending do |req|
send(req)
end
end
|
#mergeable?(connection) ⇒ Boolean
144
145
146
147
148
149
150
151
152
153
|
# File 'lib/httpx/connection.rb', line 144
def mergeable?(connection)
return false if @state == :closing || @state == :closed || !@io
return false unless connection.addresses
(
(open? && @origin == connection.origin) ||
!(@io.addresses & (connection.addresses || [])).empty?
) && @options == connection.options
end
|
#open? ⇒ Boolean
334
335
336
|
# File 'lib/httpx/connection.rb', line 334
def open?
@state == :open || @state == :inactive
end
|
#peer ⇒ Object
107
108
109
|
# File 'lib/httpx/connection.rb', line 107
def peer
@origin
end
|
#purge_pending(&block) ⇒ Object
185
186
187
188
189
190
191
192
193
194
195
|
# File 'lib/httpx/connection.rb', line 185
def purge_pending(&block)
pendings = []
if @parser
@inflight -= @parser.pending.size
pendings << @parser.pending
end
pendings << @pending
pendings.each do |pending|
pending.reject!(&block)
end
end
|
#reset ⇒ Object
278
279
280
281
282
283
284
|
# File 'lib/httpx/connection.rb', line 278
def reset
return if @state == :closing || @state == :closed
transition(:closing)
transition(:closed)
end
|
#send(request) ⇒ Object
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
|
# File 'lib/httpx/connection.rb', line 286
def send(request)
return @coalesced_connection.send(request) if @coalesced_connection
if @parser && !@write_buffer.full?
if @response_received_at && @keep_alive_timeout &&
Utils.elapsed_time(@response_received_at) > @keep_alive_timeout
log(level: 3) { "keep alive timeout expired, pinging connection..." }
@pending << request
transition(:active) if @state == :inactive
parser.ping
request.ping!
return
end
send_request_to_parser(request)
else
@pending << request
end
end
|
#terminate ⇒ Object
264
265
266
267
268
|
# File 'lib/httpx/connection.rb', line 264
def terminate
@connected_at = nil if @state == :closed
close
end
|
#timeout ⇒ Object
309
310
311
312
313
314
315
316
317
|
# File 'lib/httpx/connection.rb', line 309
def timeout
return if @state == :closed || @state == :inactive
return @timeout if @timeout
return @options.timeout[:connect_timeout] if @state == :idle
@options.timeout[:operation_timeout]
end
|
#to_io ⇒ Object
235
236
237
|
# File 'lib/httpx/connection.rb', line 235
def to_io
@io.to_io
end
|
#used? ⇒ Boolean
326
327
328
|
# File 'lib/httpx/connection.rb', line 326
def used?
@connected_at
end
|