httpx 0.16.1 → 0.18.2
Sign up to get free protection for your applications and to get access to all the features.
- 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,
|