httpx 1.5.1 → 1.6.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/doc/release_notes/1_6_0.md +50 -0
- data/lib/httpx/adapters/datadog.rb +23 -13
- data/lib/httpx/adapters/faraday.rb +14 -9
- data/lib/httpx/adapters/webmock.rb +1 -1
- data/lib/httpx/callbacks.rb +1 -1
- data/lib/httpx/connection/http1.rb +5 -6
- data/lib/httpx/connection/http2.rb +30 -12
- data/lib/httpx/connection.rb +17 -24
- data/lib/httpx/errors.rb +3 -1
- data/lib/httpx/io/ssl.rb +1 -4
- data/lib/httpx/io/tcp.rb +25 -16
- data/lib/httpx/io/unix.rb +4 -3
- data/lib/httpx/loggable.rb +4 -1
- data/lib/httpx/options.rb +252 -158
- data/lib/httpx/plugins/aws_sdk_authentication.rb +2 -0
- data/lib/httpx/plugins/aws_sigv4.rb +2 -0
- data/lib/httpx/plugins/callbacks.rb +13 -1
- data/lib/httpx/plugins/circuit_breaker.rb +2 -0
- data/lib/httpx/plugins/content_digest.rb +2 -0
- data/lib/httpx/plugins/cookies.rb +2 -2
- data/lib/httpx/plugins/digest_auth.rb +2 -0
- data/lib/httpx/plugins/expect.rb +2 -0
- data/lib/httpx/plugins/fiber_concurrency.rb +195 -0
- data/lib/httpx/plugins/follow_redirects.rb +2 -0
- data/lib/httpx/plugins/grpc.rb +2 -0
- data/lib/httpx/plugins/h2c.rb +26 -16
- data/lib/httpx/plugins/internal_telemetry.rb +0 -49
- data/lib/httpx/plugins/ntlm_auth.rb +2 -0
- data/lib/httpx/plugins/oauth.rb +2 -0
- data/lib/httpx/plugins/persistent.rb +27 -18
- data/lib/httpx/plugins/proxy/socks4.rb +1 -1
- data/lib/httpx/plugins/proxy/socks5.rb +1 -1
- data/lib/httpx/plugins/proxy/ssh.rb +2 -0
- data/lib/httpx/plugins/proxy.rb +61 -20
- data/lib/httpx/plugins/response_cache/file_store.rb +2 -2
- data/lib/httpx/plugins/response_cache.rb +2 -0
- data/lib/httpx/plugins/retries.rb +2 -0
- data/lib/httpx/plugins/ssrf_filter.rb +2 -2
- data/lib/httpx/plugins/stream_bidi.rb +3 -3
- data/lib/httpx/plugins/upgrade/h2.rb +11 -1
- data/lib/httpx/plugins/upgrade.rb +8 -0
- data/lib/httpx/pool.rb +15 -10
- data/lib/httpx/request/body.rb +8 -3
- data/lib/httpx/request.rb +22 -11
- data/lib/httpx/resolver/entry.rb +30 -0
- data/lib/httpx/resolver/https.rb +3 -1
- data/lib/httpx/resolver/multi.rb +5 -2
- data/lib/httpx/resolver/native.rb +15 -6
- data/lib/httpx/resolver/resolver.rb +3 -5
- data/lib/httpx/resolver/system.rb +1 -1
- data/lib/httpx/resolver.rb +34 -21
- data/lib/httpx/response/body.rb +1 -1
- data/lib/httpx/response/buffer.rb +13 -18
- data/lib/httpx/selector.rb +92 -34
- data/lib/httpx/session.rb +89 -30
- data/lib/httpx/session_extensions.rb +3 -2
- data/lib/httpx/transcoder/form.rb +1 -13
- data/lib/httpx/transcoder/multipart/mime_type_detector.rb +1 -1
- data/lib/httpx/transcoder/multipart.rb +14 -0
- data/lib/httpx/transcoder/utils/deflater.rb +1 -1
- data/lib/httpx/version.rb +1 -1
- data/sig/callbacks.rbs +1 -1
- data/sig/chainable.rbs +1 -0
- data/sig/connection/http1.rbs +2 -0
- data/sig/connection/http2.rbs +4 -0
- data/sig/connection.rbs +6 -6
- data/sig/errors.rbs +3 -1
- data/sig/io/ssl.rbs +1 -1
- data/sig/io/tcp.rbs +13 -7
- data/sig/io/udp.rbs +7 -2
- data/sig/io/unix.rbs +0 -1
- data/sig/io.rbs +0 -3
- data/sig/options.rbs +63 -10
- data/sig/plugins/fiber_concurrency.rbs +51 -0
- data/sig/plugins/h2c.rbs +5 -1
- data/sig/plugins/persistent.rbs +1 -1
- data/sig/plugins/proxy/socks4.rbs +1 -1
- data/sig/plugins/proxy/socks5.rbs +1 -1
- data/sig/plugins/proxy.rbs +5 -2
- data/sig/plugins/ssrf_filter.rbs +1 -1
- data/sig/plugins/stream_bidi.rbs +2 -2
- data/sig/request.rbs +4 -1
- data/sig/resolver/entry.rbs +13 -0
- data/sig/resolver/native.rbs +1 -0
- data/sig/resolver/resolver.rbs +2 -3
- data/sig/resolver/system.rbs +2 -2
- data/sig/resolver.rbs +10 -11
- data/sig/response.rbs +2 -2
- data/sig/selector.rbs +18 -10
- data/sig/session.rbs +4 -0
- data/sig/transcoder/form.rbs +3 -3
- data/sig/transcoder/multipart.rbs +9 -3
- metadata +9 -3
@@ -42,7 +42,7 @@ module HTTPX
|
|
42
42
|
%i[join_headers join_trailers join_body].each do |lock_meth|
|
43
43
|
class_eval(<<-METH, __FILE__, __LINE__ + 1)
|
44
44
|
# lock.aware version of +#{lock_meth}+
|
45
|
-
def #{lock_meth}(*) # def join_headers(*)
|
45
|
+
private def #{lock_meth}(*) # private def join_headers(*)
|
46
46
|
return super if @lock.owned?
|
47
47
|
|
48
48
|
# small race condition between
|
@@ -119,7 +119,7 @@ module HTTPX
|
|
119
119
|
class Signal
|
120
120
|
def initialize
|
121
121
|
@closed = false
|
122
|
-
@pipe_read, @pipe_write =
|
122
|
+
@pipe_read, @pipe_write = IO.pipe
|
123
123
|
end
|
124
124
|
|
125
125
|
def state
|
@@ -127,7 +127,7 @@ module HTTPX
|
|
127
127
|
end
|
128
128
|
|
129
129
|
# noop
|
130
|
-
def log(
|
130
|
+
def log(**, &_); end
|
131
131
|
|
132
132
|
def to_io
|
133
133
|
@pipe_read.to_io
|
@@ -22,6 +22,16 @@ module HTTPX
|
|
22
22
|
module ConnectionMethods
|
23
23
|
using URIExtensions
|
24
24
|
|
25
|
+
def interests
|
26
|
+
return super unless connecting? && @parser
|
27
|
+
|
28
|
+
connect
|
29
|
+
|
30
|
+
return @io.interests if connecting?
|
31
|
+
|
32
|
+
super
|
33
|
+
end
|
34
|
+
|
25
35
|
def upgrade_to_h2
|
26
36
|
prev_parser = @parser
|
27
37
|
|
@@ -30,7 +40,7 @@ module HTTPX
|
|
30
40
|
@inflight -= prev_parser.requests.size
|
31
41
|
end
|
32
42
|
|
33
|
-
@parser =
|
43
|
+
@parser = @options.http2_class.new(@write_buffer, @options)
|
34
44
|
set_parser_callbacks(@parser)
|
35
45
|
@upgrade_protocol = "h2"
|
36
46
|
|
@@ -20,6 +20,8 @@ module HTTPX
|
|
20
20
|
end
|
21
21
|
|
22
22
|
module OptionsMethods
|
23
|
+
private
|
24
|
+
|
23
25
|
def option_upgrade_handlers(value)
|
24
26
|
raise TypeError, ":upgrade_handlers must be a Hash" unless value.is_a?(Hash)
|
25
27
|
|
@@ -65,6 +67,12 @@ module HTTPX
|
|
65
67
|
module ConnectionMethods
|
66
68
|
attr_reader :upgrade_protocol, :hijacked
|
67
69
|
|
70
|
+
def initialize(*)
|
71
|
+
super
|
72
|
+
|
73
|
+
@upgrade_protocol = nil
|
74
|
+
end
|
75
|
+
|
68
76
|
def hijack_io
|
69
77
|
@hijacked = true
|
70
78
|
|
data/lib/httpx/pool.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require "httpx/selector"
|
4
4
|
require "httpx/connection"
|
5
|
+
require "httpx/connection/http2"
|
6
|
+
require "httpx/connection/http1"
|
5
7
|
require "httpx/resolver"
|
6
8
|
|
7
9
|
module HTTPX
|
@@ -51,18 +53,21 @@ module HTTPX
|
|
51
53
|
# this takes precedence over per-origin
|
52
54
|
@max_connections_cond.wait(@connection_mtx, @pool_timeout)
|
53
55
|
|
54
|
-
acquire_connection(uri, options)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
56
|
+
if (conn = acquire_connection(uri, options))
|
57
|
+
return conn
|
58
|
+
end
|
59
|
+
|
60
|
+
if @connections_counter == @max_connections
|
61
|
+
# if no matching usable connection was found, the pool will make room and drop a closed connection. if none is found,
|
62
|
+
# this means that all of them are persistent or being used, so raise a timeout error.
|
63
|
+
conn = @connections.find { |c| c.state == :closed }
|
59
64
|
|
60
|
-
|
61
|
-
|
65
|
+
raise PoolTimeoutError.new(@pool_timeout,
|
66
|
+
"Timed out after #{@pool_timeout} seconds while waiting for a connection") unless conn
|
62
67
|
|
63
|
-
|
64
|
-
end
|
68
|
+
drop_connection(conn)
|
65
69
|
end
|
70
|
+
|
66
71
|
end
|
67
72
|
|
68
73
|
if @origin_counters[uri.origin] == @max_connections_per_origin
|
@@ -110,7 +115,7 @@ module HTTPX
|
|
110
115
|
|
111
116
|
def checkout_resolver(options)
|
112
117
|
resolver_type = options.resolver_class
|
113
|
-
resolver_type = Resolver.resolver_for(resolver_type)
|
118
|
+
resolver_type = Resolver.resolver_for(resolver_type, options)
|
114
119
|
|
115
120
|
@resolver_mtx.synchronize do
|
116
121
|
resolvers = @resolvers[resolver_type]
|
data/lib/httpx/request/body.rb
CHANGED
@@ -56,7 +56,7 @@ module HTTPX
|
|
56
56
|
block.call(chunk)
|
57
57
|
end
|
58
58
|
# TODO: use copy_stream once bug is resolved: https://bugs.ruby-lang.org/issues/21131
|
59
|
-
#
|
59
|
+
# IO.copy_stream(body, ProcIO.new(block))
|
60
60
|
elsif body.respond_to?(:each)
|
61
61
|
body.each(&block)
|
62
62
|
else
|
@@ -127,8 +127,13 @@ module HTTPX
|
|
127
127
|
# @type var body: bodyIO
|
128
128
|
Transcoder::Body.encode(body)
|
129
129
|
elsif (form = params.delete(:form))
|
130
|
-
|
131
|
-
|
130
|
+
if Transcoder::Multipart.multipart?(form)
|
131
|
+
# @type var form: Transcoder::multipart_input
|
132
|
+
Transcoder::Multipart.encode(form)
|
133
|
+
else
|
134
|
+
# @type var form: Transcoder::urlencoded_input
|
135
|
+
Transcoder::Form.encode(form)
|
136
|
+
end
|
132
137
|
elsif (json = params.delete(:json))
|
133
138
|
# @type var body: _ToJson
|
134
139
|
Transcoder::JSON.encode(json)
|
data/lib/httpx/request.rb
CHANGED
@@ -8,6 +8,7 @@ module HTTPX
|
|
8
8
|
# as well as maintaining the state machine which manages streaming the request onto the wire.
|
9
9
|
class Request
|
10
10
|
extend Forwardable
|
11
|
+
include Loggable
|
11
12
|
include Callbacks
|
12
13
|
using URIExtensions
|
13
14
|
|
@@ -102,13 +103,16 @@ module HTTPX
|
|
102
103
|
raise UnsupportedSchemeError, "#{@uri}: #{@uri.scheme}: unsupported URI scheme" unless ALLOWED_URI_SCHEMES.include?(@uri.scheme)
|
103
104
|
|
104
105
|
@state = :idle
|
105
|
-
@response = nil
|
106
|
-
@peer_address = nil
|
106
|
+
@response = @peer_address = @context = @informational_status = nil
|
107
107
|
@ping = false
|
108
108
|
@persistent = @options.persistent
|
109
109
|
@active_timeouts = []
|
110
110
|
end
|
111
111
|
|
112
|
+
def complete!(response = @response)
|
113
|
+
emit(:complete, response)
|
114
|
+
end
|
115
|
+
|
112
116
|
# whether request has been buffered with a ping
|
113
117
|
def ping?
|
114
118
|
@ping
|
@@ -173,17 +177,23 @@ module HTTPX
|
|
173
177
|
def response=(response)
|
174
178
|
return unless response
|
175
179
|
|
176
|
-
|
177
|
-
|
180
|
+
case response
|
181
|
+
when Response
|
182
|
+
if response.status < 200
|
183
|
+
# deal with informational responses
|
178
184
|
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
185
|
+
if response.status == 100 && @headers.key?("expect")
|
186
|
+
@informational_status = response.status
|
187
|
+
return
|
188
|
+
end
|
189
|
+
|
190
|
+
# 103 Early Hints advertises resources in document to browsers.
|
191
|
+
# not very relevant for an HTTP client, discard.
|
192
|
+
return if response.status >= 103
|
183
193
|
|
184
|
-
|
185
|
-
|
186
|
-
|
194
|
+
end
|
195
|
+
when ErrorResponse
|
196
|
+
response.error.connection = nil if response.error.respond_to?(:connection=)
|
187
197
|
end
|
188
198
|
|
189
199
|
@response = response
|
@@ -293,6 +303,7 @@ module HTTPX
|
|
293
303
|
return if @state == :expect
|
294
304
|
|
295
305
|
end
|
306
|
+
log(level: 3) { "#{@state}] -> #{nextstate}" }
|
296
307
|
@state = nextstate
|
297
308
|
emit(@state, self)
|
298
309
|
nil
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "ipaddr"
|
4
|
+
|
5
|
+
module HTTPX
|
6
|
+
module Resolver
|
7
|
+
class Entry < SimpleDelegator
|
8
|
+
attr_reader :address
|
9
|
+
|
10
|
+
def self.convert(address)
|
11
|
+
new(address, rescue_on_convert: true)
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(address, expires_in = Float::INFINITY, rescue_on_convert: false)
|
15
|
+
@expires_in = expires_in
|
16
|
+
@address = address.is_a?(IPAddr) ? address : IPAddr.new(address.to_s)
|
17
|
+
super(@address)
|
18
|
+
rescue IPAddr::InvalidAddressError
|
19
|
+
raise unless rescue_on_convert
|
20
|
+
|
21
|
+
@address = address.to_s
|
22
|
+
super(@address)
|
23
|
+
end
|
24
|
+
|
25
|
+
def expired?
|
26
|
+
@expires_in < Utils.now
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/httpx/resolver/https.rb
CHANGED
@@ -59,6 +59,8 @@ module HTTPX
|
|
59
59
|
resolve(connection)
|
60
60
|
end
|
61
61
|
|
62
|
+
# This is already indirectly monitored bt the HTTP connection. In order to skip
|
63
|
+
# monitoring, this method returns <tt>true</tt>.
|
62
64
|
def closed?
|
63
65
|
true
|
64
66
|
end
|
@@ -202,7 +204,7 @@ module HTTPX
|
|
202
204
|
@queries.delete_if { |_, conn| connection == conn }
|
203
205
|
|
204
206
|
Resolver.cached_lookup_set(hostname, @family, addresses) if @resolver_options[:cache]
|
205
|
-
catch(:coalesced) { emit_addresses(connection, @family, addresses.map { |
|
207
|
+
catch(:coalesced) { emit_addresses(connection, @family, addresses.map { |a| Resolver::Entry.new(a["data"], a["TTL"]) }) }
|
206
208
|
end
|
207
209
|
end
|
208
210
|
return if @connections.empty?
|
data/lib/httpx/resolver/multi.rb
CHANGED
@@ -11,8 +11,7 @@ module HTTPX
|
|
11
11
|
attr_reader :resolvers, :options
|
12
12
|
|
13
13
|
def initialize(resolver_type, options)
|
14
|
-
@current_selector = nil
|
15
|
-
@current_session = nil
|
14
|
+
@current_selector = @current_session = nil
|
16
15
|
@options = options
|
17
16
|
@resolver_options = @options.resolver_options
|
18
17
|
|
@@ -35,6 +34,10 @@ module HTTPX
|
|
35
34
|
@resolvers.each { |r| r.__send__(__method__, s) }
|
36
35
|
end
|
37
36
|
|
37
|
+
def log(*args, **kwargs, &blk)
|
38
|
+
@resolvers.each { |r| r.__send__(__method__, *args, **kwargs, &blk) }
|
39
|
+
end
|
40
|
+
|
38
41
|
def closed?
|
39
42
|
@resolvers.all?(&:closed?)
|
40
43
|
end
|
@@ -42,6 +42,7 @@ module HTTPX
|
|
42
42
|
@read_buffer = "".b
|
43
43
|
@write_buffer = Buffer.new(@resolver_options[:packet_size])
|
44
44
|
@state = :idle
|
45
|
+
@timer = nil
|
45
46
|
end
|
46
47
|
|
47
48
|
def close
|
@@ -104,11 +105,11 @@ module HTTPX
|
|
104
105
|
private
|
105
106
|
|
106
107
|
def calculate_interests
|
107
|
-
return
|
108
|
+
return if @queries.empty?
|
108
109
|
|
109
|
-
return :r
|
110
|
+
return :r if @write_buffer.empty?
|
110
111
|
|
111
|
-
|
112
|
+
:w
|
112
113
|
end
|
113
114
|
|
114
115
|
def consume
|
@@ -153,6 +154,8 @@ module HTTPX
|
|
153
154
|
@timer = @current_selector.after(timeout) do
|
154
155
|
next unless @connections.include?(connection)
|
155
156
|
|
157
|
+
@timer = nil
|
158
|
+
|
156
159
|
do_retry(h, connection, timeout)
|
157
160
|
end
|
158
161
|
end
|
@@ -270,6 +273,8 @@ module HTTPX
|
|
270
273
|
def parse(buffer)
|
271
274
|
@timer.cancel
|
272
275
|
|
276
|
+
@timer = nil
|
277
|
+
|
273
278
|
code, result = Resolver.decode_dns_answer(buffer)
|
274
279
|
|
275
280
|
case code
|
@@ -370,7 +375,9 @@ module HTTPX
|
|
370
375
|
@timeouts.delete(connection.peer.host)
|
371
376
|
@connections.delete(connection)
|
372
377
|
Resolver.cached_lookup_set(connection.peer.host, @family, addresses) if @resolver_options[:cache]
|
373
|
-
catch(:coalesced)
|
378
|
+
catch(:coalesced) do
|
379
|
+
emit_addresses(connection, @family, addresses.map { |a| Resolver::Entry.new(a["data"], a["TTL"]) })
|
380
|
+
end
|
374
381
|
end
|
375
382
|
end
|
376
383
|
close_or_resolve
|
@@ -383,7 +390,8 @@ module HTTPX
|
|
383
390
|
|
384
391
|
raise Error, "no URI to resolve" unless connection
|
385
392
|
|
386
|
-
|
393
|
+
# do not buffer query if previous is still in the buffer or awaiting reply/retry
|
394
|
+
return unless @write_buffer.empty? && @timer.nil?
|
387
395
|
|
388
396
|
hostname ||= @queries.key(connection)
|
389
397
|
|
@@ -444,7 +452,7 @@ module HTTPX
|
|
444
452
|
when :tcp
|
445
453
|
log { "resolver #{FAMILY_TYPES[@record_type]}: server: tcp://#{ip}:#{port}..." }
|
446
454
|
origin = URI("tcp://#{ip}:#{port}")
|
447
|
-
TCP.new(origin, [ip], @options)
|
455
|
+
TCP.new(origin, [Resolver::Entry.new(ip)], @options)
|
448
456
|
end
|
449
457
|
end
|
450
458
|
|
@@ -480,6 +488,7 @@ module HTTPX
|
|
480
488
|
@write_buffer.clear
|
481
489
|
@read_buffer.clear
|
482
490
|
end
|
491
|
+
log(level: 3) { "#{@state} -> #{nextstate}" }
|
483
492
|
@state = nextstate
|
484
493
|
rescue Errno::ECONNREFUSED,
|
485
494
|
Errno::EADDRNOTAVAIL,
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "resolv"
|
4
|
-
require "ipaddr"
|
5
4
|
|
6
5
|
module HTTPX
|
7
6
|
# Base class for all internal internet name resolvers. It handles basic blocks
|
@@ -69,12 +68,11 @@ module HTTPX
|
|
69
68
|
end
|
70
69
|
|
71
70
|
def emit_addresses(connection, family, addresses, early_resolve = false)
|
72
|
-
addresses.map!
|
73
|
-
address.is_a?(IPAddr) ? address : IPAddr.new(address.to_s)
|
74
|
-
end
|
71
|
+
addresses.map! { |address| address.is_a?(Resolver::Entry) ? address : Resolver::Entry.new(address) }
|
75
72
|
|
76
73
|
# double emission check, but allow early resolution to work
|
77
|
-
|
74
|
+
conn_addrs = connection.addresses
|
75
|
+
return if !early_resolve && conn_addrs && (!conn_addrs.empty? && !addresses.intersect?(!conn_addrs))
|
78
76
|
|
79
77
|
log do
|
80
78
|
"resolver #{FAMILY_TYPES[RECORD_TYPES[family]]}: " \
|
data/lib/httpx/resolver.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "resolv"
|
4
|
-
require "ipaddr"
|
5
4
|
|
6
5
|
module HTTPX
|
7
6
|
module Resolver
|
8
7
|
RESOLVE_TIMEOUT = [2, 3].freeze
|
9
8
|
|
9
|
+
require "httpx/resolver/entry"
|
10
10
|
require "httpx/resolver/resolver"
|
11
11
|
require "httpx/resolver/system"
|
12
12
|
require "httpx/resolver/native"
|
@@ -18,36 +18,40 @@ module HTTPX
|
|
18
18
|
|
19
19
|
@identifier_mutex = Thread::Mutex.new
|
20
20
|
@identifier = 1
|
21
|
-
@
|
21
|
+
@hosts_resolver = Resolv::Hosts.new
|
22
22
|
|
23
23
|
module_function
|
24
24
|
|
25
|
-
def resolver_for(resolver_type)
|
25
|
+
def resolver_for(resolver_type, options)
|
26
26
|
case resolver_type
|
27
|
-
when
|
28
|
-
|
29
|
-
when :https then HTTPS
|
30
|
-
else
|
31
|
-
return resolver_type if resolver_type.is_a?(Class) && resolver_type < Resolver
|
27
|
+
when Symbol
|
28
|
+
meth = :"resolver_#{resolver_type}_class"
|
32
29
|
|
33
|
-
|
30
|
+
return options.__send__(meth) if options.respond_to?(meth)
|
31
|
+
when Class
|
32
|
+
return resolver_type if resolver_type < Resolver
|
34
33
|
end
|
34
|
+
|
35
|
+
raise Error, "unsupported resolver type (#{resolver_type})"
|
35
36
|
end
|
36
37
|
|
37
38
|
def nolookup_resolve(hostname)
|
38
|
-
ip_resolve(hostname) || cached_lookup(hostname) ||
|
39
|
+
ip_resolve(hostname) || cached_lookup(hostname) || hosts_resolve(hostname)
|
39
40
|
end
|
40
41
|
|
42
|
+
# tries to convert +hostname+ into an IPAddr, returns <tt>nil</tt> otherwise.
|
41
43
|
def ip_resolve(hostname)
|
42
|
-
[
|
44
|
+
[Entry.new(hostname)]
|
43
45
|
rescue ArgumentError
|
44
46
|
end
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
+
# matches +hostname+ to entries in the hosts file, returns <tt>nil</nil> if none is
|
49
|
+
# found, or there is no hosts file.
|
50
|
+
def hosts_resolve(hostname)
|
51
|
+
ips = @hosts_resolver.getaddresses(hostname)
|
48
52
|
return if ips.empty?
|
49
53
|
|
50
|
-
ips.map { |ip|
|
54
|
+
ips.map { |ip| Entry.new(ip) }
|
51
55
|
rescue IOError
|
52
56
|
end
|
53
57
|
|
@@ -59,10 +63,6 @@ module HTTPX
|
|
59
63
|
end
|
60
64
|
|
61
65
|
def cached_lookup_set(hostname, family, entries)
|
62
|
-
now = Utils.now
|
63
|
-
entries.each do |entry|
|
64
|
-
entry["TTL"] += now
|
65
|
-
end
|
66
66
|
lookup_synchronize do |lookups|
|
67
67
|
case family
|
68
68
|
when Socket::AF_INET6
|
@@ -83,6 +83,18 @@ module HTTPX
|
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
|
+
def cached_lookup_evict(hostname, ip)
|
87
|
+
ip = ip.to_s
|
88
|
+
|
89
|
+
lookup_synchronize do |lookups|
|
90
|
+
entries = lookups[hostname]
|
91
|
+
|
92
|
+
return unless entries
|
93
|
+
|
94
|
+
lookups.delete_if { |entry| entry["data"] == ip }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
86
98
|
# do not use directly!
|
87
99
|
def lookup(hostname, lookups, ttl)
|
88
100
|
return unless lookups.key?(hostname)
|
@@ -95,7 +107,7 @@ module HTTPX
|
|
95
107
|
if (als = address["alias"])
|
96
108
|
lookup(als, lookups, ttl)
|
97
109
|
else
|
98
|
-
|
110
|
+
Entry.new(address["data"], address["TTL"])
|
99
111
|
end
|
100
112
|
end.compact
|
101
113
|
|
@@ -129,19 +141,20 @@ module HTTPX
|
|
129
141
|
|
130
142
|
addresses = []
|
131
143
|
|
144
|
+
now = Utils.now
|
132
145
|
message.each_answer do |question, _, value|
|
133
146
|
case value
|
134
147
|
when Resolv::DNS::Resource::IN::CNAME
|
135
148
|
addresses << {
|
136
149
|
"name" => question.to_s,
|
137
|
-
"TTL" => value.ttl,
|
150
|
+
"TTL" => (now + value.ttl),
|
138
151
|
"alias" => value.name.to_s,
|
139
152
|
}
|
140
153
|
when Resolv::DNS::Resource::IN::A,
|
141
154
|
Resolv::DNS::Resource::IN::AAAA
|
142
155
|
addresses << {
|
143
156
|
"name" => question.to_s,
|
144
|
-
"TTL" => value.ttl,
|
157
|
+
"TTL" => (now + value.ttl),
|
145
158
|
"data" => value.address.to_s,
|
146
159
|
}
|
147
160
|
end
|
data/lib/httpx/response/body.rb
CHANGED
@@ -29,7 +29,9 @@ module HTTPX
|
|
29
29
|
when StringIO
|
30
30
|
StringIO.new(other.buffer.string, mode: File::RDONLY)
|
31
31
|
else
|
32
|
-
other.buffer.class.new(other.buffer.path, encoding: Encoding::BINARY, mode: File::RDONLY)
|
32
|
+
other.buffer.class.new(other.buffer.path, encoding: Encoding::BINARY, mode: File::RDONLY).tap do |temp|
|
33
|
+
FileUtils.copy_file(other.buffer.path, temp.path)
|
34
|
+
end
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
@@ -75,22 +77,15 @@ module HTTPX
|
|
75
77
|
super || begin
|
76
78
|
return false unless other.is_a?(Response::Buffer)
|
77
79
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
FileUtils.compare_stream(@buffer, other.buffer)
|
88
|
-
ensure
|
89
|
-
@buffer.pos = buffer_pos
|
90
|
-
other.buffer.pos = other_pos
|
91
|
-
end
|
92
|
-
else
|
93
|
-
to_s == other.to_s
|
80
|
+
buffer_pos = @buffer.pos
|
81
|
+
other_pos = other.buffer.pos
|
82
|
+
@buffer.rewind
|
83
|
+
other.buffer.rewind
|
84
|
+
begin
|
85
|
+
FileUtils.compare_stream(@buffer, other.buffer)
|
86
|
+
ensure
|
87
|
+
@buffer.pos = buffer_pos
|
88
|
+
other.buffer.pos = other_pos
|
94
89
|
end
|
95
90
|
end
|
96
91
|
end
|
@@ -110,7 +105,7 @@ module HTTPX
|
|
110
105
|
|
111
106
|
if aux
|
112
107
|
aux.rewind
|
113
|
-
|
108
|
+
IO.copy_stream(aux, @buffer)
|
114
109
|
aux.close
|
115
110
|
end
|
116
111
|
|