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,463 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
# Contains a set of options which are passed and shared across from session to its requests or
|
|
5
|
+
# responses.
|
|
6
|
+
class Options
|
|
7
|
+
BUFFER_SIZE = 1 << 14
|
|
8
|
+
WINDOW_SIZE = 1 << 14 # 16K
|
|
9
|
+
MAX_BODY_THRESHOLD_SIZE = (1 << 10) * 112 # 112K
|
|
10
|
+
KEEP_ALIVE_TIMEOUT = 20
|
|
11
|
+
SETTINGS_TIMEOUT = 10
|
|
12
|
+
CLOSE_HANDSHAKE_TIMEOUT = 10
|
|
13
|
+
CONNECT_TIMEOUT = READ_TIMEOUT = WRITE_TIMEOUT = 60
|
|
14
|
+
REQUEST_TIMEOUT = OPERATION_TIMEOUT = nil
|
|
15
|
+
|
|
16
|
+
@options_names = []
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
attr_reader :options_names
|
|
20
|
+
|
|
21
|
+
def inherited(klass)
|
|
22
|
+
super
|
|
23
|
+
klass.instance_variable_set(:@options_names, @options_names.dup)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def new(options = {})
|
|
27
|
+
# let enhanced options go through
|
|
28
|
+
return options if self == Options && options.class < self
|
|
29
|
+
return options if options.is_a?(self)
|
|
30
|
+
|
|
31
|
+
super
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def freeze
|
|
35
|
+
@options_names.freeze
|
|
36
|
+
super
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def method_added(meth)
|
|
40
|
+
super
|
|
41
|
+
|
|
42
|
+
return unless meth =~ /^option_(.+)$/
|
|
43
|
+
|
|
44
|
+
optname = Regexp.last_match(1)
|
|
45
|
+
|
|
46
|
+
if optname =~ /^(.+[^_])_+with/
|
|
47
|
+
# ignore alias method chain generated methods.
|
|
48
|
+
# this is the case with RBS runtime tests.
|
|
49
|
+
# it relies on the "_with/_without" separator, which is the most used convention,
|
|
50
|
+
# however it shouldn't be used in practice in httpx given the plugin architecture
|
|
51
|
+
# as the main extension API.
|
|
52
|
+
orig_name = Regexp.last_match(1)
|
|
53
|
+
|
|
54
|
+
return if @options_names.include?(orig_name.to_sym)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
optname = optname.to_sym
|
|
58
|
+
|
|
59
|
+
attr_reader(optname)
|
|
60
|
+
|
|
61
|
+
@options_names << optname unless @options_names.include?(optname)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# creates a new options instance from a given hash, which optionally define the following:
|
|
66
|
+
#
|
|
67
|
+
# :debug :: an object which log messages are written to (must respond to <tt><<</tt>)
|
|
68
|
+
# :debug_level :: the log level of messages (can be 1, 2, or 3).
|
|
69
|
+
# :debug_redact :: whether header/body payload should be redacted (defaults to <tt>false</tt>).
|
|
70
|
+
# :ssl :: a hash of options which can be set as params of OpenSSL::SSL::SSLContext (see HTTPX::SSL)
|
|
71
|
+
# :http2_settings :: a hash of options to be passed to a HTTP2::Connection (ex: <tt>{ max_concurrent_streams: 2 }</tt>)
|
|
72
|
+
# :fallback_protocol :: version of HTTP protocol to use by default in the absence of protocol negotiation
|
|
73
|
+
# like ALPN (defaults to <tt>"http/1.1"</tt>)
|
|
74
|
+
# :supported_compression_formats :: list of compressions supported by the transcoder layer (defaults to <tt>%w[gzip deflate]</tt>).
|
|
75
|
+
# :decompress_response_body :: whether to auto-decompress response body (defaults to <tt>true</tt>).
|
|
76
|
+
# :compress_request_body :: whether to auto-decompress response body (defaults to <tt>true</tt>)
|
|
77
|
+
# :timeout :: hash of timeout configurations (supports <tt>:connect_timeout</tt>, <tt>:settings_timeout</tt>,
|
|
78
|
+
# <tt>:operation_timeout</tt>, <tt>:keep_alive_timeout</tt>, <tt>:read_timeout</tt>, <tt>:write_timeout</tt>
|
|
79
|
+
# and <tt>:request_timeout</tt>
|
|
80
|
+
# :headers :: hash of HTTP headers (ex: <tt>{ "x-custom-foo" => "bar" }</tt>)
|
|
81
|
+
# :window_size :: number of bytes to read from a socket
|
|
82
|
+
# :buffer_size :: internal read and write buffer size in bytes
|
|
83
|
+
# :body_threshold_size :: maximum size in bytes of response payload that is buffered in memory.
|
|
84
|
+
# :request_class :: class used to instantiate a request
|
|
85
|
+
# :response_class :: class used to instantiate a response
|
|
86
|
+
# :headers_class :: class used to instantiate headers
|
|
87
|
+
# :request_body_class :: class used to instantiate a request body
|
|
88
|
+
# :response_body_class :: class used to instantiate a response body
|
|
89
|
+
# :connection_class :: class used to instantiate connections
|
|
90
|
+
# :http1_class :: class used to manage HTTP1 sessions
|
|
91
|
+
# :http2_class :: class used to imanage HTTP2 sessions
|
|
92
|
+
# :resolver_native_class :: class used to resolve names using pure ruby DNS implementation
|
|
93
|
+
# :resolver_system_class :: class used to resolve names using system-based (getaddrinfo) name resolution
|
|
94
|
+
# :resolver_https_class :: class used to resolve names using DoH
|
|
95
|
+
# :pool_class :: class used to instantiate the session connection pool
|
|
96
|
+
# :options_class :: class used to instantiate options
|
|
97
|
+
# :transport :: type of transport to use (set to "unix" for UNIX sockets)
|
|
98
|
+
# :addresses :: bucket of peer addresses (can be a list of IP addresses, a hash of domain to list of adddresses;
|
|
99
|
+
# paths should be used for UNIX sockets instead)
|
|
100
|
+
# :io :: open socket, or domain/ip-to-socket hash, which requests should be sent to
|
|
101
|
+
# :persistent :: whether to persist connections in between requests (defaults to <tt>true</tt>)
|
|
102
|
+
# :resolver_class :: which resolver to use (defaults to <tt>:native</tt>, can also be <tt>:system<tt> for
|
|
103
|
+
# using getaddrinfo or <tt>:https</tt> for DoH resolver, or a custom class)
|
|
104
|
+
# :resolver_options :: hash of options passed to the resolver. Accepted keys depend on the resolver type.
|
|
105
|
+
# :pool_options :: hash of options passed to the connection pool (See Pool#initialize).
|
|
106
|
+
# :ip_families :: which socket families are supported (system-dependent)
|
|
107
|
+
# :origin :: HTTP origin to set on requests with relative path (ex: "https://api.serv.com")
|
|
108
|
+
# :base_path :: path to prefix given relative paths with (ex: "/v2")
|
|
109
|
+
# :max_concurrent_requests :: max number of requests which can be set concurrently
|
|
110
|
+
# :max_requests :: max number of requests which can be made on socket before it reconnects.
|
|
111
|
+
# :close_on_fork :: whether the session automatically closes when the process is fork (defaults to <tt>false</tt>).
|
|
112
|
+
# it only works if the session is persistent (and ruby 3.1 or higher is used).
|
|
113
|
+
#
|
|
114
|
+
# This list of options are enhanced with each loaded plugin, see the plugin docs for details.
|
|
115
|
+
def initialize(options = EMPTY_HASH)
|
|
116
|
+
options_names = self.class.options_names
|
|
117
|
+
|
|
118
|
+
defaults =
|
|
119
|
+
case options
|
|
120
|
+
when Options
|
|
121
|
+
unknown_options = options.class.options_names - options_names
|
|
122
|
+
|
|
123
|
+
raise Error, "unknown option: #{unknown_options.first}" unless unknown_options.empty?
|
|
124
|
+
|
|
125
|
+
DEFAULT_OPTIONS.merge(options)
|
|
126
|
+
else
|
|
127
|
+
options.each_key do |k|
|
|
128
|
+
raise Error, "unknown option: #{k}" unless options_names.include?(k)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
options.empty? ? DEFAULT_OPTIONS : DEFAULT_OPTIONS.merge(options)
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
options_names.each do |k|
|
|
135
|
+
v = defaults[k]
|
|
136
|
+
|
|
137
|
+
if v.nil?
|
|
138
|
+
instance_variable_set(:"@#{k}", v)
|
|
139
|
+
|
|
140
|
+
next
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
value = __send__(:"option_#{k}", v)
|
|
144
|
+
instance_variable_set(:"@#{k}", value)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
freeze
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def freeze
|
|
151
|
+
self.class.options_names.each do |ivar|
|
|
152
|
+
# avoid freezing debug option, as when it's set, it's usually an
|
|
153
|
+
# object which cannot be frozen, like stderr or stdout. It's a
|
|
154
|
+
# documented exception then, and still does not defeat the purpose
|
|
155
|
+
# here, which is to make option objects shareable across ractors,
|
|
156
|
+
# and in most cases debug should be nil, or one of the objects
|
|
157
|
+
# which will eventually be shareable, like STDOUT or STDERR.
|
|
158
|
+
next if ivar == :debug
|
|
159
|
+
|
|
160
|
+
instance_variable_get(:"@#{ivar}").freeze
|
|
161
|
+
end
|
|
162
|
+
super
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
REQUEST_BODY_IVARS = %i[@headers].freeze
|
|
166
|
+
|
|
167
|
+
def ==(other)
|
|
168
|
+
super || options_equals?(other)
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def options_equals?(other, ignore_ivars = REQUEST_BODY_IVARS)
|
|
172
|
+
# headers and other request options do not play a role, as they are
|
|
173
|
+
# relevant only for the request.
|
|
174
|
+
ivars = instance_variables - ignore_ivars
|
|
175
|
+
other_ivars = other.instance_variables - ignore_ivars
|
|
176
|
+
|
|
177
|
+
return false if ivars.size != other_ivars.size
|
|
178
|
+
|
|
179
|
+
return false if ivars.sort != other_ivars.sort
|
|
180
|
+
|
|
181
|
+
ivars.all? do |ivar|
|
|
182
|
+
instance_variable_get(ivar) == other.instance_variable_get(ivar)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def merge(other)
|
|
187
|
+
ivar_map = nil
|
|
188
|
+
other_ivars = case other
|
|
189
|
+
when Options
|
|
190
|
+
other.instance_variables
|
|
191
|
+
else
|
|
192
|
+
other = Hash[other] unless other.is_a?(Hash)
|
|
193
|
+
ivar_map = other.keys.to_h { |k| [:"@#{k}", k] }
|
|
194
|
+
ivar_map.keys
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
return self if other_ivars.empty?
|
|
198
|
+
|
|
199
|
+
return self if other_ivars.all? { |ivar| instance_variable_get(ivar) == access_option(other, ivar, ivar_map) }
|
|
200
|
+
|
|
201
|
+
opts = dup
|
|
202
|
+
|
|
203
|
+
other_ivars.each do |ivar|
|
|
204
|
+
v = access_option(other, ivar, ivar_map)
|
|
205
|
+
|
|
206
|
+
unless v
|
|
207
|
+
opts.instance_variable_set(ivar, v)
|
|
208
|
+
next
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
v = opts.__send__(:"option_#{ivar[1..-1]}", v)
|
|
212
|
+
|
|
213
|
+
orig_v = instance_variable_get(ivar)
|
|
214
|
+
|
|
215
|
+
v = orig_v.merge(v) if orig_v.respond_to?(:merge) && v.respond_to?(:merge)
|
|
216
|
+
|
|
217
|
+
opts.instance_variable_set(ivar, v)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
opts
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def to_hash
|
|
224
|
+
instance_variables.each_with_object({}) do |ivar, hs|
|
|
225
|
+
val = instance_variable_get(ivar)
|
|
226
|
+
|
|
227
|
+
next if val.nil?
|
|
228
|
+
|
|
229
|
+
hs[ivar[1..-1].to_sym] = val
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def extend_with_plugin_classes(pl)
|
|
234
|
+
# extend request class
|
|
235
|
+
if defined?(pl::RequestMethods) || defined?(pl::RequestClassMethods)
|
|
236
|
+
@request_class = @request_class.dup
|
|
237
|
+
SET_TEMPORARY_NAME[@request_class, pl]
|
|
238
|
+
@request_class.__send__(:include, pl::RequestMethods) if defined?(pl::RequestMethods)
|
|
239
|
+
@request_class.extend(pl::RequestClassMethods) if defined?(pl::RequestClassMethods)
|
|
240
|
+
end
|
|
241
|
+
# extend response class
|
|
242
|
+
if defined?(pl::ResponseMethods) || defined?(pl::ResponseClassMethods)
|
|
243
|
+
@response_class = @response_class.dup
|
|
244
|
+
SET_TEMPORARY_NAME[@response_class, pl]
|
|
245
|
+
@response_class.__send__(:include, pl::ResponseMethods) if defined?(pl::ResponseMethods)
|
|
246
|
+
@response_class.extend(pl::ResponseClassMethods) if defined?(pl::ResponseClassMethods)
|
|
247
|
+
end
|
|
248
|
+
# extend headers class
|
|
249
|
+
if defined?(pl::HeadersMethods) || defined?(pl::HeadersClassMethods)
|
|
250
|
+
@headers_class = @headers_class.dup
|
|
251
|
+
SET_TEMPORARY_NAME[@headers_class, pl]
|
|
252
|
+
@headers_class.__send__(:include, pl::HeadersMethods) if defined?(pl::HeadersMethods)
|
|
253
|
+
@headers_class.extend(pl::HeadersClassMethods) if defined?(pl::HeadersClassMethods)
|
|
254
|
+
end
|
|
255
|
+
# extend request body class
|
|
256
|
+
if defined?(pl::RequestBodyMethods) || defined?(pl::RequestBodyClassMethods)
|
|
257
|
+
@request_body_class = @request_body_class.dup
|
|
258
|
+
SET_TEMPORARY_NAME[@request_body_class, pl]
|
|
259
|
+
@request_body_class.__send__(:include, pl::RequestBodyMethods) if defined?(pl::RequestBodyMethods)
|
|
260
|
+
@request_body_class.extend(pl::RequestBodyClassMethods) if defined?(pl::RequestBodyClassMethods)
|
|
261
|
+
end
|
|
262
|
+
# extend response body class
|
|
263
|
+
if defined?(pl::ResponseBodyMethods) || defined?(pl::ResponseBodyClassMethods)
|
|
264
|
+
@response_body_class = @response_body_class.dup
|
|
265
|
+
SET_TEMPORARY_NAME[@response_body_class, pl]
|
|
266
|
+
@response_body_class.__send__(:include, pl::ResponseBodyMethods) if defined?(pl::ResponseBodyMethods)
|
|
267
|
+
@response_body_class.extend(pl::ResponseBodyClassMethods) if defined?(pl::ResponseBodyClassMethods)
|
|
268
|
+
end
|
|
269
|
+
# extend connection pool class
|
|
270
|
+
if defined?(pl::PoolMethods)
|
|
271
|
+
@pool_class = @pool_class.dup
|
|
272
|
+
SET_TEMPORARY_NAME[@pool_class, pl]
|
|
273
|
+
@pool_class.__send__(:include, pl::PoolMethods)
|
|
274
|
+
end
|
|
275
|
+
# extend connection class
|
|
276
|
+
if defined?(pl::ConnectionMethods)
|
|
277
|
+
@connection_class = @connection_class.dup
|
|
278
|
+
SET_TEMPORARY_NAME[@connection_class, pl]
|
|
279
|
+
@connection_class.__send__(:include, pl::ConnectionMethods)
|
|
280
|
+
end
|
|
281
|
+
# extend http1 class
|
|
282
|
+
if defined?(pl::HTTP1Methods)
|
|
283
|
+
@http1_class = @http1_class.dup
|
|
284
|
+
SET_TEMPORARY_NAME[@http1_class, pl]
|
|
285
|
+
@http1_class.__send__(:include, pl::HTTP1Methods)
|
|
286
|
+
end
|
|
287
|
+
# extend http2 class
|
|
288
|
+
if defined?(pl::HTTP2Methods)
|
|
289
|
+
@http2_class = @http2_class.dup
|
|
290
|
+
SET_TEMPORARY_NAME[@http2_class, pl]
|
|
291
|
+
@http2_class.__send__(:include, pl::HTTP2Methods)
|
|
292
|
+
end
|
|
293
|
+
# extend native resolver class
|
|
294
|
+
if defined?(pl::ResolverNativeMethods)
|
|
295
|
+
@resolver_native_class = @resolver_native_class.dup
|
|
296
|
+
SET_TEMPORARY_NAME[@resolver_native_class, pl]
|
|
297
|
+
@resolver_native_class.__send__(:include, pl::ResolverNativeMethods)
|
|
298
|
+
end
|
|
299
|
+
# extend system resolver class
|
|
300
|
+
if defined?(pl::ResolverSystemMethods)
|
|
301
|
+
@resolver_system_class = @resolver_system_class.dup
|
|
302
|
+
SET_TEMPORARY_NAME[@resolver_system_class, pl]
|
|
303
|
+
@resolver_system_class.__send__(:include, pl::ResolverSystemMethods)
|
|
304
|
+
end
|
|
305
|
+
# extend https resolver class
|
|
306
|
+
if defined?(pl::ResolverHTTPSMethods)
|
|
307
|
+
@resolver_https_class = @resolver_https_class.dup
|
|
308
|
+
SET_TEMPORARY_NAME[@resolver_https_class, pl]
|
|
309
|
+
@resolver_https_class.__send__(:include, pl::ResolverHTTPSMethods)
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
return unless defined?(pl::OptionsMethods)
|
|
313
|
+
|
|
314
|
+
# extend option class
|
|
315
|
+
# works around lack of initialize_dup callback
|
|
316
|
+
@options_class = @options_class.dup
|
|
317
|
+
# (self.class.options_names)
|
|
318
|
+
@options_class.__send__(:include, pl::OptionsMethods)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
private
|
|
322
|
+
|
|
323
|
+
# number options
|
|
324
|
+
%i[
|
|
325
|
+
max_concurrent_requests max_requests window_size buffer_size
|
|
326
|
+
body_threshold_size debug_level
|
|
327
|
+
].each do |option|
|
|
328
|
+
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
329
|
+
# converts +v+ into an Integer before setting the +#{option}+ option.
|
|
330
|
+
private def option_#{option}(value) # private def option_max_requests(v)
|
|
331
|
+
value = Integer(value) unless value.respond_to?(:infinite?) && value.infinite?
|
|
332
|
+
raise TypeError, ":#{option} must be positive" unless value.positive? # raise TypeError, ":max_requests must be positive" unless value.positive?
|
|
333
|
+
|
|
334
|
+
value
|
|
335
|
+
end
|
|
336
|
+
OUT
|
|
337
|
+
end
|
|
338
|
+
|
|
339
|
+
# hashable options
|
|
340
|
+
%i[ssl http2_settings resolver_options pool_options].each do |option|
|
|
341
|
+
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
342
|
+
# converts +v+ into an Hash before setting the +#{option}+ option.
|
|
343
|
+
private def option_#{option}(value) # def option_ssl(v)
|
|
344
|
+
Hash[value]
|
|
345
|
+
end
|
|
346
|
+
OUT
|
|
347
|
+
end
|
|
348
|
+
|
|
349
|
+
%i[
|
|
350
|
+
request_class response_class headers_class request_body_class
|
|
351
|
+
response_body_class connection_class http1_class http2_class
|
|
352
|
+
resolver_native_class resolver_system_class resolver_https_class options_class pool_class
|
|
353
|
+
io fallback_protocol debug debug_redact resolver_class
|
|
354
|
+
compress_request_body decompress_response_body
|
|
355
|
+
persistent close_on_fork
|
|
356
|
+
].each do |method_name|
|
|
357
|
+
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
|
358
|
+
# sets +v+ as the value of the +#{method_name}+ option
|
|
359
|
+
private def option_#{method_name}(v); v; end # private def option_smth(v); v; end
|
|
360
|
+
OUT
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
def option_origin(value)
|
|
364
|
+
URI(value)
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
def option_base_path(value)
|
|
368
|
+
String(value)
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def option_headers(value)
|
|
372
|
+
headers_class.new(value)
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def option_timeout(value)
|
|
376
|
+
Hash[value]
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def option_supported_compression_formats(value)
|
|
380
|
+
Array(value).map(&:to_s)
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def option_transport(value)
|
|
384
|
+
transport = value.to_s
|
|
385
|
+
raise TypeError, "#{transport} is an unsupported transport type" unless %w[unix].include?(transport)
|
|
386
|
+
|
|
387
|
+
transport
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
def option_addresses(value)
|
|
391
|
+
Array(value).map { |entry| Resolver::Entry.convert(entry) }
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
def option_ip_families(value)
|
|
395
|
+
Array(value)
|
|
396
|
+
end
|
|
397
|
+
|
|
398
|
+
def access_option(obj, k, ivar_map)
|
|
399
|
+
case obj
|
|
400
|
+
when Hash
|
|
401
|
+
obj[ivar_map[k]]
|
|
402
|
+
else
|
|
403
|
+
obj.instance_variable_get(k)
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
SET_TEMPORARY_NAME = ->(klass, pl = nil) do
|
|
408
|
+
if klass.respond_to?(:set_temporary_name) # ruby 3.4 only
|
|
409
|
+
name = klass.name || "#{klass.superclass.name}(plugin)"
|
|
410
|
+
name = "#{name}/#{pl}" if pl
|
|
411
|
+
klass.set_temporary_name(name)
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
DEFAULT_OPTIONS = {
|
|
416
|
+
:max_requests => Float::INFINITY,
|
|
417
|
+
:debug => nil,
|
|
418
|
+
:debug_level => (ENV["HTTPX_DEBUG"] || 1).to_i,
|
|
419
|
+
:debug_redact => ENV.key?("HTTPX_DEBUG_REDACT"),
|
|
420
|
+
:ssl => EMPTY_HASH,
|
|
421
|
+
:http2_settings => { settings_enable_push: 0 }.freeze,
|
|
422
|
+
:fallback_protocol => "http/1.1",
|
|
423
|
+
:supported_compression_formats => %w[gzip deflate],
|
|
424
|
+
:decompress_response_body => true,
|
|
425
|
+
:compress_request_body => true,
|
|
426
|
+
:timeout => {
|
|
427
|
+
connect_timeout: CONNECT_TIMEOUT,
|
|
428
|
+
settings_timeout: SETTINGS_TIMEOUT,
|
|
429
|
+
close_handshake_timeout: CLOSE_HANDSHAKE_TIMEOUT,
|
|
430
|
+
operation_timeout: OPERATION_TIMEOUT,
|
|
431
|
+
keep_alive_timeout: KEEP_ALIVE_TIMEOUT,
|
|
432
|
+
read_timeout: READ_TIMEOUT,
|
|
433
|
+
write_timeout: WRITE_TIMEOUT,
|
|
434
|
+
request_timeout: REQUEST_TIMEOUT,
|
|
435
|
+
}.freeze,
|
|
436
|
+
:headers_class => Class.new(Headers, &SET_TEMPORARY_NAME),
|
|
437
|
+
:headers => EMPTY_HASH,
|
|
438
|
+
:window_size => WINDOW_SIZE,
|
|
439
|
+
:buffer_size => BUFFER_SIZE,
|
|
440
|
+
:body_threshold_size => MAX_BODY_THRESHOLD_SIZE,
|
|
441
|
+
:request_class => Class.new(Request, &SET_TEMPORARY_NAME),
|
|
442
|
+
:response_class => Class.new(Response, &SET_TEMPORARY_NAME),
|
|
443
|
+
:request_body_class => Class.new(Request::Body, &SET_TEMPORARY_NAME),
|
|
444
|
+
:response_body_class => Class.new(Response::Body, &SET_TEMPORARY_NAME),
|
|
445
|
+
:pool_class => Class.new(Pool, &SET_TEMPORARY_NAME),
|
|
446
|
+
:connection_class => Class.new(Connection, &SET_TEMPORARY_NAME),
|
|
447
|
+
:http1_class => Class.new(Connection::HTTP1, &SET_TEMPORARY_NAME),
|
|
448
|
+
:http2_class => Class.new(Connection::HTTP2, &SET_TEMPORARY_NAME),
|
|
449
|
+
:resolver_native_class => Class.new(Resolver::Native, &SET_TEMPORARY_NAME),
|
|
450
|
+
:resolver_system_class => Class.new(Resolver::System, &SET_TEMPORARY_NAME),
|
|
451
|
+
:resolver_https_class => Class.new(Resolver::HTTPS, &SET_TEMPORARY_NAME),
|
|
452
|
+
:options_class => Class.new(self, &SET_TEMPORARY_NAME),
|
|
453
|
+
:transport => nil,
|
|
454
|
+
:addresses => nil,
|
|
455
|
+
:persistent => false,
|
|
456
|
+
:resolver_class => (ENV["HTTPX_RESOLVER"] || :native).to_sym,
|
|
457
|
+
:resolver_options => { cache: true }.freeze,
|
|
458
|
+
:pool_options => EMPTY_HASH,
|
|
459
|
+
:ip_families => nil,
|
|
460
|
+
:close_on_fork => false,
|
|
461
|
+
}.freeze
|
|
462
|
+
end
|
|
463
|
+
end
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
module Parser
|
|
5
|
+
class Error < Error; end
|
|
6
|
+
|
|
7
|
+
class HTTP1
|
|
8
|
+
VERSIONS = %w[1.0 1.1].freeze
|
|
9
|
+
|
|
10
|
+
attr_reader :status_code, :http_version, :headers
|
|
11
|
+
|
|
12
|
+
def initialize(observer)
|
|
13
|
+
@observer = observer
|
|
14
|
+
@state = :idle
|
|
15
|
+
@buffer = "".b
|
|
16
|
+
@headers = {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def <<(chunk)
|
|
20
|
+
@buffer << chunk
|
|
21
|
+
parse
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def reset!
|
|
25
|
+
@state = :idle
|
|
26
|
+
@headers = {}
|
|
27
|
+
@content_length = nil
|
|
28
|
+
@_has_trailers = nil
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def upgrade?
|
|
32
|
+
@upgrade
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def upgrade_data
|
|
36
|
+
@buffer
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def parse
|
|
42
|
+
loop do
|
|
43
|
+
state = @state
|
|
44
|
+
case @state
|
|
45
|
+
when :idle
|
|
46
|
+
parse_headline
|
|
47
|
+
when :headers, :trailers
|
|
48
|
+
parse_headers
|
|
49
|
+
when :data
|
|
50
|
+
parse_data
|
|
51
|
+
end
|
|
52
|
+
return if @buffer.empty? || state == @state
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def parse_headline
|
|
57
|
+
idx = @buffer.index("\n")
|
|
58
|
+
return unless idx
|
|
59
|
+
|
|
60
|
+
(m = %r{\AHTTP(?:/(\d+\.\d+))?\s+(\d\d\d)(?:\s+(.*))?}in.match(@buffer)) ||
|
|
61
|
+
raise(Error, "wrong head line format")
|
|
62
|
+
version, code, _ = m.captures
|
|
63
|
+
raise(Error, "unsupported HTTP version (HTTP/#{version})") unless version && VERSIONS.include?(version)
|
|
64
|
+
|
|
65
|
+
@http_version = version.split(".").map(&:to_i)
|
|
66
|
+
@status_code = code.to_i
|
|
67
|
+
raise(Error, "wrong status code (#{@status_code})") unless (100..599).cover?(@status_code)
|
|
68
|
+
|
|
69
|
+
@buffer = @buffer.byteslice((idx + 1)..-1)
|
|
70
|
+
nextstate(:headers)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def parse_headers
|
|
74
|
+
headers = @headers
|
|
75
|
+
buffer = @buffer
|
|
76
|
+
|
|
77
|
+
while (idx = buffer.index("\n"))
|
|
78
|
+
# @type var line: String
|
|
79
|
+
line = buffer.byteslice(0..idx)
|
|
80
|
+
raise Error, "wrong header format" if line.start_with?("\s", "\t")
|
|
81
|
+
|
|
82
|
+
line.lstrip!
|
|
83
|
+
buffer = @buffer = buffer.byteslice((idx + 1)..-1)
|
|
84
|
+
if line.empty?
|
|
85
|
+
case @state
|
|
86
|
+
when :headers
|
|
87
|
+
prepare_data(headers)
|
|
88
|
+
@observer.on_headers(headers)
|
|
89
|
+
return unless @state == :headers
|
|
90
|
+
|
|
91
|
+
# state might have been reset
|
|
92
|
+
# in the :headers callback
|
|
93
|
+
nextstate(:data)
|
|
94
|
+
headers.clear
|
|
95
|
+
when :trailers
|
|
96
|
+
@observer.on_trailers(headers)
|
|
97
|
+
headers.clear
|
|
98
|
+
nextstate(:complete)
|
|
99
|
+
end
|
|
100
|
+
return
|
|
101
|
+
end
|
|
102
|
+
separator_index = line.index(":")
|
|
103
|
+
raise Error, "wrong header format" unless separator_index
|
|
104
|
+
|
|
105
|
+
# @type var key: String
|
|
106
|
+
key = line.byteslice(0..(separator_index - 1))
|
|
107
|
+
|
|
108
|
+
key.rstrip! # was lstripped previously!
|
|
109
|
+
# @type var value: String
|
|
110
|
+
value = line.byteslice((separator_index + 1)..-1)
|
|
111
|
+
value.strip!
|
|
112
|
+
raise Error, "wrong header format" if value.nil?
|
|
113
|
+
|
|
114
|
+
(headers[key.downcase] ||= []) << value
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def parse_data
|
|
119
|
+
if @buffer.respond_to?(:each)
|
|
120
|
+
@buffer.each do |chunk|
|
|
121
|
+
@observer.on_data(chunk)
|
|
122
|
+
end
|
|
123
|
+
elsif @content_length
|
|
124
|
+
# @type var data: String
|
|
125
|
+
data = @buffer.byteslice(0, @content_length)
|
|
126
|
+
@buffer = @buffer.byteslice(@content_length..-1) || "".b
|
|
127
|
+
@content_length -= data.bytesize
|
|
128
|
+
@observer.on_data(data)
|
|
129
|
+
data.clear
|
|
130
|
+
else
|
|
131
|
+
@observer.on_data(@buffer)
|
|
132
|
+
@buffer.clear
|
|
133
|
+
end
|
|
134
|
+
return unless no_more_data?
|
|
135
|
+
|
|
136
|
+
@buffer = @buffer.to_s
|
|
137
|
+
if @_has_trailers
|
|
138
|
+
nextstate(:trailers)
|
|
139
|
+
else
|
|
140
|
+
nextstate(:complete)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def prepare_data(headers)
|
|
145
|
+
@upgrade = headers.key?("upgrade")
|
|
146
|
+
|
|
147
|
+
@_has_trailers = headers.key?("trailer")
|
|
148
|
+
|
|
149
|
+
if (tr_encodings = headers["transfer-encoding"])
|
|
150
|
+
tr_encodings.reverse_each do |tr_encoding|
|
|
151
|
+
tr_encoding.split(/ *, */).each do |encoding|
|
|
152
|
+
case encoding
|
|
153
|
+
when "chunked"
|
|
154
|
+
@buffer = Transcoder::Chunker::Decoder.new(@buffer, @_has_trailers)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
else
|
|
159
|
+
@content_length = headers["content-length"][0].to_i if headers.key?("content-length")
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def no_more_data?
|
|
164
|
+
if @content_length
|
|
165
|
+
@content_length <= 0
|
|
166
|
+
elsif @buffer.respond_to?(:finished?)
|
|
167
|
+
@buffer.finished?
|
|
168
|
+
else
|
|
169
|
+
false
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def nextstate(state)
|
|
174
|
+
@state = state
|
|
175
|
+
case state
|
|
176
|
+
when :headers
|
|
177
|
+
@observer.on_start
|
|
178
|
+
when :complete
|
|
179
|
+
@observer.on_complete
|
|
180
|
+
reset!
|
|
181
|
+
nextstate(:idle) unless @buffer.empty?
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "httpx/base64"
|
|
4
|
+
|
|
5
|
+
module HTTPX
|
|
6
|
+
module Plugins
|
|
7
|
+
module Authentication
|
|
8
|
+
class Basic
|
|
9
|
+
def initialize(user, password, **)
|
|
10
|
+
@user = user
|
|
11
|
+
@password = password
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def authenticate(*)
|
|
15
|
+
"Basic #{Base64.strict_encode64("#{@user}:#{@password}")}"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|