httpx-patched 1.6.2.1
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 +7 -0
- data/LICENSE.txt +191 -0
- data/README.md +162 -0
- data/doc/release_notes/0_0_1.md +7 -0
- data/doc/release_notes/0_0_2.md +9 -0
- data/doc/release_notes/0_0_3.md +9 -0
- data/doc/release_notes/0_0_4.md +7 -0
- data/doc/release_notes/0_0_5.md +5 -0
- data/doc/release_notes/0_10_0.md +66 -0
- data/doc/release_notes/0_10_1.md +37 -0
- data/doc/release_notes/0_10_2.md +5 -0
- data/doc/release_notes/0_11_0.md +74 -0
- data/doc/release_notes/0_11_1.md +5 -0
- data/doc/release_notes/0_11_2.md +5 -0
- data/doc/release_notes/0_11_3.md +5 -0
- 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/doc/release_notes/0_14_1.md +7 -0
- data/doc/release_notes/0_14_2.md +6 -0
- data/doc/release_notes/0_14_3.md +5 -0
- data/doc/release_notes/0_14_4.md +5 -0
- data/doc/release_notes/0_14_5.md +11 -0
- data/doc/release_notes/0_15_0.md +53 -0
- data/doc/release_notes/0_15_1.md +8 -0
- data/doc/release_notes/0_15_2.md +9 -0
- data/doc/release_notes/0_15_3.md +5 -0
- data/doc/release_notes/0_15_4.md +5 -0
- data/doc/release_notes/0_16_0.md +93 -0
- data/doc/release_notes/0_16_1.md +5 -0
- 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/doc/release_notes/0_18_3.md +7 -0
- data/doc/release_notes/0_18_4.md +14 -0
- data/doc/release_notes/0_18_5.md +10 -0
- data/doc/release_notes/0_18_6.md +5 -0
- data/doc/release_notes/0_18_7.md +5 -0
- data/doc/release_notes/0_19_0.md +39 -0
- data/doc/release_notes/0_19_1.md +5 -0
- data/doc/release_notes/0_19_2.md +7 -0
- data/doc/release_notes/0_19_3.md +6 -0
- data/doc/release_notes/0_19_4.md +14 -0
- data/doc/release_notes/0_19_5.md +13 -0
- data/doc/release_notes/0_19_6.md +5 -0
- data/doc/release_notes/0_19_7.md +5 -0
- data/doc/release_notes/0_19_8.md +5 -0
- data/doc/release_notes/0_1_0.md +9 -0
- data/doc/release_notes/0_20_0.md +36 -0
- data/doc/release_notes/0_20_1.md +5 -0
- data/doc/release_notes/0_20_2.md +7 -0
- data/doc/release_notes/0_20_3.md +6 -0
- data/doc/release_notes/0_20_4.md +17 -0
- data/doc/release_notes/0_20_5.md +3 -0
- data/doc/release_notes/0_21_0.md +96 -0
- data/doc/release_notes/0_21_1.md +12 -0
- data/doc/release_notes/0_22_0.md +13 -0
- data/doc/release_notes/0_22_1.md +11 -0
- data/doc/release_notes/0_22_2.md +5 -0
- data/doc/release_notes/0_22_3.md +55 -0
- data/doc/release_notes/0_22_4.md +6 -0
- data/doc/release_notes/0_22_5.md +6 -0
- data/doc/release_notes/0_23_0.md +42 -0
- data/doc/release_notes/0_23_1.md +5 -0
- data/doc/release_notes/0_23_2.md +5 -0
- data/doc/release_notes/0_23_3.md +6 -0
- data/doc/release_notes/0_23_4.md +5 -0
- data/doc/release_notes/0_24_0.md +48 -0
- data/doc/release_notes/0_24_1.md +12 -0
- data/doc/release_notes/0_24_2.md +12 -0
- data/doc/release_notes/0_24_3.md +12 -0
- data/doc/release_notes/0_24_4.md +18 -0
- data/doc/release_notes/0_24_5.md +6 -0
- data/doc/release_notes/0_24_6.md +5 -0
- data/doc/release_notes/0_24_7.md +10 -0
- data/doc/release_notes/0_2_0.md +5 -0
- data/doc/release_notes/0_2_1.md +16 -0
- data/doc/release_notes/0_3_0.md +12 -0
- data/doc/release_notes/0_3_1.md +6 -0
- data/doc/release_notes/0_4_0.md +51 -0
- data/doc/release_notes/0_4_1.md +3 -0
- data/doc/release_notes/0_5_0.md +15 -0
- data/doc/release_notes/0_5_1.md +14 -0
- data/doc/release_notes/0_6_0.md +5 -0
- data/doc/release_notes/0_6_1.md +6 -0
- data/doc/release_notes/0_6_2.md +6 -0
- data/doc/release_notes/0_6_3.md +13 -0
- data/doc/release_notes/0_6_4.md +21 -0
- data/doc/release_notes/0_6_5.md +22 -0
- data/doc/release_notes/0_6_6.md +19 -0
- data/doc/release_notes/0_6_7.md +5 -0
- data/doc/release_notes/0_7_0.md +46 -0
- data/doc/release_notes/0_8_0.md +27 -0
- data/doc/release_notes/0_8_1.md +8 -0
- data/doc/release_notes/0_8_2.md +7 -0
- data/doc/release_notes/0_9_0.md +38 -0
- data/doc/release_notes/1_0_0.md +60 -0
- data/doc/release_notes/1_0_1.md +5 -0
- data/doc/release_notes/1_0_2.md +7 -0
- data/doc/release_notes/1_1_0.md +32 -0
- data/doc/release_notes/1_1_1.md +17 -0
- data/doc/release_notes/1_1_2.md +12 -0
- data/doc/release_notes/1_1_3.md +18 -0
- data/doc/release_notes/1_1_4.md +6 -0
- data/doc/release_notes/1_1_5.md +12 -0
- data/doc/release_notes/1_2_0.md +49 -0
- data/doc/release_notes/1_2_1.md +6 -0
- data/doc/release_notes/1_2_2.md +10 -0
- data/doc/release_notes/1_2_3.md +16 -0
- data/doc/release_notes/1_2_4.md +8 -0
- data/doc/release_notes/1_2_5.md +7 -0
- data/doc/release_notes/1_2_6.md +13 -0
- data/doc/release_notes/1_3_0.md +18 -0
- data/doc/release_notes/1_3_1.md +17 -0
- data/doc/release_notes/1_3_2.md +6 -0
- data/doc/release_notes/1_3_3.md +5 -0
- data/doc/release_notes/1_3_4.md +6 -0
- data/doc/release_notes/1_4_0.md +43 -0
- data/doc/release_notes/1_4_1.md +19 -0
- data/doc/release_notes/1_4_2.md +20 -0
- data/doc/release_notes/1_4_3.md +11 -0
- data/doc/release_notes/1_4_4.md +14 -0
- data/doc/release_notes/1_5_0.md +126 -0
- data/doc/release_notes/1_5_1.md +6 -0
- data/doc/release_notes/1_6_0.md +50 -0
- data/doc/release_notes/1_6_1.md +17 -0
- data/doc/release_notes/1_6_2.md +11 -0
- data/lib/httpx/adapters/datadog.rb +359 -0
- data/lib/httpx/adapters/faraday.rb +303 -0
- data/lib/httpx/adapters/sentry.rb +121 -0
- data/lib/httpx/adapters/webmock.rb +175 -0
- data/lib/httpx/altsvc.rb +163 -0
- data/lib/httpx/base64.rb +27 -0
- data/lib/httpx/buffer.rb +61 -0
- data/lib/httpx/callbacks.rb +35 -0
- data/lib/httpx/chainable.rb +106 -0
- data/lib/httpx/connection/http1.rb +399 -0
- data/lib/httpx/connection/http2.rb +468 -0
- data/lib/httpx/connection.rb +954 -0
- data/lib/httpx/domain_name.rb +145 -0
- data/lib/httpx/errors.rb +111 -0
- data/lib/httpx/extensions.rb +59 -0
- data/lib/httpx/headers.rb +176 -0
- data/lib/httpx/io/ssl.rb +163 -0
- data/lib/httpx/io/tcp.rb +239 -0
- data/lib/httpx/io/udp.rb +62 -0
- data/lib/httpx/io/unix.rb +71 -0
- data/lib/httpx/io.rb +11 -0
- data/lib/httpx/loggable.rb +56 -0
- data/lib/httpx/options.rb +463 -0
- data/lib/httpx/parser/http1.rb +186 -0
- data/lib/httpx/plugins/auth/basic.rb +20 -0
- data/lib/httpx/plugins/auth/digest.rb +102 -0
- data/lib/httpx/plugins/auth/ntlm.rb +35 -0
- data/lib/httpx/plugins/auth/socks5.rb +22 -0
- data/lib/httpx/plugins/auth.rb +25 -0
- data/lib/httpx/plugins/aws_sdk_authentication.rb +111 -0
- data/lib/httpx/plugins/aws_sigv4.rb +239 -0
- data/lib/httpx/plugins/basic_auth.rb +29 -0
- data/lib/httpx/plugins/brotli.rb +50 -0
- data/lib/httpx/plugins/callbacks.rb +127 -0
- data/lib/httpx/plugins/circuit_breaker/circuit.rb +100 -0
- data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +53 -0
- data/lib/httpx/plugins/circuit_breaker.rb +147 -0
- data/lib/httpx/plugins/content_digest.rb +204 -0
- data/lib/httpx/plugins/cookies/cookie.rb +174 -0
- data/lib/httpx/plugins/cookies/jar.rb +95 -0
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +143 -0
- data/lib/httpx/plugins/cookies.rb +107 -0
- data/lib/httpx/plugins/digest_auth.rb +67 -0
- data/lib/httpx/plugins/expect.rb +120 -0
- data/lib/httpx/plugins/fiber_concurrency.rb +195 -0
- data/lib/httpx/plugins/follow_redirects.rb +233 -0
- data/lib/httpx/plugins/grpc/call.rb +63 -0
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +90 -0
- data/lib/httpx/plugins/grpc/message.rb +55 -0
- data/lib/httpx/plugins/grpc.rb +282 -0
- data/lib/httpx/plugins/h2c.rb +127 -0
- data/lib/httpx/plugins/internal_telemetry.rb +107 -0
- data/lib/httpx/plugins/ntlm_auth.rb +62 -0
- data/lib/httpx/plugins/oauth.rb +183 -0
- data/lib/httpx/plugins/persistent.rb +82 -0
- data/lib/httpx/plugins/proxy/http.rb +184 -0
- data/lib/httpx/plugins/proxy/socks4.rb +135 -0
- data/lib/httpx/plugins/proxy/socks5.rb +194 -0
- data/lib/httpx/plugins/proxy/ssh.rb +94 -0
- data/lib/httpx/plugins/proxy.rb +349 -0
- data/lib/httpx/plugins/push_promise.rb +81 -0
- data/lib/httpx/plugins/query.rb +35 -0
- data/lib/httpx/plugins/rate_limiter.rb +55 -0
- data/lib/httpx/plugins/response_cache/file_store.rb +140 -0
- data/lib/httpx/plugins/response_cache/store.rb +33 -0
- data/lib/httpx/plugins/response_cache.rb +333 -0
- data/lib/httpx/plugins/retries.rb +230 -0
- data/lib/httpx/plugins/ssrf_filter.rb +145 -0
- data/lib/httpx/plugins/stream.rb +183 -0
- data/lib/httpx/plugins/stream_bidi.rb +315 -0
- data/lib/httpx/plugins/upgrade/h2.rb +64 -0
- data/lib/httpx/plugins/upgrade.rb +86 -0
- data/lib/httpx/plugins/webdav.rb +86 -0
- data/lib/httpx/plugins/xml.rb +76 -0
- data/lib/httpx/pmatch_extensions.rb +33 -0
- data/lib/httpx/pool.rb +190 -0
- data/lib/httpx/punycode.rb +22 -0
- data/lib/httpx/request/body.rb +158 -0
- data/lib/httpx/request.rb +328 -0
- data/lib/httpx/resolver/entry.rb +30 -0
- data/lib/httpx/resolver/https.rb +256 -0
- data/lib/httpx/resolver/multi.rb +102 -0
- data/lib/httpx/resolver/native.rb +547 -0
- data/lib/httpx/resolver/resolver.rb +173 -0
- data/lib/httpx/resolver/system.rb +255 -0
- data/lib/httpx/resolver.rb +189 -0
- data/lib/httpx/response/body.rb +242 -0
- data/lib/httpx/response/buffer.rb +115 -0
- data/lib/httpx/response.rb +304 -0
- data/lib/httpx/selector.rb +282 -0
- data/lib/httpx/session.rb +612 -0
- data/lib/httpx/session_extensions.rb +30 -0
- data/lib/httpx/timers.rb +133 -0
- data/lib/httpx/transcoder/body.rb +43 -0
- data/lib/httpx/transcoder/chunker.rb +115 -0
- data/lib/httpx/transcoder/deflate.rb +37 -0
- data/lib/httpx/transcoder/form.rb +68 -0
- data/lib/httpx/transcoder/gzip.rb +71 -0
- data/lib/httpx/transcoder/json.rb +71 -0
- data/lib/httpx/transcoder/multipart/decoder.rb +141 -0
- data/lib/httpx/transcoder/multipart/encoder.rb +120 -0
- data/lib/httpx/transcoder/multipart/mime_type_detector.rb +78 -0
- data/lib/httpx/transcoder/multipart/part.rb +35 -0
- data/lib/httpx/transcoder/multipart.rb +31 -0
- data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
- data/lib/httpx/transcoder/utils/deflater.rb +75 -0
- data/lib/httpx/transcoder.rb +91 -0
- data/lib/httpx/utils.rb +75 -0
- data/lib/httpx/version.rb +5 -0
- data/lib/httpx.rb +66 -0
- data/sig/altsvc.rbs +33 -0
- data/sig/buffer.rbs +27 -0
- data/sig/callbacks.rbs +15 -0
- data/sig/chainable.rbs +55 -0
- data/sig/connection/http1.rbs +85 -0
- data/sig/connection/http2.rbs +116 -0
- data/sig/connection.rbs +169 -0
- data/sig/domain_name.rbs +17 -0
- data/sig/errors.rbs +69 -0
- data/sig/headers.rbs +49 -0
- data/sig/httpx.rbs +27 -0
- data/sig/io/ssl.rbs +27 -0
- data/sig/io/tcp.rbs +72 -0
- data/sig/io/udp.rbs +25 -0
- data/sig/io/unix.rbs +26 -0
- data/sig/io.rbs +3 -0
- data/sig/loggable.rbs +17 -0
- data/sig/options.rbs +202 -0
- data/sig/parser/http1.rbs +59 -0
- data/sig/plugins/auth/basic.rbs +17 -0
- data/sig/plugins/auth/digest.rbs +25 -0
- data/sig/plugins/auth/ntlm.rbs +20 -0
- data/sig/plugins/auth/socks5.rbs +18 -0
- data/sig/plugins/auth.rbs +13 -0
- data/sig/plugins/aws_sdk_authentication.rbs +43 -0
- data/sig/plugins/aws_sigv4.rbs +78 -0
- data/sig/plugins/basic_auth.rbs +15 -0
- data/sig/plugins/brotli.rbs +22 -0
- data/sig/plugins/callbacks.rbs +38 -0
- data/sig/plugins/circuit_breaker.rbs +71 -0
- data/sig/plugins/compression.rbs +57 -0
- data/sig/plugins/content_digest.rbs +51 -0
- data/sig/plugins/cookies/cookie.rbs +55 -0
- data/sig/plugins/cookies/jar.rbs +26 -0
- data/sig/plugins/cookies/set_cookie_parser.rbs +22 -0
- data/sig/plugins/cookies.rbs +28 -0
- data/sig/plugins/digest_auth.rbs +21 -0
- data/sig/plugins/expect.rbs +15 -0
- data/sig/plugins/fiber_concurrency.rbs +51 -0
- data/sig/plugins/follow_redirects.rbs +47 -0
- data/sig/plugins/grpc/call.rbs +23 -0
- data/sig/plugins/grpc/grpc_encoding.rbs +37 -0
- data/sig/plugins/grpc/message.rbs +17 -0
- data/sig/plugins/grpc.rbs +65 -0
- data/sig/plugins/h2c.rbs +27 -0
- data/sig/plugins/ntlm_auth.rbs +21 -0
- data/sig/plugins/oauth.rbs +68 -0
- data/sig/plugins/persistent.rbs +14 -0
- data/sig/plugins/proxy/http.rbs +30 -0
- data/sig/plugins/proxy/socks4.rbs +37 -0
- data/sig/plugins/proxy/socks5.rbs +49 -0
- data/sig/plugins/proxy/ssh.rbs +18 -0
- data/sig/plugins/proxy.rbs +70 -0
- data/sig/plugins/push_promise.rbs +23 -0
- data/sig/plugins/query.rbs +18 -0
- data/sig/plugins/rate_limiter.rbs +13 -0
- data/sig/plugins/response_cache/file_store.rbs +19 -0
- data/sig/plugins/response_cache/store.rbs +13 -0
- data/sig/plugins/response_cache.rbs +86 -0
- data/sig/plugins/retries.rbs +66 -0
- data/sig/plugins/ssrf_filter.rbs +26 -0
- data/sig/plugins/stream.rbs +54 -0
- data/sig/plugins/stream_bidi.rbs +68 -0
- data/sig/plugins/upgrade/h2.rbs +9 -0
- data/sig/plugins/upgrade.rbs +29 -0
- data/sig/plugins/webdav.rbs +23 -0
- data/sig/plugins/xml.rbs +37 -0
- data/sig/pool.rbs +51 -0
- data/sig/punycode.rbs +5 -0
- data/sig/request/body.rbs +34 -0
- data/sig/request.rbs +88 -0
- data/sig/resolver/entry.rbs +13 -0
- data/sig/resolver/https.rbs +45 -0
- data/sig/resolver/multi.rbs +32 -0
- data/sig/resolver/native.rbs +74 -0
- data/sig/resolver/resolver.rbs +64 -0
- data/sig/resolver/system.rbs +34 -0
- data/sig/resolver.rbs +48 -0
- data/sig/response/body.rbs +52 -0
- data/sig/response/buffer.rbs +23 -0
- data/sig/response.rbs +103 -0
- data/sig/selector.rbs +68 -0
- data/sig/session.rbs +104 -0
- data/sig/timers.rbs +54 -0
- data/sig/transcoder/body.rbs +24 -0
- data/sig/transcoder/chunker.rbs +49 -0
- data/sig/transcoder/deflate.rbs +12 -0
- data/sig/transcoder/form.rbs +34 -0
- data/sig/transcoder/gzip.rbs +27 -0
- data/sig/transcoder/json.rbs +28 -0
- data/sig/transcoder/multipart.rbs +103 -0
- data/sig/transcoder/utils/body_reader.rbs +15 -0
- data/sig/transcoder/utils/deflater.rbs +28 -0
- data/sig/transcoder.rbs +43 -0
- data/sig/utils.rbs +19 -0
- metadata +518 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "resolv"
|
|
4
|
+
|
|
5
|
+
module HTTPX
|
|
6
|
+
# Implementation of a synchronous name resolver which relies on the system resolver,
|
|
7
|
+
# which is lib'c getaddrinfo function (abstracted in ruby via Addrinfo.getaddrinfo).
|
|
8
|
+
#
|
|
9
|
+
# Its main advantage is relying on the reference implementation for name resolution
|
|
10
|
+
# across most/all OSs which deploy ruby (it's what TCPSocket also uses), its main
|
|
11
|
+
# disadvantage is the inability to set timeouts / check socket for readiness events,
|
|
12
|
+
# hence why it relies on using the Timeout module, which poses a lot of problems for
|
|
13
|
+
# the selector loop, specially when network is unstable.
|
|
14
|
+
#
|
|
15
|
+
class Resolver::System < Resolver::Resolver
|
|
16
|
+
using URIExtensions
|
|
17
|
+
|
|
18
|
+
RESOLV_ERRORS = [Resolv::ResolvError,
|
|
19
|
+
Resolv::DNS::Requester::RequestError,
|
|
20
|
+
Resolv::DNS::EncodeError,
|
|
21
|
+
Resolv::DNS::DecodeError].freeze
|
|
22
|
+
|
|
23
|
+
DONE = 1
|
|
24
|
+
ERROR = 2
|
|
25
|
+
|
|
26
|
+
class << self
|
|
27
|
+
def multi?
|
|
28
|
+
false
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
attr_reader :state
|
|
33
|
+
|
|
34
|
+
def initialize(options)
|
|
35
|
+
super(0, options)
|
|
36
|
+
@resolver_options = @options.resolver_options
|
|
37
|
+
resolv_options = @resolver_options.dup
|
|
38
|
+
timeouts = resolv_options.delete(:timeouts) || Resolver::RESOLVE_TIMEOUT
|
|
39
|
+
@_timeouts = Array(timeouts)
|
|
40
|
+
@timeouts = Hash.new { |tims, host| tims[host] = @_timeouts.dup }
|
|
41
|
+
resolv_options.delete(:cache)
|
|
42
|
+
@queries = []
|
|
43
|
+
@ips = []
|
|
44
|
+
@pipe_mutex = Thread::Mutex.new
|
|
45
|
+
@state = :idle
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def resolvers
|
|
49
|
+
return enum_for(__method__) unless block_given?
|
|
50
|
+
|
|
51
|
+
yield self
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def multi
|
|
55
|
+
self
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def empty?
|
|
59
|
+
true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def close
|
|
63
|
+
transition(:closed)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def closed?
|
|
67
|
+
@state == :closed
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def to_io
|
|
71
|
+
@pipe_read.to_io
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def call
|
|
75
|
+
case @state
|
|
76
|
+
when :open
|
|
77
|
+
consume
|
|
78
|
+
end
|
|
79
|
+
nil
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def interests
|
|
83
|
+
return if @queries.empty?
|
|
84
|
+
|
|
85
|
+
:r
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def timeout
|
|
89
|
+
return unless @queries.empty?
|
|
90
|
+
|
|
91
|
+
_, connection = @queries.first
|
|
92
|
+
|
|
93
|
+
return unless connection
|
|
94
|
+
|
|
95
|
+
@timeouts[connection.peer.host].first
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def <<(connection)
|
|
99
|
+
@connections << connection
|
|
100
|
+
resolve
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def early_resolve(connection, **)
|
|
104
|
+
self << connection
|
|
105
|
+
true
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def handle_socket_timeout(interval)
|
|
109
|
+
error = HTTPX::ResolveTimeoutError.new(interval, "timed out while waiting on select")
|
|
110
|
+
error.set_backtrace(caller)
|
|
111
|
+
@queries.each do |host, connection|
|
|
112
|
+
@connections.delete(connection)
|
|
113
|
+
emit_resolve_error(connection, host, error)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
while (connection = @connections.shift)
|
|
117
|
+
emit_resolve_error(connection, connection.peer.host, error)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
|
|
123
|
+
def transition(nextstate)
|
|
124
|
+
case nextstate
|
|
125
|
+
when :idle
|
|
126
|
+
@timeouts.clear
|
|
127
|
+
when :open
|
|
128
|
+
return unless @state == :idle
|
|
129
|
+
|
|
130
|
+
@pipe_read, @pipe_write = IO.pipe
|
|
131
|
+
when :closed
|
|
132
|
+
return unless @state == :open
|
|
133
|
+
|
|
134
|
+
@pipe_write.close
|
|
135
|
+
@pipe_read.close
|
|
136
|
+
end
|
|
137
|
+
@state = nextstate
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def consume
|
|
141
|
+
return if @connections.empty?
|
|
142
|
+
|
|
143
|
+
if @pipe_read.wait_readable
|
|
144
|
+
event = @pipe_read.getbyte
|
|
145
|
+
|
|
146
|
+
case event
|
|
147
|
+
when DONE
|
|
148
|
+
*pair, addrs = @pipe_mutex.synchronize { @ips.pop }
|
|
149
|
+
if pair
|
|
150
|
+
@queries.delete(pair)
|
|
151
|
+
family, connection = pair
|
|
152
|
+
@connections.delete(connection)
|
|
153
|
+
|
|
154
|
+
catch(:coalesced) { emit_addresses(connection, family, addrs) }
|
|
155
|
+
end
|
|
156
|
+
when ERROR
|
|
157
|
+
*pair, error = @pipe_mutex.synchronize { @ips.pop }
|
|
158
|
+
if pair && error
|
|
159
|
+
@queries.delete(pair)
|
|
160
|
+
@connections.delete(connection)
|
|
161
|
+
|
|
162
|
+
_, connection = pair
|
|
163
|
+
emit_resolve_error(connection, connection.peer.host, error)
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
return emit(:close, self) if @connections.empty?
|
|
169
|
+
|
|
170
|
+
resolve
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def resolve(connection = nil, hostname = nil)
|
|
174
|
+
@connections.shift until @connections.empty? || @connections.first.state != :closed
|
|
175
|
+
|
|
176
|
+
connection ||= @connections.first
|
|
177
|
+
|
|
178
|
+
raise Error, "no URI to resolve" unless connection
|
|
179
|
+
|
|
180
|
+
return unless @queries.empty?
|
|
181
|
+
|
|
182
|
+
hostname ||= connection.peer.host
|
|
183
|
+
scheme = connection.origin.scheme
|
|
184
|
+
log do
|
|
185
|
+
"resolver: resolve IDN #{connection.peer.non_ascii_hostname} as #{hostname}"
|
|
186
|
+
end if connection.peer.non_ascii_hostname
|
|
187
|
+
|
|
188
|
+
transition(:open)
|
|
189
|
+
|
|
190
|
+
ip_families = connection.options.ip_families || Resolver.supported_ip_families
|
|
191
|
+
|
|
192
|
+
ip_families.each do |family|
|
|
193
|
+
@queries << [family, connection]
|
|
194
|
+
end
|
|
195
|
+
async_resolve(connection, hostname, scheme)
|
|
196
|
+
consume
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def async_resolve(connection, hostname, scheme)
|
|
200
|
+
families = connection.options.ip_families || Resolver.supported_ip_families
|
|
201
|
+
log { "resolver: query for #{hostname}" }
|
|
202
|
+
timeouts = @timeouts[connection.peer.host]
|
|
203
|
+
resolve_timeout = timeouts.first
|
|
204
|
+
|
|
205
|
+
Thread.start do
|
|
206
|
+
Thread.current.report_on_exception = false
|
|
207
|
+
begin
|
|
208
|
+
addrs = if resolve_timeout
|
|
209
|
+
|
|
210
|
+
Timeout.timeout(resolve_timeout) do
|
|
211
|
+
__addrinfo_resolve(hostname, scheme)
|
|
212
|
+
end
|
|
213
|
+
else
|
|
214
|
+
__addrinfo_resolve(hostname, scheme)
|
|
215
|
+
end
|
|
216
|
+
addrs = addrs.sort_by(&:afamily).group_by(&:afamily)
|
|
217
|
+
families.each do |family|
|
|
218
|
+
addresses = addrs[family]
|
|
219
|
+
next unless addresses
|
|
220
|
+
|
|
221
|
+
addresses.map!(&:ip_address)
|
|
222
|
+
addresses.uniq!
|
|
223
|
+
@pipe_mutex.synchronize do
|
|
224
|
+
@ips.unshift([family, connection, addresses])
|
|
225
|
+
@pipe_write.putc(DONE) unless @pipe_write.closed?
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
rescue StandardError => e
|
|
229
|
+
if e.is_a?(Timeout::Error)
|
|
230
|
+
timeouts.shift
|
|
231
|
+
retry unless timeouts.empty?
|
|
232
|
+
e = ResolveTimeoutError.new(resolve_timeout, e.message)
|
|
233
|
+
e.set_backtrace(e.backtrace)
|
|
234
|
+
end
|
|
235
|
+
@pipe_mutex.synchronize do
|
|
236
|
+
families.each do |family|
|
|
237
|
+
@ips.unshift([family, connection, e])
|
|
238
|
+
@pipe_write.putc(ERROR) unless @pipe_write.closed?
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def __addrinfo_resolve(host, scheme)
|
|
246
|
+
Addrinfo.getaddrinfo(host, scheme, Socket::AF_UNSPEC, Socket::SOCK_STREAM)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def emit_connection_error(_, error)
|
|
250
|
+
throw(:resolve_error, error)
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def close_resolver(resolver); end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "socket"
|
|
4
|
+
require "resolv"
|
|
5
|
+
|
|
6
|
+
module HTTPX
|
|
7
|
+
module Resolver
|
|
8
|
+
RESOLVE_TIMEOUT = [2, 3].freeze
|
|
9
|
+
|
|
10
|
+
require "httpx/resolver/entry"
|
|
11
|
+
require "httpx/resolver/resolver"
|
|
12
|
+
require "httpx/resolver/system"
|
|
13
|
+
require "httpx/resolver/native"
|
|
14
|
+
require "httpx/resolver/https"
|
|
15
|
+
require "httpx/resolver/multi"
|
|
16
|
+
|
|
17
|
+
@lookup_mutex = Thread::Mutex.new
|
|
18
|
+
@lookups = Hash.new { |h, k| h[k] = [] }
|
|
19
|
+
|
|
20
|
+
@identifier_mutex = Thread::Mutex.new
|
|
21
|
+
@identifier = 1
|
|
22
|
+
@hosts_resolver = Resolv::Hosts.new
|
|
23
|
+
|
|
24
|
+
module_function
|
|
25
|
+
|
|
26
|
+
def supported_ip_families
|
|
27
|
+
@supported_ip_families ||= begin
|
|
28
|
+
# https://github.com/ruby/resolv/blob/095f1c003f6073730500f02acbdbc55f83d70987/lib/resolv.rb#L408
|
|
29
|
+
list = Socket.ip_address_list
|
|
30
|
+
if list.any? { |a| a.ipv6? && !a.ipv6_loopback? && !a.ipv6_linklocal? }
|
|
31
|
+
[Socket::AF_INET6, Socket::AF_INET]
|
|
32
|
+
else
|
|
33
|
+
[Socket::AF_INET]
|
|
34
|
+
end
|
|
35
|
+
rescue NotImplementedError
|
|
36
|
+
[Socket::AF_INET]
|
|
37
|
+
end.freeze
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def resolver_for(resolver_type, options)
|
|
41
|
+
case resolver_type
|
|
42
|
+
when Symbol
|
|
43
|
+
meth = :"resolver_#{resolver_type}_class"
|
|
44
|
+
|
|
45
|
+
return options.__send__(meth) if options.respond_to?(meth)
|
|
46
|
+
when Class
|
|
47
|
+
return resolver_type if resolver_type < Resolver
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
raise Error, "unsupported resolver type (#{resolver_type})"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def nolookup_resolve(hostname)
|
|
54
|
+
ip_resolve(hostname) || cached_lookup(hostname) || hosts_resolve(hostname)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# tries to convert +hostname+ into an IPAddr, returns <tt>nil</tt> otherwise.
|
|
58
|
+
def ip_resolve(hostname)
|
|
59
|
+
[Entry.new(hostname)]
|
|
60
|
+
rescue ArgumentError
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# matches +hostname+ to entries in the hosts file, returns <tt>nil</nil> if none is
|
|
64
|
+
# found, or there is no hosts file.
|
|
65
|
+
def hosts_resolve(hostname)
|
|
66
|
+
ips = @hosts_resolver.getaddresses(hostname)
|
|
67
|
+
return if ips.empty?
|
|
68
|
+
|
|
69
|
+
ips.map { |ip| Entry.new(ip) }
|
|
70
|
+
rescue IOError
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def cached_lookup(hostname)
|
|
74
|
+
now = Utils.now
|
|
75
|
+
lookup_synchronize do |lookups|
|
|
76
|
+
lookup(hostname, lookups, now)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def cached_lookup_set(hostname, family, entries)
|
|
81
|
+
lookup_synchronize do |lookups|
|
|
82
|
+
case family
|
|
83
|
+
when Socket::AF_INET6
|
|
84
|
+
lookups[hostname].concat(entries)
|
|
85
|
+
when Socket::AF_INET
|
|
86
|
+
lookups[hostname].unshift(*entries)
|
|
87
|
+
end
|
|
88
|
+
entries.each do |entry|
|
|
89
|
+
next unless entry["name"] != hostname
|
|
90
|
+
|
|
91
|
+
case family
|
|
92
|
+
when Socket::AF_INET6
|
|
93
|
+
lookups[entry["name"]] << entry
|
|
94
|
+
when Socket::AF_INET
|
|
95
|
+
lookups[entry["name"]].unshift(entry)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def cached_lookup_evict(hostname, ip)
|
|
102
|
+
ip = ip.to_s
|
|
103
|
+
|
|
104
|
+
lookup_synchronize do |lookups|
|
|
105
|
+
entries = lookups[hostname]
|
|
106
|
+
|
|
107
|
+
return unless entries
|
|
108
|
+
|
|
109
|
+
lookups.delete_if { |entry| entry["data"] == ip }
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# do not use directly!
|
|
114
|
+
def lookup(hostname, lookups, ttl)
|
|
115
|
+
return unless lookups.key?(hostname)
|
|
116
|
+
|
|
117
|
+
entries = lookups[hostname] = lookups[hostname].select do |address|
|
|
118
|
+
address["TTL"] > ttl
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
ips = entries.flat_map do |address|
|
|
122
|
+
if (als = address["alias"])
|
|
123
|
+
lookup(als, lookups, ttl)
|
|
124
|
+
else
|
|
125
|
+
Entry.new(address["data"], address["TTL"])
|
|
126
|
+
end
|
|
127
|
+
end.compact
|
|
128
|
+
|
|
129
|
+
ips unless ips.empty?
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def generate_id
|
|
133
|
+
id_synchronize { @identifier = (@identifier + 1) & 0xFFFF }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def encode_dns_query(hostname, type: Resolv::DNS::Resource::IN::A, message_id: generate_id)
|
|
137
|
+
Resolv::DNS::Message.new(message_id).tap do |query|
|
|
138
|
+
query.rd = 1
|
|
139
|
+
query.add_question(hostname, type)
|
|
140
|
+
end.encode
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def decode_dns_answer(payload)
|
|
144
|
+
begin
|
|
145
|
+
message = Resolv::DNS::Message.decode(payload)
|
|
146
|
+
rescue Resolv::DNS::DecodeError => e
|
|
147
|
+
return :decode_error, e
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# no domain was found
|
|
151
|
+
return :no_domain_found if message.rcode == Resolv::DNS::RCode::NXDomain
|
|
152
|
+
|
|
153
|
+
return :message_truncated if message.tc == 1
|
|
154
|
+
|
|
155
|
+
return :dns_error, message.rcode if message.rcode != Resolv::DNS::RCode::NoError
|
|
156
|
+
|
|
157
|
+
addresses = []
|
|
158
|
+
|
|
159
|
+
now = Utils.now
|
|
160
|
+
message.each_answer do |question, _, value|
|
|
161
|
+
case value
|
|
162
|
+
when Resolv::DNS::Resource::IN::CNAME
|
|
163
|
+
addresses << {
|
|
164
|
+
"name" => question.to_s,
|
|
165
|
+
"TTL" => (now + value.ttl),
|
|
166
|
+
"alias" => value.name.to_s,
|
|
167
|
+
}
|
|
168
|
+
when Resolv::DNS::Resource::IN::A,
|
|
169
|
+
Resolv::DNS::Resource::IN::AAAA
|
|
170
|
+
addresses << {
|
|
171
|
+
"name" => question.to_s,
|
|
172
|
+
"TTL" => (now + value.ttl),
|
|
173
|
+
"data" => value.address.to_s,
|
|
174
|
+
}
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
[:ok, addresses]
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def lookup_synchronize
|
|
182
|
+
@lookup_mutex.synchronize { yield(@lookups) }
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def id_synchronize(&block)
|
|
186
|
+
@identifier_mutex.synchronize(&block)
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
# Implementation of the HTTP Response body as a buffer which implements the IO writer protocol
|
|
5
|
+
# (for buffering the response payload), the IO reader protocol (for consuming the response payload),
|
|
6
|
+
# and can be iterated over (via #each, which yields the payload in chunks).
|
|
7
|
+
class Response::Body
|
|
8
|
+
# the payload encoding (i.e. "utf-8", "ASCII-8BIT")
|
|
9
|
+
attr_reader :encoding
|
|
10
|
+
|
|
11
|
+
# Array of encodings contained in the response "content-encoding" header.
|
|
12
|
+
attr_reader :encodings
|
|
13
|
+
|
|
14
|
+
attr_reader :buffer
|
|
15
|
+
protected :buffer
|
|
16
|
+
|
|
17
|
+
# initialized with the corresponding HTTPX::Response +response+ and HTTPX::Options +options+.
|
|
18
|
+
def initialize(response, options)
|
|
19
|
+
@response = response
|
|
20
|
+
@headers = response.headers
|
|
21
|
+
@options = options
|
|
22
|
+
@window_size = options.window_size
|
|
23
|
+
@encodings = []
|
|
24
|
+
@length = 0
|
|
25
|
+
@buffer = nil
|
|
26
|
+
@reader = nil
|
|
27
|
+
@state = :idle
|
|
28
|
+
|
|
29
|
+
# initialize response encoding
|
|
30
|
+
@encoding = if (enc = response.content_type.charset)
|
|
31
|
+
begin
|
|
32
|
+
Encoding.find(enc)
|
|
33
|
+
rescue ArgumentError
|
|
34
|
+
Encoding::BINARY
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
Encoding::BINARY
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
initialize_inflaters
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def initialize_dup(other)
|
|
44
|
+
super
|
|
45
|
+
|
|
46
|
+
@buffer = other.instance_variable_get(:@buffer).dup
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def closed?
|
|
50
|
+
@state == :closed
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# write the response payload +chunk+ into the buffer. Inflates the chunk when required
|
|
54
|
+
# and supported.
|
|
55
|
+
def write(chunk)
|
|
56
|
+
return if @state == :closed
|
|
57
|
+
|
|
58
|
+
return 0 if chunk.empty?
|
|
59
|
+
|
|
60
|
+
chunk = decode_chunk(chunk)
|
|
61
|
+
|
|
62
|
+
size = chunk.bytesize
|
|
63
|
+
@length += size
|
|
64
|
+
transition(:open)
|
|
65
|
+
@buffer.write(chunk)
|
|
66
|
+
|
|
67
|
+
@response.emit(:chunk_received, chunk)
|
|
68
|
+
size
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# reads a chunk from the payload (implementation of the IO reader protocol).
|
|
72
|
+
def read(*args)
|
|
73
|
+
return unless @buffer
|
|
74
|
+
|
|
75
|
+
unless @reader
|
|
76
|
+
rewind
|
|
77
|
+
@reader = @buffer
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
@reader.read(*args)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# size of the decoded response payload. May differ from "content-length" header if
|
|
84
|
+
# response was encoded over-the-wire.
|
|
85
|
+
def bytesize
|
|
86
|
+
@length
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# yields the payload in chunks.
|
|
90
|
+
def each
|
|
91
|
+
return enum_for(__method__) unless block_given?
|
|
92
|
+
|
|
93
|
+
begin
|
|
94
|
+
if @buffer
|
|
95
|
+
rewind
|
|
96
|
+
while (chunk = @buffer.read(@window_size))
|
|
97
|
+
yield(chunk.force_encoding(@encoding))
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
ensure
|
|
101
|
+
close
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# returns the declared filename in the "contennt-disposition" header, when present.
|
|
106
|
+
def filename
|
|
107
|
+
return unless @headers.key?("content-disposition")
|
|
108
|
+
|
|
109
|
+
Utils.get_filename(@headers["content-disposition"])
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# returns the full response payload as a string.
|
|
113
|
+
def to_s
|
|
114
|
+
return "".b unless @buffer
|
|
115
|
+
|
|
116
|
+
@buffer.to_s
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
alias_method :to_str, :to_s
|
|
120
|
+
|
|
121
|
+
# whether the payload is empty.
|
|
122
|
+
def empty?
|
|
123
|
+
@length.zero?
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# copies the payload to +dest+.
|
|
127
|
+
#
|
|
128
|
+
# body.copy_to("path/to/file")
|
|
129
|
+
# body.copy_to(Pathname.new("path/to/file"))
|
|
130
|
+
# body.copy_to(File.new("path/to/file"))
|
|
131
|
+
def copy_to(dest)
|
|
132
|
+
return unless @buffer
|
|
133
|
+
|
|
134
|
+
rewind
|
|
135
|
+
|
|
136
|
+
if dest.respond_to?(:path) && @buffer.respond_to?(:path)
|
|
137
|
+
FileUtils.mv(@buffer.path, dest.path)
|
|
138
|
+
else
|
|
139
|
+
IO.copy_stream(@buffer, dest)
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# closes/cleans the buffer, resets everything
|
|
144
|
+
def close
|
|
145
|
+
if @buffer
|
|
146
|
+
@buffer.close
|
|
147
|
+
@buffer = nil
|
|
148
|
+
end
|
|
149
|
+
@length = 0
|
|
150
|
+
transition(:closed)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def ==(other)
|
|
154
|
+
super || case other
|
|
155
|
+
when Response::Body
|
|
156
|
+
@buffer == other.buffer
|
|
157
|
+
else
|
|
158
|
+
@buffer = other
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# :nocov:
|
|
163
|
+
def inspect
|
|
164
|
+
"#<#{self.class}:#{object_id} " \
|
|
165
|
+
"@state=#{@state} " \
|
|
166
|
+
"@length=#{@length}>"
|
|
167
|
+
end
|
|
168
|
+
# :nocov:
|
|
169
|
+
|
|
170
|
+
# rewinds the response payload buffer.
|
|
171
|
+
def rewind
|
|
172
|
+
return unless @buffer
|
|
173
|
+
|
|
174
|
+
# in case there's some reading going on
|
|
175
|
+
@reader = nil
|
|
176
|
+
|
|
177
|
+
@buffer.rewind
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
private
|
|
181
|
+
|
|
182
|
+
# prepares inflaters for the advertised encodings in "content-encoding" header.
|
|
183
|
+
def initialize_inflaters
|
|
184
|
+
@inflaters = nil
|
|
185
|
+
|
|
186
|
+
return unless @headers.key?("content-encoding")
|
|
187
|
+
|
|
188
|
+
return unless @options.decompress_response_body
|
|
189
|
+
|
|
190
|
+
@inflaters = @headers.get("content-encoding").filter_map do |encoding|
|
|
191
|
+
next if encoding == "identity"
|
|
192
|
+
|
|
193
|
+
inflater = self.class.initialize_inflater_by_encoding(encoding, @response)
|
|
194
|
+
|
|
195
|
+
# do not uncompress if there is no decoder available. In fact, we can't reliably
|
|
196
|
+
# continue decompressing beyond that, so ignore.
|
|
197
|
+
break unless inflater
|
|
198
|
+
|
|
199
|
+
@encodings << encoding
|
|
200
|
+
inflater
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# passes the +chunk+ through all inflaters to decode it.
|
|
205
|
+
def decode_chunk(chunk)
|
|
206
|
+
@inflaters.reverse_each do |inflater|
|
|
207
|
+
chunk = inflater.call(chunk)
|
|
208
|
+
end if @inflaters
|
|
209
|
+
|
|
210
|
+
chunk
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# tries transitioning the body STM to the +nextstate+.
|
|
214
|
+
def transition(nextstate)
|
|
215
|
+
case nextstate
|
|
216
|
+
when :open
|
|
217
|
+
return unless @state == :idle
|
|
218
|
+
|
|
219
|
+
@buffer = Response::Buffer.new(
|
|
220
|
+
threshold_size: @options.body_threshold_size,
|
|
221
|
+
bytesize: @length,
|
|
222
|
+
encoding: @encoding
|
|
223
|
+
)
|
|
224
|
+
when :closed
|
|
225
|
+
return if @state == :closed
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
@state = nextstate
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
class << self
|
|
232
|
+
def initialize_inflater_by_encoding(encoding, response, **kwargs) # :nodoc:
|
|
233
|
+
case encoding
|
|
234
|
+
when "gzip"
|
|
235
|
+
Transcoder::GZIP.decode(response, **kwargs)
|
|
236
|
+
when "deflate"
|
|
237
|
+
Transcoder::Deflate.decode(response, **kwargs)
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
end
|