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:
- 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.
Defined Under Namespace
Classes: HTTP1, HTTP2
Constant Summary
Constants included
from Loggable
Loggable::COLORS
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
Methods included from Callbacks
#callbacks_for?, #emit, #on, #once, #only
Methods included from Loggable
#log, #log_exception
Constructor Details
#initialize(uri, options) ⇒ Connection
Returns a new instance of Connection.
50
51
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
|
# File 'lib/httpx/connection.rb', line 50
def initialize(uri, options)
@origins = [uri.origin]
@origin = Utils.to_uri(uri.origin)
@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
@inflight = 0
@keep_alive_timeout = @options.timeout[:keep_alive_timeout]
@intervals = []
self.addresses = @options.addresses if @options.addresses
end
|
Instance Attribute Details
#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
|
#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
|
#timers=(value) ⇒ Object
Sets the attribute timers
46
47
48
|
# File 'lib/httpx/connection.rb', line 46
def timers=(value)
@timers = value
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
729
730
731
732
733
734
735
736
|
# File 'lib/httpx/connection.rb', line 729
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
90
91
92
|
# File 'lib/httpx/connection.rb', line 90
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.
82
83
84
85
86
87
88
|
# File 'lib/httpx/connection.rb', line 82
def addresses=(addrs)
if @io
@io.add_addresses(addrs)
else
@io = build_socket(addrs)
end
end
|
#call ⇒ Object
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
|
# File 'lib/httpx/connection.rb', line 194
def call
case @state
when :idle
connect
consume
when :closed
return
when :closing
consume
transition(:closed)
when :open
consume
end
nil
end
|
#close ⇒ Object
210
211
212
213
214
|
# File 'lib/httpx/connection.rb', line 210
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?
126
127
128
129
130
131
132
133
134
135
|
# File 'lib/httpx/connection.rb', line 126
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
|
#connecting? ⇒ Boolean
166
167
168
|
# File 'lib/httpx/connection.rb', line 166
def connecting?
@state == :idle
end
|
#create_idle(options = {}) ⇒ Object
137
138
139
|
# File 'lib/httpx/connection.rb', line 137
def create_idle(options = {})
self.class.new(@origin, @options.merge(options))
end
|
#deactivate ⇒ Object
276
277
278
|
# File 'lib/httpx/connection.rb', line 276
def deactivate
transition(:inactive)
end
|
#expired? ⇒ Boolean
107
108
109
110
111
|
# File 'lib/httpx/connection.rb', line 107
def expired?
return false unless @io
@io.expired?
end
|
#force_reset ⇒ Object
bypasses the state machine to force closing of connections still connecting.
only used for Happy Eyeballs v2.
224
225
226
227
|
# File 'lib/httpx/connection.rb', line 224
def force_reset
@state = :closing
transition(:closed)
end
|
#handle_socket_timeout(interval) ⇒ Object
284
285
286
287
288
289
290
291
292
293
294
295
296
|
# File 'lib/httpx/connection.rb', line 284
def handle_socket_timeout(interval)
@intervals.delete_if(&:elapsed?)
unless @intervals.empty?
return
end
error = HTTPX::TimeoutError.new(interval, "timed out while waiting on select")
error.set_backtrace(caller)
on_error(error)
end
|
#idling ⇒ Object
265
266
267
268
269
270
|
# File 'lib/httpx/connection.rb', line 265
def idling
purge_after_closed
@write_buffer.clear
transition(:idle)
@parser = nil if @parser
end
|
#inflight? ⇒ Boolean
170
171
172
|
# File 'lib/httpx/connection.rb', line 170
def inflight?
@parser && !@parser.empty? && !@write_buffer.empty?
end
|
#interests ⇒ Object
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
|
# File 'lib/httpx/connection.rb', line 174
def interests
if connecting?
connect
return @io.interests if connecting?
end
return :w unless @write_buffer.empty?
return @parser.interests if @parser
nil
end
|
#match?(uri, options) ⇒ Boolean
94
95
96
97
98
99
100
101
102
103
104
105
|
# File 'lib/httpx/connection.rb', line 94
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
141
142
143
144
145
146
147
148
149
150
151
152
|
# File 'lib/httpx/connection.rb', line 141
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
113
114
115
116
117
118
119
120
121
122
|
# File 'lib/httpx/connection.rb', line 113
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
280
281
282
|
# File 'lib/httpx/connection.rb', line 280
def open?
@state == :open || @state == :inactive
end
|
#purge_pending(&block) ⇒ Object
154
155
156
157
158
159
160
161
162
163
164
|
# File 'lib/httpx/connection.rb', line 154
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
229
230
231
232
233
234
235
|
# File 'lib/httpx/connection.rb', line 229
def reset
return if @state == :closing || @state == :closed
transition(:closing)
transition(:closed)
end
|
#send(request) ⇒ Object
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
|
# File 'lib/httpx/connection.rb', line 237
def send(request)
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
return
end
send_request_to_parser(request)
else
@pending << request
end
end
|
#terminate ⇒ Object
216
217
218
219
220
|
# File 'lib/httpx/connection.rb', line 216
def terminate
@connected_at = nil if @state == :closed
close
end
|
#timeout ⇒ Object
257
258
259
260
261
262
263
|
# File 'lib/httpx/connection.rb', line 257
def timeout
return @timeout if @timeout
return @options.timeout[:connect_timeout] if @state == :idle
@options.timeout[:operation_timeout]
end
|
#to_io ⇒ Object
190
191
192
|
# File 'lib/httpx/connection.rb', line 190
def to_io
@io.to_io
end
|
#used? ⇒ Boolean
272
273
274
|
# File 'lib/httpx/connection.rb', line 272
def used?
@connected_at
end
|