httpx 0.11.3 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/doc/release_notes/0_10_1.md +1 -1
- data/doc/release_notes/0_11_1.md +5 -1
- data/doc/release_notes/0_12_0.md +55 -0
- data/doc/release_notes/0_13_0.md +58 -0
- data/doc/release_notes/0_13_1.md +5 -0
- data/doc/release_notes/0_13_2.md +9 -0
- data/doc/release_notes/0_14_0.md +79 -0
- data/lib/httpx.rb +3 -3
- data/lib/httpx/adapters/faraday.rb +4 -6
- data/lib/httpx/altsvc.rb +1 -0
- data/lib/httpx/callbacks.rb +12 -3
- data/lib/httpx/chainable.rb +2 -2
- data/lib/httpx/connection.rb +92 -37
- data/lib/httpx/connection/http1.rb +37 -19
- data/lib/httpx/connection/http2.rb +82 -31
- data/lib/httpx/headers.rb +1 -1
- data/lib/httpx/io.rb +16 -3
- data/lib/httpx/io/ssl.rb +35 -24
- data/lib/httpx/io/tcp.rb +50 -28
- data/lib/httpx/io/tls.rb +218 -0
- data/lib/httpx/io/tls/box.rb +365 -0
- data/lib/httpx/io/tls/context.rb +199 -0
- data/lib/httpx/io/tls/ffi.rb +390 -0
- data/lib/httpx/io/udp.rb +31 -7
- data/lib/httpx/io/unix.rb +27 -12
- data/lib/httpx/options.rb +97 -74
- data/lib/httpx/parser/http1.rb +4 -4
- data/lib/httpx/plugins/aws_sdk_authentication.rb +84 -0
- data/lib/httpx/plugins/aws_sigv4.rb +219 -0
- data/lib/httpx/plugins/basic_authentication.rb +8 -3
- data/lib/httpx/plugins/compression.rb +24 -12
- data/lib/httpx/plugins/compression/brotli.rb +10 -7
- data/lib/httpx/plugins/compression/deflate.rb +8 -10
- data/lib/httpx/plugins/compression/gzip.rb +4 -3
- data/lib/httpx/plugins/cookies.rb +3 -7
- data/lib/httpx/plugins/digest_authentication.rb +5 -5
- data/lib/httpx/plugins/expect.rb +6 -6
- data/lib/httpx/plugins/follow_redirects.rb +4 -4
- data/lib/httpx/plugins/grpc.rb +247 -0
- data/lib/httpx/plugins/grpc/call.rb +62 -0
- data/lib/httpx/plugins/grpc/message.rb +85 -0
- data/lib/httpx/plugins/h2c.rb +43 -58
- data/lib/httpx/plugins/internal_telemetry.rb +93 -0
- data/lib/httpx/plugins/multipart.rb +2 -0
- data/lib/httpx/plugins/multipart/encoder.rb +4 -9
- data/lib/httpx/plugins/multipart/part.rb +1 -1
- data/lib/httpx/plugins/proxy.rb +4 -8
- data/lib/httpx/plugins/proxy/http.rb +1 -1
- data/lib/httpx/plugins/proxy/socks4.rb +8 -0
- data/lib/httpx/plugins/proxy/socks5.rb +8 -0
- data/lib/httpx/plugins/proxy/ssh.rb +3 -3
- data/lib/httpx/plugins/push_promise.rb +3 -2
- data/lib/httpx/plugins/rate_limiter.rb +1 -1
- data/lib/httpx/plugins/retries.rb +15 -16
- data/lib/httpx/plugins/stream.rb +99 -77
- data/lib/httpx/plugins/upgrade.rb +84 -0
- data/lib/httpx/plugins/upgrade/h2.rb +54 -0
- data/lib/httpx/pool.rb +14 -6
- data/lib/httpx/registry.rb +1 -7
- data/lib/httpx/request.rb +36 -3
- data/lib/httpx/resolver/https.rb +3 -11
- data/lib/httpx/resolver/native.rb +7 -3
- data/lib/httpx/response.rb +18 -7
- data/lib/httpx/selector.rb +5 -0
- data/lib/httpx/session.rb +41 -8
- data/lib/httpx/transcoder/body.rb +3 -5
- data/lib/httpx/transcoder/chunker.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/callbacks.rbs +2 -0
- data/sig/chainable.rbs +2 -1
- data/sig/connection/http1.rbs +7 -2
- data/sig/connection/http2.rbs +10 -4
- data/sig/options.rbs +16 -22
- data/sig/plugins/aws_sdk_authentication.rbs +19 -0
- data/sig/plugins/aws_sigv4.rbs +64 -0
- data/sig/plugins/basic_authentication.rbs +2 -0
- data/sig/plugins/compression.rbs +7 -5
- data/sig/plugins/compression/brotli.rbs +1 -1
- data/sig/plugins/compression/deflate.rbs +1 -1
- data/sig/plugins/compression/gzip.rbs +1 -1
- data/sig/plugins/cookies.rbs +0 -1
- data/sig/plugins/digest_authentication.rbs +0 -1
- data/sig/plugins/expect.rbs +0 -2
- data/sig/plugins/follow_redirects.rbs +0 -2
- data/sig/plugins/h2c.rbs +5 -10
- data/sig/plugins/persistent.rbs +0 -1
- data/sig/plugins/proxy.rbs +0 -1
- data/sig/plugins/push_promise.rbs +1 -1
- data/sig/plugins/retries.rbs +0 -4
- data/sig/plugins/stream.rbs +17 -16
- data/sig/plugins/upgrade.rbs +23 -0
- data/sig/request.rbs +7 -2
- data/sig/response.rbs +4 -1
- data/sig/session.rbs +4 -0
- metadata +56 -33
- data/lib/httpx/timeout.rb +0 -67
- data/sig/timeout.rbs +0 -29
@@ -10,7 +10,7 @@ module HTTPX
|
|
10
10
|
MAX_REQUESTS = 100
|
11
11
|
CRLF = "\r\n"
|
12
12
|
|
13
|
-
attr_reader :pending
|
13
|
+
attr_reader :pending, :requests
|
14
14
|
|
15
15
|
def initialize(buffer, options)
|
16
16
|
@options = Options.new(options)
|
@@ -74,9 +74,10 @@ module HTTPX
|
|
74
74
|
end
|
75
75
|
|
76
76
|
def consume
|
77
|
-
requests_limit = [@
|
77
|
+
requests_limit = [@max_requests, @requests.size].min
|
78
|
+
concurrent_requests_limit = [@max_concurrent_requests, requests_limit].min
|
78
79
|
@requests.each_with_index do |request, idx|
|
79
|
-
break if idx >=
|
80
|
+
break if idx >= concurrent_requests_limit
|
80
81
|
next if request.state == :done
|
81
82
|
|
82
83
|
request.headers["connection"] ||= request.options.persistent || idx < requests_limit - 1 ? "keep-alive" : "close"
|
@@ -115,7 +116,7 @@ module HTTPX
|
|
115
116
|
response = @request.response
|
116
117
|
log(level: 2) { "trailer headers received" }
|
117
118
|
|
118
|
-
log(color: :yellow) { h.each.map { |f, v| "-> HEADER: #{f}: #{v}" }.join("\n") }
|
119
|
+
log(color: :yellow) { h.each.map { |f, v| "-> HEADER: #{f}: #{v.join(", ")}" }.join("\n") }
|
119
120
|
response.merge_headers(h)
|
120
121
|
end
|
121
122
|
|
@@ -161,13 +162,13 @@ module HTTPX
|
|
161
162
|
end
|
162
163
|
|
163
164
|
def handle_error(ex)
|
164
|
-
if ex.is_a?(EOFError) && @request && @request.response &&
|
165
|
+
if (ex.is_a?(EOFError) || ex.is_a?(TimeoutError)) && @request && @request.response &&
|
165
166
|
!@request.response.headers.key?("content-length") &&
|
166
167
|
!@request.response.headers.key?("transfer-encoding")
|
167
168
|
# if the response does not contain a content-length header, the server closing the
|
168
169
|
# connnection is the indicator of response consumed.
|
169
170
|
# https://greenbytes.de/tech/webdav/rfc2616.html#rfc.section.4.4
|
170
|
-
on_complete
|
171
|
+
catch(:called) { on_complete }
|
171
172
|
return
|
172
173
|
end
|
173
174
|
|
@@ -234,6 +235,8 @@ module HTTPX
|
|
234
235
|
|
235
236
|
def disable_pipelining
|
236
237
|
return if @requests.empty?
|
238
|
+
# do not disable pipelining if already set to 1 request at a time
|
239
|
+
return if @max_concurrent_requests == 1
|
237
240
|
|
238
241
|
@requests.each do |r|
|
239
242
|
r.transition(:idle)
|
@@ -250,7 +253,7 @@ module HTTPX
|
|
250
253
|
@pipelining = false
|
251
254
|
end
|
252
255
|
|
253
|
-
def
|
256
|
+
def set_protocol_headers(request)
|
254
257
|
request.headers["host"] ||= request.authority
|
255
258
|
request.headers["connection"] ||= request.options.persistent ? "keep-alive" : "close"
|
256
259
|
if !request.headers.key?("content-length") &&
|
@@ -264,28 +267,23 @@ module HTTPX
|
|
264
267
|
end
|
265
268
|
|
266
269
|
def handle(request)
|
267
|
-
set_request_headers(request)
|
268
270
|
catch(:buffer_full) do
|
269
271
|
request.transition(:headers)
|
270
272
|
join_headers(request) if request.state == :headers
|
271
273
|
request.transition(:body)
|
272
274
|
join_body(request) if request.state == :body
|
275
|
+
request.transition(:trailers)
|
276
|
+
# HTTP/1.1 trailers should only work for chunked encoding
|
277
|
+
join_trailers(request) if request.body.chunked? && request.state == :trailers
|
273
278
|
request.transition(:done)
|
274
279
|
end
|
275
280
|
end
|
276
281
|
|
277
282
|
def join_headers(request)
|
278
|
-
buffer
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
buffer.clear
|
283
|
-
request.headers.each do |field, value|
|
284
|
-
buffer << "#{capitalized(field)}: #{value}" << CRLF
|
285
|
-
log(color: :yellow) { "<- HEADER: #{buffer.chomp}" }
|
286
|
-
@buffer << buffer
|
287
|
-
buffer.clear
|
288
|
-
end
|
283
|
+
@buffer << "#{request.verb.to_s.upcase} #{headline_uri(request)} HTTP/#{@version.join(".")}" << CRLF
|
284
|
+
log(color: :yellow) { "<- HEADLINE: #{@buffer.to_s.chomp.inspect}" }
|
285
|
+
set_protocol_headers(request)
|
286
|
+
join_headers2(request.headers)
|
289
287
|
log { "<- " }
|
290
288
|
@buffer << CRLF
|
291
289
|
end
|
@@ -299,6 +297,26 @@ module HTTPX
|
|
299
297
|
@buffer << chunk
|
300
298
|
throw(:buffer_full, request) if @buffer.full?
|
301
299
|
end
|
300
|
+
|
301
|
+
raise request.drain_error if request.drain_error
|
302
|
+
end
|
303
|
+
|
304
|
+
def join_trailers(request)
|
305
|
+
return unless request.trailers? && request.callbacks_for?(:trailers)
|
306
|
+
|
307
|
+
join_headers2(request.trailers)
|
308
|
+
log { "<- " }
|
309
|
+
@buffer << CRLF
|
310
|
+
end
|
311
|
+
|
312
|
+
def join_headers2(headers)
|
313
|
+
buffer = "".b
|
314
|
+
headers.each do |field, value|
|
315
|
+
buffer << "#{capitalized(field)}: #{value}" << CRLF
|
316
|
+
log(color: :yellow) { "<- HEADER: #{buffer.chomp}" }
|
317
|
+
@buffer << buffer
|
318
|
+
buffer.clear
|
319
|
+
end
|
302
320
|
end
|
303
321
|
|
304
322
|
UPCASED = {
|
@@ -21,14 +21,16 @@ module HTTPX
|
|
21
21
|
|
22
22
|
def initialize(buffer, options)
|
23
23
|
@options = Options.new(options)
|
24
|
-
@
|
25
|
-
@max_requests = @options.max_requests || 0
|
24
|
+
@settings = @options.http2_settings
|
26
25
|
@pending = []
|
27
26
|
@streams = {}
|
28
27
|
@drains = {}
|
29
28
|
@pings = []
|
30
29
|
@buffer = buffer
|
31
30
|
@handshake_completed = false
|
31
|
+
@wait_for_handshake = @settings.key?(:wait_for_handshake) ? @settings.delete(:wait_for_handshake) : true
|
32
|
+
@max_concurrent_requests = @options.max_concurrent_requests || MAX_CONCURRENT_REQUESTS
|
33
|
+
@max_requests = @options.max_requests || 0
|
32
34
|
init_connection
|
33
35
|
end
|
34
36
|
|
@@ -36,17 +38,25 @@ module HTTPX
|
|
36
38
|
# waiting for WINDOW_UPDATE frames
|
37
39
|
return :r if @buffer.full?
|
38
40
|
|
39
|
-
|
41
|
+
if @connection.state == :closed
|
42
|
+
return unless @handshake_completed
|
43
|
+
|
44
|
+
return :w
|
45
|
+
end
|
40
46
|
|
41
47
|
unless (@connection.state == :connected && @handshake_completed)
|
42
48
|
return @buffer.empty? ? :r : :rw
|
43
49
|
end
|
44
50
|
|
45
|
-
return :w
|
51
|
+
return :w if !@pending.empty? && can_buffer_more_requests?
|
46
52
|
|
47
53
|
return :w if @streams.each_key.any? { |r| r.interests == :w }
|
48
54
|
|
49
|
-
|
55
|
+
if @buffer.empty?
|
56
|
+
return if @streams.empty? && @pings.empty?
|
57
|
+
|
58
|
+
return :r
|
59
|
+
end
|
50
60
|
|
51
61
|
:rw
|
52
62
|
end
|
@@ -70,10 +80,18 @@ module HTTPX
|
|
70
80
|
@connection << data
|
71
81
|
end
|
72
82
|
|
83
|
+
def can_buffer_more_requests?
|
84
|
+
if @handshake_completed
|
85
|
+
@streams.size < @max_concurrent_requests &&
|
86
|
+
@streams.size < @max_requests
|
87
|
+
else
|
88
|
+
!@wait_for_handshake &&
|
89
|
+
@streams.size < @max_concurrent_requests
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
73
93
|
def send(request)
|
74
|
-
|
75
|
-
@streams.size >= @max_concurrent_requests ||
|
76
|
-
@streams.size >= @max_requests
|
94
|
+
unless can_buffer_more_requests?
|
77
95
|
@pending << request
|
78
96
|
return
|
79
97
|
end
|
@@ -126,20 +144,20 @@ module HTTPX
|
|
126
144
|
request.path
|
127
145
|
end
|
128
146
|
|
129
|
-
def set_request_headers(request); end
|
130
|
-
|
131
147
|
def handle(request, stream)
|
132
148
|
catch(:buffer_full) do
|
133
149
|
request.transition(:headers)
|
134
150
|
join_headers(stream, request) if request.state == :headers
|
135
151
|
request.transition(:body)
|
136
152
|
join_body(stream, request) if request.state == :body
|
153
|
+
request.transition(:trailers)
|
154
|
+
join_trailers(stream, request) if request.state == :trailers && !request.body.empty?
|
137
155
|
request.transition(:done)
|
138
156
|
end
|
139
157
|
end
|
140
158
|
|
141
159
|
def init_connection
|
142
|
-
@connection = HTTP2Next::Client.new(@
|
160
|
+
@connection = HTTP2Next::Client.new(@settings)
|
143
161
|
@connection.max_streams = @max_requests if @connection.respond_to?(:max_streams=) && @max_requests.positive?
|
144
162
|
@connection.on(:frame, &method(:on_frame))
|
145
163
|
@connection.on(:frame_sent, &method(:on_frame_sent))
|
@@ -163,6 +181,7 @@ module HTTPX
|
|
163
181
|
public :reset
|
164
182
|
|
165
183
|
def handle_stream(stream, request)
|
184
|
+
request.on(:refuse, &method(:on_stream_refuse).curry(3)[stream, request])
|
166
185
|
stream.on(:close, &method(:on_stream_close).curry(3)[stream, request])
|
167
186
|
stream.on(:half_close) do
|
168
187
|
log(level: 2) { "#{stream.id}: waiting for response..." }
|
@@ -172,18 +191,31 @@ module HTTPX
|
|
172
191
|
stream.on(:data, &method(:on_stream_data).curry(3)[stream, request])
|
173
192
|
end
|
174
193
|
|
194
|
+
def set_protocol_headers(request)
|
195
|
+
request.headers[":scheme"] = request.scheme
|
196
|
+
request.headers[":method"] = request.verb.to_s.upcase
|
197
|
+
request.headers[":path"] = headline_uri(request)
|
198
|
+
request.headers[":authority"] = request.authority
|
199
|
+
end
|
200
|
+
|
175
201
|
def join_headers(stream, request)
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
headers
|
181
|
-
|
182
|
-
|
202
|
+
set_protocol_headers(request)
|
203
|
+
log(level: 1, color: :yellow) do
|
204
|
+
request.headers.each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{v}" }.join("\n")
|
205
|
+
end
|
206
|
+
stream.headers(request.headers.each, end_stream: request.empty?)
|
207
|
+
end
|
208
|
+
|
209
|
+
def join_trailers(stream, request)
|
210
|
+
unless request.trailers?
|
211
|
+
stream.data("", end_stream: true) if request.callbacks_for?(:trailers)
|
212
|
+
return
|
213
|
+
end
|
214
|
+
|
183
215
|
log(level: 1, color: :yellow) do
|
184
|
-
|
216
|
+
request.trailers.each.map { |k, v| "#{stream.id}: -> HEADER: #{k}: #{v}" }.join("\n")
|
185
217
|
end
|
186
|
-
stream.headers(
|
218
|
+
stream.headers(request.trailers.each, end_stream: true)
|
187
219
|
end
|
188
220
|
|
189
221
|
def join_body(stream, request)
|
@@ -194,13 +226,15 @@ module HTTPX
|
|
194
226
|
next_chunk = request.drain_body
|
195
227
|
log(level: 1, color: :green) { "#{stream.id}: -> DATA: #{chunk.bytesize} bytes..." }
|
196
228
|
log(level: 2, color: :green) { "#{stream.id}: -> #{chunk.inspect}" }
|
197
|
-
stream.data(chunk, end_stream: !next_chunk)
|
198
|
-
if next_chunk && @buffer.full?
|
229
|
+
stream.data(chunk, end_stream: !(next_chunk || request.trailers? || request.callbacks_for?(:trailers)))
|
230
|
+
if next_chunk && (@buffer.full? || request.body.unbounded_body?)
|
199
231
|
@drains[request] = next_chunk
|
200
232
|
throw(:buffer_full)
|
201
233
|
end
|
202
234
|
chunk = next_chunk
|
203
235
|
end
|
236
|
+
|
237
|
+
on_stream_refuse(stream, request, request.drain_error) if request.drain_error
|
204
238
|
end
|
205
239
|
|
206
240
|
######
|
@@ -208,6 +242,11 @@ module HTTPX
|
|
208
242
|
######
|
209
243
|
|
210
244
|
def on_stream_headers(stream, request, h)
|
245
|
+
if request.response && request.response.version == "2.0"
|
246
|
+
on_stream_trailers(stream, request, h)
|
247
|
+
return
|
248
|
+
end
|
249
|
+
|
211
250
|
log(color: :yellow) do
|
212
251
|
h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{v}" }.join("\n")
|
213
252
|
end
|
@@ -220,17 +259,34 @@ module HTTPX
|
|
220
259
|
handle(request, stream) if request.expects?
|
221
260
|
end
|
222
261
|
|
262
|
+
def on_stream_trailers(stream, request, h)
|
263
|
+
log(color: :yellow) do
|
264
|
+
h.map { |k, v| "#{stream.id}: <- HEADER: #{k}: #{v}" }.join("\n")
|
265
|
+
end
|
266
|
+
request.response.merge_headers(h)
|
267
|
+
end
|
268
|
+
|
223
269
|
def on_stream_data(stream, request, data)
|
224
270
|
log(level: 1, color: :green) { "#{stream.id}: <- DATA: #{data.bytesize} bytes..." }
|
225
271
|
log(level: 2, color: :green) { "#{stream.id}: <- #{data.inspect}" }
|
226
272
|
request.response << data
|
227
273
|
end
|
228
274
|
|
275
|
+
def on_stream_refuse(stream, request, error)
|
276
|
+
stream.close
|
277
|
+
on_stream_close(stream, request, error)
|
278
|
+
end
|
279
|
+
|
229
280
|
def on_stream_close(stream, request, error)
|
281
|
+
log(level: 2) { "#{stream.id}: closing stream" }
|
282
|
+
@drains.delete(request)
|
283
|
+
@streams.delete(request)
|
284
|
+
|
230
285
|
if error && error != :no_error
|
231
286
|
ex = Error.new(stream.id, error)
|
232
287
|
ex.set_backtrace(caller)
|
233
|
-
|
288
|
+
response = ErrorResponse.new(request, ex, request.options)
|
289
|
+
emit(:response, request, response)
|
234
290
|
else
|
235
291
|
response = request.response
|
236
292
|
if response.status == 421
|
@@ -241,9 +297,6 @@ module HTTPX
|
|
241
297
|
emit(:response, request, response)
|
242
298
|
end
|
243
299
|
end
|
244
|
-
log(level: 2) { "#{stream.id}: closing stream" }
|
245
|
-
|
246
|
-
@streams.delete(request)
|
247
300
|
send(@pending.shift) unless @pending.empty?
|
248
301
|
return unless @streams.empty? && exhausted?
|
249
302
|
|
@@ -328,11 +381,9 @@ module HTTPX
|
|
328
381
|
end
|
329
382
|
|
330
383
|
def method_missing(meth, *args, &blk)
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
super
|
335
|
-
end
|
384
|
+
return super unless @connection.respond_to?(meth)
|
385
|
+
|
386
|
+
@connection.__send__(meth, *args, &blk)
|
336
387
|
end
|
337
388
|
end
|
338
389
|
Connection.register "h2", Connection::HTTP2
|
data/lib/httpx/headers.rb
CHANGED
data/lib/httpx/io.rb
CHANGED
@@ -2,16 +2,29 @@
|
|
2
2
|
|
3
3
|
require "socket"
|
4
4
|
require "httpx/io/tcp"
|
5
|
-
require "httpx/io/ssl"
|
6
5
|
require "httpx/io/unix"
|
7
6
|
require "httpx/io/udp"
|
8
7
|
|
9
8
|
module HTTPX
|
10
9
|
module IO
|
11
10
|
extend Registry
|
12
|
-
register "tcp", TCP
|
13
|
-
register "ssl", SSL
|
14
11
|
register "udp", UDP
|
15
12
|
register "unix", HTTPX::UNIX
|
13
|
+
register "tcp", TCP
|
14
|
+
|
15
|
+
if RUBY_ENGINE == "jruby"
|
16
|
+
begin
|
17
|
+
require "httpx/io/tls"
|
18
|
+
register "ssl", TLS
|
19
|
+
rescue LoadError
|
20
|
+
# :nocov:
|
21
|
+
require "httpx/io/ssl"
|
22
|
+
register "ssl", SSL
|
23
|
+
# :nocov:
|
24
|
+
end
|
25
|
+
else
|
26
|
+
require "httpx/io/ssl"
|
27
|
+
register "ssl", SSL
|
28
|
+
end
|
16
29
|
end
|
17
30
|
end
|
data/lib/httpx/io/ssl.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require "openssl"
|
4
4
|
|
5
5
|
module HTTPX
|
6
|
+
TLSError = OpenSSL::SSL::SSLError
|
7
|
+
|
6
8
|
class SSL < TCP
|
7
9
|
TLS_OPTIONS = if OpenSSL::SSL::SSLContext.instance_methods.include?(:alpn_protocols)
|
8
10
|
{ alpn_protocols: %w[h2 http/1.1] }
|
@@ -11,19 +13,14 @@ module HTTPX
|
|
11
13
|
end
|
12
14
|
|
13
15
|
def initialize(_, _, options)
|
16
|
+
super
|
14
17
|
@ctx = OpenSSL::SSL::SSLContext.new
|
15
18
|
ctx_options = TLS_OPTIONS.merge(options.ssl)
|
16
|
-
@
|
19
|
+
@sni_hostname = ctx_options.delete(:hostname) || @hostname
|
17
20
|
@ctx.set_params(ctx_options) unless ctx_options.empty?
|
18
|
-
super
|
19
|
-
@tls_hostname ||= @hostname
|
20
21
|
@state = :negotiated if @keep_open
|
21
22
|
end
|
22
23
|
|
23
|
-
def interests
|
24
|
-
@interests || super
|
25
|
-
end
|
26
|
-
|
27
24
|
def protocol
|
28
25
|
@io.alpn_protocol || super
|
29
26
|
rescue StandardError
|
@@ -50,28 +47,30 @@ module HTTPX
|
|
50
47
|
|
51
48
|
def connect
|
52
49
|
super
|
53
|
-
if @keep_open
|
54
|
-
@state = :negotiated
|
55
|
-
return
|
56
|
-
end
|
57
50
|
return if @state == :negotiated ||
|
58
51
|
@state != :connected
|
59
52
|
|
60
53
|
unless @io.is_a?(OpenSSL::SSL::SSLSocket)
|
61
54
|
@io = OpenSSL::SSL::SSLSocket.new(@io, @ctx)
|
62
|
-
@io.hostname = @
|
55
|
+
@io.hostname = @sni_hostname
|
63
56
|
@io.sync_close = true
|
64
57
|
end
|
65
|
-
|
66
|
-
@io.post_connection_check(@tls_hostname) if @ctx.verify_mode != OpenSSL::SSL::VERIFY_NONE
|
67
|
-
transition(:negotiated)
|
68
|
-
rescue ::IO::WaitReadable
|
69
|
-
@interests = :r
|
70
|
-
rescue ::IO::WaitWritable
|
71
|
-
@interests = :w
|
58
|
+
try_ssl_connect
|
72
59
|
end
|
73
60
|
|
74
61
|
if RUBY_VERSION < "2.3"
|
62
|
+
# :nocov:
|
63
|
+
def try_ssl_connect
|
64
|
+
@io.connect_nonblock
|
65
|
+
@io.post_connection_check(@sni_hostname) if @ctx.verify_mode != OpenSSL::SSL::VERIFY_NONE
|
66
|
+
transition(:negotiated)
|
67
|
+
@interests = :w
|
68
|
+
rescue ::IO::WaitReadable
|
69
|
+
@interests = :r
|
70
|
+
rescue ::IO::WaitWritable
|
71
|
+
@interests = :w
|
72
|
+
end
|
73
|
+
|
75
74
|
def read(_, buffer)
|
76
75
|
super
|
77
76
|
rescue ::IO::WaitWritable
|
@@ -84,7 +83,23 @@ module HTTPX
|
|
84
83
|
rescue ::IO::WaitReadable
|
85
84
|
0
|
86
85
|
end
|
86
|
+
# :nocov:
|
87
87
|
else
|
88
|
+
def try_ssl_connect
|
89
|
+
case @io.connect_nonblock(exception: false)
|
90
|
+
when :wait_readable
|
91
|
+
@interests = :r
|
92
|
+
return
|
93
|
+
when :wait_writable
|
94
|
+
@interests = :w
|
95
|
+
return
|
96
|
+
end
|
97
|
+
@io.post_connection_check(@sni_hostname) if @ctx.verify_mode != OpenSSL::SSL::VERIFY_NONE
|
98
|
+
transition(:negotiated)
|
99
|
+
@interests = :w
|
100
|
+
end
|
101
|
+
|
102
|
+
# :nocov:
|
88
103
|
if OpenSSL::VERSION < "2.0.6"
|
89
104
|
def read(size, buffer)
|
90
105
|
@io.read_nonblock(size, buffer)
|
@@ -97,11 +112,7 @@ module HTTPX
|
|
97
112
|
nil
|
98
113
|
end
|
99
114
|
end
|
100
|
-
|
101
|
-
|
102
|
-
def inspect
|
103
|
-
id = @io.closed? ? "closed" : @io.to_io.fileno
|
104
|
-
"#<SSL(fd: #{id}): #{@ip}:#{@port} state: #{@state}>"
|
115
|
+
# :nocov:
|
105
116
|
end
|
106
117
|
|
107
118
|
private
|