httpx 0.16.1 → 0.18.2
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 +4 -3
- data/doc/release_notes/0_17_0.md +49 -0
- data/doc/release_notes/0_18_0.md +69 -0
- data/doc/release_notes/0_18_1.md +12 -0
- data/doc/release_notes/0_18_2.md +10 -0
- data/lib/httpx/adapters/datadog.rb +1 -1
- data/lib/httpx/adapters/faraday.rb +5 -3
- data/lib/httpx/adapters/webmock.rb +9 -3
- data/lib/httpx/altsvc.rb +2 -2
- data/lib/httpx/chainable.rb +4 -4
- data/lib/httpx/connection/http1.rb +23 -14
- data/lib/httpx/connection/http2.rb +35 -17
- data/lib/httpx/connection.rb +74 -76
- data/lib/httpx/domain_name.rb +1 -1
- data/lib/httpx/extensions.rb +50 -4
- data/lib/httpx/headers.rb +1 -1
- data/lib/httpx/io/ssl.rb +5 -1
- data/lib/httpx/io/tls.rb +7 -7
- data/lib/httpx/loggable.rb +5 -5
- data/lib/httpx/options.rb +35 -13
- data/lib/httpx/parser/http1.rb +10 -6
- data/lib/httpx/plugins/aws_sdk_authentication.rb +42 -18
- data/lib/httpx/plugins/aws_sigv4.rb +9 -11
- data/lib/httpx/plugins/compression.rb +5 -3
- data/lib/httpx/plugins/cookies/jar.rb +1 -1
- data/lib/httpx/plugins/digest_authentication.rb +4 -4
- data/lib/httpx/plugins/expect.rb +7 -3
- data/lib/httpx/plugins/grpc/message.rb +2 -2
- data/lib/httpx/plugins/grpc.rb +3 -3
- data/lib/httpx/plugins/h2c.rb +7 -3
- data/lib/httpx/plugins/internal_telemetry.rb +8 -8
- data/lib/httpx/plugins/multipart/decoder.rb +187 -0
- data/lib/httpx/plugins/multipart/mime_type_detector.rb +3 -3
- data/lib/httpx/plugins/multipart/part.rb +2 -2
- data/lib/httpx/plugins/multipart.rb +16 -2
- data/lib/httpx/plugins/ntlm_authentication.rb +4 -4
- data/lib/httpx/plugins/proxy/ssh.rb +11 -4
- data/lib/httpx/plugins/proxy.rb +6 -4
- data/lib/httpx/plugins/response_cache/store.rb +55 -0
- data/lib/httpx/plugins/response_cache.rb +88 -0
- data/lib/httpx/plugins/retries.rb +36 -14
- data/lib/httpx/plugins/stream.rb +3 -4
- data/lib/httpx/pool.rb +39 -13
- data/lib/httpx/registry.rb +1 -1
- data/lib/httpx/request.rb +12 -13
- data/lib/httpx/resolver/https.rb +5 -7
- data/lib/httpx/resolver/native.rb +4 -2
- data/lib/httpx/resolver/resolver_mixin.rb +2 -1
- data/lib/httpx/resolver/system.rb +2 -0
- data/lib/httpx/resolver.rb +2 -2
- data/lib/httpx/response.rb +60 -44
- data/lib/httpx/selector.rb +16 -19
- data/lib/httpx/session.rb +22 -15
- data/lib/httpx/session2.rb +1 -1
- data/lib/httpx/timers.rb +84 -0
- data/lib/httpx/transcoder/body.rb +2 -1
- data/lib/httpx/transcoder/form.rb +20 -0
- data/lib/httpx/transcoder/json.rb +12 -0
- data/lib/httpx/transcoder.rb +62 -1
- data/lib/httpx/utils.rb +10 -2
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +1 -0
- data/sig/buffer.rbs +2 -2
- data/sig/chainable.rbs +7 -1
- data/sig/connection/http1.rbs +15 -4
- data/sig/connection/http2.rbs +19 -5
- data/sig/connection.rbs +15 -9
- data/sig/headers.rbs +19 -18
- data/sig/options.rbs +13 -5
- data/sig/parser/http1.rbs +3 -3
- data/sig/plugins/aws_sdk_authentication.rbs +22 -4
- data/sig/plugins/aws_sigv4.rbs +12 -3
- data/sig/plugins/basic_authentication.rbs +1 -1
- data/sig/plugins/multipart.rbs +64 -8
- data/sig/plugins/proxy.rbs +6 -6
- data/sig/plugins/response_cache.rbs +35 -0
- data/sig/plugins/retries.rbs +3 -0
- data/sig/pool.rbs +6 -0
- data/sig/request.rbs +11 -8
- data/sig/resolver/native.rbs +2 -1
- data/sig/resolver/resolver_mixin.rbs +1 -1
- data/sig/resolver/system.rbs +3 -1
- data/sig/response.rbs +11 -4
- data/sig/selector.rbs +8 -6
- data/sig/session.rbs +8 -14
- data/sig/timers.rbs +32 -0
- data/sig/transcoder/form.rbs +1 -0
- data/sig/transcoder/json.rbs +1 -0
- data/sig/transcoder.rbs +5 -4
- data/sig/utils.rbs +4 -0
- metadata +18 -17
data/lib/httpx/connection.rb
CHANGED
@@ -70,7 +70,7 @@ module HTTPX
|
|
70
70
|
|
71
71
|
@inflight = 0
|
72
72
|
@keep_alive_timeout = @options.timeout[:keep_alive_timeout]
|
73
|
-
@
|
73
|
+
@total_timeout = @options.timeout[:total_timeout]
|
74
74
|
|
75
75
|
self.addresses = @options.addresses if @options.addresses
|
76
76
|
end
|
@@ -117,7 +117,8 @@ module HTTPX
|
|
117
117
|
def coalescable?(connection)
|
118
118
|
if @io.protocol == "h2" &&
|
119
119
|
@origin.scheme == "https" &&
|
120
|
-
connection.origin.scheme == "https"
|
120
|
+
connection.origin.scheme == "https" &&
|
121
|
+
@io.can_verify_peer?
|
121
122
|
@io.verify_hostname(connection.origin.host)
|
122
123
|
else
|
123
124
|
@origin == connection.origin
|
@@ -200,11 +201,9 @@ module HTTPX
|
|
200
201
|
end
|
201
202
|
|
202
203
|
def close
|
203
|
-
|
204
|
-
return unless @keep_alive_timer
|
204
|
+
transition(:active) if @state == :inactive
|
205
205
|
|
206
|
-
@
|
207
|
-
remove_instance_variable(:@keep_alive_timer)
|
206
|
+
@parser.close if @parser
|
208
207
|
end
|
209
208
|
|
210
209
|
def reset
|
@@ -216,26 +215,40 @@ module HTTPX
|
|
216
215
|
def send(request)
|
217
216
|
if @parser && !@write_buffer.full?
|
218
217
|
request.headers["alt-used"] = @origin.authority if match_altsvcs?(request.uri)
|
219
|
-
|
218
|
+
|
219
|
+
if @response_received_at && @keep_alive_timeout &&
|
220
|
+
Utils.elapsed_time(@response_received_at) > @keep_alive_timeout
|
220
221
|
# when pushing a request into an existing connection, we have to check whether there
|
221
222
|
# is the possibility that the connection might have extended the keep alive timeout.
|
222
223
|
# for such cases, we want to ping for availability before deciding to shovel requests.
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
end
|
228
|
-
|
229
|
-
@keep_alive_timer.pause
|
224
|
+
@pending << request
|
225
|
+
parser.ping
|
226
|
+
transition(:active) if @state == :inactive
|
227
|
+
return
|
230
228
|
end
|
231
|
-
|
232
|
-
|
229
|
+
|
230
|
+
send_request_to_parser(request)
|
233
231
|
else
|
234
232
|
@pending << request
|
235
233
|
end
|
236
234
|
end
|
237
235
|
|
238
236
|
def timeout
|
237
|
+
if @total_timeout
|
238
|
+
return @total_timeout unless @connected_at
|
239
|
+
|
240
|
+
elapsed_time = @total_timeout - Utils.elapsed_time(@connected_at)
|
241
|
+
|
242
|
+
if elapsed_time.negative?
|
243
|
+
ex = TotalTimeoutError.new(@total_timeout, "Timed out after #{@total_timeout} seconds")
|
244
|
+
ex.set_backtrace(caller)
|
245
|
+
on_error(ex)
|
246
|
+
return
|
247
|
+
end
|
248
|
+
|
249
|
+
return elapsed_time
|
250
|
+
end
|
251
|
+
|
239
252
|
return @timeout if defined?(@timeout)
|
240
253
|
|
241
254
|
return @options.timeout[:connect_timeout] if @state == :idle
|
@@ -243,6 +256,14 @@ module HTTPX
|
|
243
256
|
@options.timeout[:operation_timeout]
|
244
257
|
end
|
245
258
|
|
259
|
+
def deactivate
|
260
|
+
transition(:inactive)
|
261
|
+
end
|
262
|
+
|
263
|
+
def open?
|
264
|
+
@state == :open || @state == :inactive
|
265
|
+
end
|
266
|
+
|
246
267
|
private
|
247
268
|
|
248
269
|
def connect
|
@@ -313,7 +334,7 @@ module HTTPX
|
|
313
334
|
|
314
335
|
# exit #consume altogether if all outstanding requests have been dealt with
|
315
336
|
return if @pending.size.zero? && @inflight.zero?
|
316
|
-
end unless (interests.nil? ||
|
337
|
+
end unless ((ints = interests).nil? || ints == :w || @state == :closing) && !epiped
|
317
338
|
|
318
339
|
#
|
319
340
|
# tight write loop.
|
@@ -360,19 +381,18 @@ module HTTPX
|
|
360
381
|
break if interests == :r || @state == :closing || @state == :closed
|
361
382
|
|
362
383
|
write_drained = false
|
363
|
-
end unless interests == :r
|
384
|
+
end unless (ints = interests) == :r
|
364
385
|
|
365
386
|
send_pending if @state == :open
|
366
387
|
|
367
388
|
# return if socket is drained
|
368
|
-
next unless (
|
369
|
-
(interests != :w || write_drained)
|
389
|
+
next unless (ints != :r || read_drained) && (ints != :w || write_drained)
|
370
390
|
|
371
391
|
# gotta go back to the event loop. It happens when:
|
372
392
|
#
|
373
393
|
# * the socket is drained of bytes or it's not the interest of the conn to read;
|
374
394
|
# * theres nothing more to write, or it's not in the interest of the conn to write;
|
375
|
-
log(level: 3) { "(#{
|
395
|
+
log(level: 3) { "(#{ints}): WAITING FOR EVENTS..." }
|
376
396
|
return
|
377
397
|
end
|
378
398
|
end
|
@@ -380,9 +400,7 @@ module HTTPX
|
|
380
400
|
|
381
401
|
def send_pending
|
382
402
|
while !@write_buffer.full? && (request = @pending.shift)
|
383
|
-
|
384
|
-
@keep_alive_timer.pause if @keep_alive_timer
|
385
|
-
parser.send(request)
|
403
|
+
send_request_to_parser(request)
|
386
404
|
end
|
387
405
|
end
|
388
406
|
|
@@ -390,6 +408,15 @@ module HTTPX
|
|
390
408
|
@parser ||= build_parser
|
391
409
|
end
|
392
410
|
|
411
|
+
def send_request_to_parser(request)
|
412
|
+
@inflight += 1
|
413
|
+
parser.send(request)
|
414
|
+
|
415
|
+
return unless @state == :inactive
|
416
|
+
|
417
|
+
transition(:active)
|
418
|
+
end
|
419
|
+
|
393
420
|
def build_parser(protocol = @io.protocol)
|
394
421
|
parser = registry(protocol).new(@write_buffer, @options)
|
395
422
|
set_parser_callbacks(parser)
|
@@ -401,7 +428,8 @@ module HTTPX
|
|
401
428
|
AltSvc.emit(request, response) do |alt_origin, origin, alt_params|
|
402
429
|
emit(:altsvc, alt_origin, origin, alt_params)
|
403
430
|
end
|
404
|
-
|
431
|
+
@response_received_at = Utils.now
|
432
|
+
@inflight -= 1
|
405
433
|
request.emit(:response, response)
|
406
434
|
end
|
407
435
|
parser.on(:altsvc) do |alt_origin, origin, alt_params|
|
@@ -421,7 +449,7 @@ module HTTPX
|
|
421
449
|
end
|
422
450
|
parser.on(:close) do |force|
|
423
451
|
transition(:closing)
|
424
|
-
if force
|
452
|
+
if force || @state == :idle
|
425
453
|
transition(:closed)
|
426
454
|
emit(:close)
|
427
455
|
end
|
@@ -436,6 +464,7 @@ module HTTPX
|
|
436
464
|
transition(:closing)
|
437
465
|
transition(:closed)
|
438
466
|
emit(:reset)
|
467
|
+
|
439
468
|
@parser.reset if @parser
|
440
469
|
transition(:idle)
|
441
470
|
transition(:open)
|
@@ -467,15 +496,17 @@ module HTTPX
|
|
467
496
|
when :open
|
468
497
|
return if @state == :closed
|
469
498
|
|
470
|
-
total_timeout
|
471
|
-
|
472
499
|
@io.connect
|
473
500
|
return unless @io.connected?
|
474
501
|
|
502
|
+
@connected_at = Utils.now
|
503
|
+
|
475
504
|
send_pending
|
476
505
|
|
477
506
|
@timeout = @current_timeout = parser.timeout
|
478
507
|
emit(:open)
|
508
|
+
when :inactive
|
509
|
+
return unless @state == :open
|
479
510
|
when :closing
|
480
511
|
return unless @state == :open
|
481
512
|
|
@@ -483,15 +514,15 @@ module HTTPX
|
|
483
514
|
return unless @state == :closing
|
484
515
|
return unless @write_buffer.empty?
|
485
516
|
|
486
|
-
if @total_timeout
|
487
|
-
@total_timeout.cancel
|
488
|
-
remove_instance_variable(:@total_timeout)
|
489
|
-
end
|
490
|
-
|
491
517
|
purge_after_closed
|
492
518
|
when :already_open
|
493
519
|
nextstate = :open
|
494
520
|
send_pending
|
521
|
+
when :active
|
522
|
+
return unless @state == :inactive
|
523
|
+
|
524
|
+
nextstate = :open
|
525
|
+
emit(:activate)
|
495
526
|
end
|
496
527
|
@state = nextstate
|
497
528
|
rescue Errno::ECONNREFUSED,
|
@@ -507,44 +538,24 @@ module HTTPX
|
|
507
538
|
def purge_after_closed
|
508
539
|
@io.close if @io
|
509
540
|
@read_buffer.clear
|
510
|
-
if @keep_alive_timer
|
511
|
-
@keep_alive_timer.cancel
|
512
|
-
remove_instance_variable(:@keep_alive_timer)
|
513
|
-
end
|
514
|
-
|
515
541
|
remove_instance_variable(:@timeout) if defined?(@timeout)
|
516
542
|
end
|
517
543
|
|
518
|
-
def handle_response
|
519
|
-
@inflight -= 1
|
520
|
-
return unless @inflight.zero?
|
521
|
-
|
522
|
-
if @keep_alive_timer
|
523
|
-
@keep_alive_timer.resume
|
524
|
-
@keep_alive_timer.reset
|
525
|
-
else
|
526
|
-
@keep_alive_timer = @timers.after(@keep_alive_timeout) do
|
527
|
-
unless @inflight.zero?
|
528
|
-
log { "(#{@origin}): keep alive timeout expired" }
|
529
|
-
parser.ping
|
530
|
-
end
|
531
|
-
end
|
532
|
-
end
|
533
|
-
end
|
534
|
-
|
535
544
|
def on_error(error)
|
536
545
|
if error.instance_of?(TimeoutError)
|
537
|
-
if @timeout
|
538
|
-
@timeout -= error.timeout
|
539
|
-
return unless @timeout <= 0
|
540
|
-
end
|
541
546
|
|
542
|
-
if @total_timeout && @
|
543
|
-
|
547
|
+
if @total_timeout && @connected_at &&
|
548
|
+
Utils.elapsed_time(@connected_at) > @total_timeout
|
549
|
+
ex = TotalTimeoutError.new(@total_timeout, "Timed out after #{@total_timeout} seconds")
|
544
550
|
ex.set_backtrace(error.backtrace)
|
545
551
|
error = ex
|
546
|
-
|
547
|
-
|
552
|
+
else
|
553
|
+
if @timeout
|
554
|
+
@timeout -= error.timeout
|
555
|
+
return unless @timeout <= 0
|
556
|
+
end
|
557
|
+
|
558
|
+
error = error.to_connection_error if connecting?
|
548
559
|
end
|
549
560
|
end
|
550
561
|
handle_error(error)
|
@@ -559,18 +570,5 @@ module HTTPX
|
|
559
570
|
request.emit(:response, response)
|
560
571
|
end
|
561
572
|
end
|
562
|
-
|
563
|
-
def total_timeout
|
564
|
-
total = @options.timeout[:total_timeout]
|
565
|
-
|
566
|
-
return unless total
|
567
|
-
|
568
|
-
@total_timeout ||= @timers.after(total) do
|
569
|
-
ex = TotalTimeoutError.new(total, "Timed out after #{total} seconds")
|
570
|
-
ex.set_backtrace(caller)
|
571
|
-
on_error(ex)
|
572
|
-
@parser.close if @parser
|
573
|
-
end
|
574
|
-
end
|
575
573
|
end
|
576
574
|
end
|
data/lib/httpx/domain_name.rb
CHANGED
data/lib/httpx/extensions.rb
CHANGED
@@ -54,6 +54,51 @@ module HTTPX
|
|
54
54
|
Numeric.__send__(:include, NegMethods)
|
55
55
|
end
|
56
56
|
|
57
|
+
module HashExtensions
|
58
|
+
refine Hash do
|
59
|
+
def compact
|
60
|
+
h = {}
|
61
|
+
each do |key, value|
|
62
|
+
h[key] = value unless value == nil
|
63
|
+
end
|
64
|
+
h
|
65
|
+
end unless Hash.method_defined?(:compact)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
module ArrayExtensions
|
70
|
+
refine Array do
|
71
|
+
|
72
|
+
def filter_map
|
73
|
+
return to_enum(:filter_map) unless block_given?
|
74
|
+
|
75
|
+
each_with_object([]) do |item, res|
|
76
|
+
processed = yield(item)
|
77
|
+
res << processed if processed
|
78
|
+
end
|
79
|
+
end unless Array.method_defined?(:filter_map)
|
80
|
+
|
81
|
+
def sum(accumulator = 0, &block)
|
82
|
+
values = block_given? ? map(&block) : self
|
83
|
+
values.inject(accumulator, :+)
|
84
|
+
end unless Array.method_defined?(:sum)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
module IOExtensions
|
89
|
+
refine IO do
|
90
|
+
# provides a fallback for rubies where IO#wait isn't implemented,
|
91
|
+
# but IO#wait_readable and IO#wait_writable are.
|
92
|
+
def wait(timeout = nil, _mode = :read_write)
|
93
|
+
r, w = IO.select([self], [self], nil, timeout)
|
94
|
+
|
95
|
+
return unless r || w
|
96
|
+
|
97
|
+
self
|
98
|
+
end unless IO.method_defined?(:wait) && IO.instance_method(:wait).arity == 2
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
57
102
|
module RegexpExtensions
|
58
103
|
# If you wonder why this is there: the oauth feature uses a refinement to enhance the
|
59
104
|
# Regexp class locally with #match? , but this is never tested, because ActiveSupport
|
@@ -77,13 +122,14 @@ module HTTPX
|
|
77
122
|
end
|
78
123
|
|
79
124
|
def authority
|
80
|
-
|
81
|
-
|
82
|
-
|
125
|
+
return host if port == default_port
|
126
|
+
|
127
|
+
"#{host}:#{port}"
|
128
|
+
end unless URI::HTTP.method_defined?(:authority)
|
83
129
|
|
84
130
|
def origin
|
85
131
|
"#{scheme}://#{authority}"
|
86
|
-
end
|
132
|
+
end unless URI::HTTP.method_defined?(:origin)
|
87
133
|
|
88
134
|
def altsvc_match?(uri)
|
89
135
|
uri = URI.parse(uri)
|
data/lib/httpx/headers.rb
CHANGED
data/lib/httpx/io/ssl.rb
CHANGED
@@ -27,6 +27,10 @@ module HTTPX
|
|
27
27
|
super
|
28
28
|
end
|
29
29
|
|
30
|
+
def can_verify_peer?
|
31
|
+
@ctx.verify_mode == OpenSSL::SSL::VERIFY_PEER
|
32
|
+
end
|
33
|
+
|
30
34
|
def verify_hostname(host)
|
31
35
|
return false if @ctx.verify_mode == OpenSSL::SSL::VERIFY_NONE
|
32
36
|
return false if !@io.respond_to?(:peer_cert) || @io.peer_cert.nil?
|
@@ -134,7 +138,7 @@ module HTTPX
|
|
134
138
|
server_cert = @io.peer_cert
|
135
139
|
|
136
140
|
"#{super}\n\n" \
|
137
|
-
|
141
|
+
"SSL connection using #{@io.ssl_version} / #{Array(@io.cipher).first}\n" \
|
138
142
|
"ALPN, server accepted to use #{protocol}\n" \
|
139
143
|
"Server certificate:\n" \
|
140
144
|
" subject: #{server_cert.subject}\n" \
|
data/lib/httpx/io/tls.rb
CHANGED
@@ -194,15 +194,15 @@ module HTTPX
|
|
194
194
|
server_cert = @peer_cert
|
195
195
|
|
196
196
|
"#{super}\n\n" \
|
197
|
-
|
198
|
-
|
197
|
+
"SSL connection using #{@ctx.ssl_version} / #{Array(@ctx.cipher).first}\n" \
|
198
|
+
"ALPN, server accepted to use #{protocol}\n" +
|
199
199
|
(if server_cert
|
200
200
|
"Server certificate:\n" \
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
201
|
+
" subject: #{server_cert.subject}\n" \
|
202
|
+
" start date: #{server_cert.not_before}\n" \
|
203
|
+
" expire date: #{server_cert.not_after}\n" \
|
204
|
+
" issuer: #{server_cert.issuer}\n" \
|
205
|
+
" SSL certificate verify ok."
|
206
206
|
else
|
207
207
|
"SSL certificate verify failed."
|
208
208
|
end
|
data/lib/httpx/loggable.rb
CHANGED
@@ -24,15 +24,13 @@ module HTTPX
|
|
24
24
|
debug_stream << message
|
25
25
|
end
|
26
26
|
|
27
|
-
if
|
27
|
+
if Exception.instance_methods.include?(:full_message)
|
28
28
|
|
29
29
|
def log_exception(ex, level: @options.debug_level, color: nil)
|
30
30
|
return unless @options.debug
|
31
31
|
return unless @options.debug_level >= level
|
32
32
|
|
33
|
-
|
34
|
-
message << "\n" << ex.backtrace.join("\n") unless ex.backtrace.nil?
|
35
|
-
log(level: level, color: color) { message }
|
33
|
+
log(level: level, color: color) { ex.full_message }
|
36
34
|
end
|
37
35
|
|
38
36
|
else
|
@@ -41,7 +39,9 @@ module HTTPX
|
|
41
39
|
return unless @options.debug
|
42
40
|
return unless @options.debug_level >= level
|
43
41
|
|
44
|
-
|
42
|
+
message = +"#{ex.message} (#{ex.class})"
|
43
|
+
message << "\n" << ex.backtrace.join("\n") unless ex.backtrace.nil?
|
44
|
+
log(level: level, color: color) { message }
|
45
45
|
end
|
46
46
|
|
47
47
|
end
|
data/lib/httpx/options.rb
CHANGED
@@ -39,6 +39,28 @@ module HTTPX
|
|
39
39
|
:resolver_options => { cache: true },
|
40
40
|
}.freeze
|
41
41
|
|
42
|
+
begin
|
43
|
+
module HashExtensions
|
44
|
+
refine Hash do
|
45
|
+
def >=(other)
|
46
|
+
Hash[other] <= self
|
47
|
+
end
|
48
|
+
|
49
|
+
def <=(other)
|
50
|
+
other = Hash[other]
|
51
|
+
return false unless size <= other.size
|
52
|
+
|
53
|
+
each do |k, v|
|
54
|
+
v2 = other.fetch(k) { return false }
|
55
|
+
return false unless v2 == v
|
56
|
+
end
|
57
|
+
true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
using HashExtensions
|
62
|
+
end unless Hash.method_defined?(:>=)
|
63
|
+
|
42
64
|
class << self
|
43
65
|
def new(options = {})
|
44
66
|
# let enhanced options go through
|
@@ -59,9 +81,9 @@ module HTTPX
|
|
59
81
|
end
|
60
82
|
|
61
83
|
def def_option(optname, *args, &block)
|
62
|
-
if args.size.zero? && !
|
84
|
+
if args.size.zero? && !block
|
63
85
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
64
|
-
def option_#{optname}(v); v; end
|
86
|
+
def option_#{optname}(v); v; end # def option_smth(v); v; end
|
65
87
|
OUT
|
66
88
|
return
|
67
89
|
end
|
@@ -71,15 +93,15 @@ module HTTPX
|
|
71
93
|
|
72
94
|
def deprecated_def_option(optname, layout = nil, &interpreter)
|
73
95
|
warn "DEPRECATION WARNING: using `def_option(#{optname})` for setting options is deprecated. " \
|
74
|
-
|
96
|
+
"Define module OptionsMethods and `def option_#{optname}(val)` instead."
|
75
97
|
|
76
98
|
if layout
|
77
99
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
78
|
-
def option_#{optname}(value)
|
79
|
-
#{layout}
|
80
|
-
end
|
100
|
+
def option_#{optname}(value) # def option_origin(v)
|
101
|
+
#{layout} # URI(v)
|
102
|
+
end # end
|
81
103
|
OUT
|
82
|
-
elsif
|
104
|
+
elsif interpreter
|
83
105
|
define_method(:"option_#{optname}") do |value|
|
84
106
|
instance_exec(value, &interpreter)
|
85
107
|
end
|
@@ -89,7 +111,7 @@ module HTTPX
|
|
89
111
|
|
90
112
|
def initialize(options = {})
|
91
113
|
defaults = DEFAULT_OPTIONS.merge(options)
|
92
|
-
defaults.each do |
|
114
|
+
defaults.each do |k, v|
|
93
115
|
next if v.nil?
|
94
116
|
|
95
117
|
begin
|
@@ -163,6 +185,7 @@ module HTTPX
|
|
163
185
|
end
|
164
186
|
|
165
187
|
REQUEST_IVARS = %i[@params @form @json @body].freeze
|
188
|
+
private_constant :REQUEST_IVARS
|
166
189
|
|
167
190
|
def ==(other)
|
168
191
|
ivars = instance_variables | other.instance_variables
|
@@ -180,14 +203,14 @@ module HTTPX
|
|
180
203
|
end
|
181
204
|
|
182
205
|
def merge(other)
|
183
|
-
raise ArgumentError, "#{other
|
206
|
+
raise ArgumentError, "#{other} is not a valid set of options" unless other.respond_to?(:to_hash)
|
184
207
|
|
185
208
|
h2 = other.to_hash
|
186
209
|
return self if h2.empty?
|
187
210
|
|
188
211
|
h1 = to_hash
|
189
212
|
|
190
|
-
return self if h1
|
213
|
+
return self if h1 >= h2
|
191
214
|
|
192
215
|
merged = h1.merge(h2) do |_k, v1, v2|
|
193
216
|
if v1.respond_to?(:merge) && v2.respond_to?(:merge)
|
@@ -201,10 +224,9 @@ module HTTPX
|
|
201
224
|
end
|
202
225
|
|
203
226
|
def to_hash
|
204
|
-
|
205
|
-
[ivar[1..-1].to_sym
|
227
|
+
instance_variables.each_with_object({}) do |ivar, hs|
|
228
|
+
hs[ivar[1..-1].to_sym] = instance_variable_get(ivar)
|
206
229
|
end
|
207
|
-
Hash[hash_pairs]
|
208
230
|
end
|
209
231
|
|
210
232
|
if RUBY_VERSION > "2.4.0"
|
data/lib/httpx/parser/http1.rb
CHANGED
@@ -60,7 +60,7 @@ module HTTPX
|
|
60
60
|
(m = %r{\AHTTP(?:/(\d+\.\d+))?\s+(\d\d\d)(?:\s+(.*))?}in.match(@buffer)) ||
|
61
61
|
raise(Error, "wrong head line format")
|
62
62
|
version, code, _ = m.captures
|
63
|
-
raise(Error, "unsupported HTTP version (HTTP/#{version})") unless VERSIONS.include?(version)
|
63
|
+
raise(Error, "unsupported HTTP version (HTTP/#{version})") unless version && VERSIONS.include?(version)
|
64
64
|
|
65
65
|
@http_version = version.split(".").map(&:to_i)
|
66
66
|
@status_code = code.to_i
|
@@ -72,9 +72,14 @@ module HTTPX
|
|
72
72
|
|
73
73
|
def parse_headers
|
74
74
|
headers = @headers
|
75
|
-
|
76
|
-
|
77
|
-
|
75
|
+
buffer = @buffer
|
76
|
+
|
77
|
+
while (idx = buffer.index("\n"))
|
78
|
+
line = buffer.byteslice(0..idx)
|
79
|
+
raise Error, "wrong header format" if line.start_with?("\s", "\t")
|
80
|
+
|
81
|
+
line.lstrip!
|
82
|
+
buffer = @buffer = buffer.byteslice((idx + 1)..-1)
|
78
83
|
if line.empty?
|
79
84
|
case @state
|
80
85
|
when :headers
|
@@ -97,9 +102,8 @@ module HTTPX
|
|
97
102
|
raise Error, "wrong header format" unless separator_index
|
98
103
|
|
99
104
|
key = line.byteslice(0..(separator_index - 1))
|
100
|
-
raise Error, "wrong header format" if key.start_with?("\s", "\t")
|
101
105
|
|
102
|
-
key.
|
106
|
+
key.rstrip! # was lstripped previously!
|
103
107
|
value = line.byteslice((separator_index + 1)..-1)
|
104
108
|
value.strip!
|
105
109
|
raise Error, "wrong header format" if value.nil?
|
@@ -8,6 +8,23 @@ module HTTPX
|
|
8
8
|
# It requires the "aws-sdk-core" gem.
|
9
9
|
#
|
10
10
|
module AwsSdkAuthentication
|
11
|
+
# Mock configuration, to be used only when resolving credentials
|
12
|
+
class Configuration
|
13
|
+
attr_reader :profile
|
14
|
+
|
15
|
+
def initialize(profile)
|
16
|
+
@profile = profile
|
17
|
+
end
|
18
|
+
|
19
|
+
def respond_to_missing?(*)
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def method_missing(*)
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
11
28
|
#
|
12
29
|
# encapsulates access to an AWS SDK credentials store.
|
13
30
|
#
|
@@ -30,23 +47,8 @@ module HTTPX
|
|
30
47
|
end
|
31
48
|
|
32
49
|
class << self
|
33
|
-
attr_reader :credentials, :region
|
34
|
-
|
35
50
|
def load_dependencies(_klass)
|
36
51
|
require "aws-sdk-core"
|
37
|
-
|
38
|
-
client = Class.new(Seahorse::Client::Base) do
|
39
|
-
@identifier = :httpx
|
40
|
-
set_api(Aws::S3::ClientApi::API)
|
41
|
-
add_plugin(Aws::Plugins::CredentialsConfiguration)
|
42
|
-
add_plugin(Aws::Plugins::RegionalEndpoint)
|
43
|
-
class << self
|
44
|
-
attr_reader :identifier
|
45
|
-
end
|
46
|
-
end.new
|
47
|
-
|
48
|
-
@credentials = Credentials.new(client.config[:credentials])
|
49
|
-
@region = client.config[:region]
|
50
52
|
end
|
51
53
|
|
52
54
|
def configure(klass)
|
@@ -56,6 +58,26 @@ module HTTPX
|
|
56
58
|
def extra_options(options)
|
57
59
|
options.merge(max_concurrent_requests: 1)
|
58
60
|
end
|
61
|
+
|
62
|
+
def credentials(profile)
|
63
|
+
mock_configuration = Configuration.new(profile)
|
64
|
+
Credentials.new(Aws::CredentialProviderChain.new(mock_configuration).resolve)
|
65
|
+
end
|
66
|
+
|
67
|
+
def region(profile)
|
68
|
+
# https://github.com/aws/aws-sdk-ruby/blob/version-3/gems/aws-sdk-core/lib/aws-sdk-core/plugins/regional_endpoint.rb#L62
|
69
|
+
keys = %w[AWS_REGION AMAZON_REGION AWS_DEFAULT_REGION]
|
70
|
+
env_region = ENV.values_at(*keys).compact.first
|
71
|
+
env_region = nil if env_region == ""
|
72
|
+
cfg_region = Aws.shared_config.region(profile: profile)
|
73
|
+
env_region || cfg_region
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
module OptionsMethods
|
78
|
+
def option_aws_profile(value)
|
79
|
+
String(value)
|
80
|
+
end
|
59
81
|
end
|
60
82
|
|
61
83
|
module InstanceMethods
|
@@ -64,9 +86,11 @@ module HTTPX
|
|
64
86
|
# aws_authentication(credentials: Aws::Credentials.new('akid', 'secret'))
|
65
87
|
# aws_authentication()
|
66
88
|
#
|
67
|
-
def aws_sdk_authentication(
|
68
|
-
credentials
|
69
|
-
region
|
89
|
+
def aws_sdk_authentication(
|
90
|
+
credentials: AwsSdkAuthentication.credentials(@options.aws_profile),
|
91
|
+
region: AwsSdkAuthentication.region(@options.aws_profile),
|
92
|
+
**options
|
93
|
+
)
|
70
94
|
|
71
95
|
aws_sigv4_authentication(
|
72
96
|
credentials: credentials,
|