httpx 0.21.0 → 1.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 +4 -4
- data/LICENSE.txt +0 -48
- data/README.md +54 -45
- data/doc/release_notes/0_10_0.md +2 -2
- data/doc/release_notes/0_11_0.md +3 -5
- data/doc/release_notes/0_12_0.md +5 -5
- data/doc/release_notes/0_13_0.md +4 -4
- data/doc/release_notes/0_14_0.md +2 -2
- data/doc/release_notes/0_16_0.md +3 -3
- data/doc/release_notes/0_17_0.md +1 -1
- data/doc/release_notes/0_18_0.md +4 -4
- data/doc/release_notes/0_18_2.md +1 -1
- data/doc/release_notes/0_19_0.md +1 -1
- data/doc/release_notes/0_20_0.md +1 -1
- data/doc/release_notes/0_21_0.md +7 -5
- 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/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/lib/httpx/adapters/datadog.rb +100 -106
- data/lib/httpx/adapters/faraday.rb +143 -107
- data/lib/httpx/adapters/sentry.rb +26 -7
- data/lib/httpx/adapters/webmock.rb +33 -17
- data/lib/httpx/altsvc.rb +61 -24
- data/lib/httpx/base64.rb +27 -0
- data/lib/httpx/buffer.rb +12 -0
- data/lib/httpx/callbacks.rb +5 -3
- data/lib/httpx/chainable.rb +54 -39
- data/lib/httpx/connection/http1.rb +62 -37
- data/lib/httpx/connection/http2.rb +16 -27
- data/lib/httpx/connection.rb +213 -120
- data/lib/httpx/domain_name.rb +10 -13
- data/lib/httpx/errors.rb +34 -2
- data/lib/httpx/extensions.rb +4 -134
- data/lib/httpx/io/ssl.rb +77 -71
- data/lib/httpx/io/tcp.rb +46 -70
- data/lib/httpx/io/udp.rb +18 -52
- data/lib/httpx/io/unix.rb +6 -13
- data/lib/httpx/io.rb +3 -9
- data/lib/httpx/loggable.rb +4 -19
- data/lib/httpx/options.rb +168 -110
- data/lib/httpx/plugins/{authentication → auth}/basic.rb +1 -5
- data/lib/httpx/plugins/{authentication → auth}/digest.rb +13 -14
- data/lib/httpx/plugins/{authentication → auth}/ntlm.rb +1 -3
- data/lib/httpx/plugins/{authentication → auth}/socks5.rb +0 -2
- data/lib/httpx/plugins/auth.rb +25 -0
- data/lib/httpx/plugins/aws_sdk_authentication.rb +1 -3
- data/lib/httpx/plugins/aws_sigv4.rb +5 -6
- data/lib/httpx/plugins/basic_auth.rb +29 -0
- data/lib/httpx/plugins/brotli.rb +50 -0
- data/lib/httpx/plugins/callbacks.rb +91 -0
- data/lib/httpx/plugins/circuit_breaker/circuit.rb +40 -16
- data/lib/httpx/plugins/circuit_breaker/circuit_store.rb +14 -5
- data/lib/httpx/plugins/circuit_breaker.rb +30 -7
- data/lib/httpx/plugins/cookies/set_cookie_parser.rb +0 -2
- data/lib/httpx/plugins/cookies.rb +20 -10
- data/lib/httpx/plugins/{digest_authentication.rb → digest_auth.rb} +11 -12
- data/lib/httpx/plugins/expect.rb +15 -13
- data/lib/httpx/plugins/follow_redirects.rb +71 -29
- data/lib/httpx/plugins/grpc/call.rb +2 -3
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +88 -0
- data/lib/httpx/plugins/grpc/message.rb +7 -37
- data/lib/httpx/plugins/grpc.rb +35 -29
- data/lib/httpx/plugins/h2c.rb +25 -18
- data/lib/httpx/plugins/internal_telemetry.rb +16 -0
- data/lib/httpx/plugins/{ntlm_authentication.rb → ntlm_auth.rb} +7 -5
- data/lib/httpx/plugins/oauth.rb +170 -0
- data/lib/httpx/plugins/persistent.rb +1 -1
- data/lib/httpx/plugins/proxy/http.rb +15 -10
- data/lib/httpx/plugins/proxy/socks4.rb +8 -6
- data/lib/httpx/plugins/proxy/socks5.rb +10 -8
- data/lib/httpx/plugins/proxy.rb +69 -67
- data/lib/httpx/plugins/push_promise.rb +1 -1
- data/lib/httpx/plugins/rate_limiter.rb +3 -1
- data/lib/httpx/plugins/response_cache/file_store.rb +40 -0
- data/lib/httpx/plugins/response_cache/store.rb +34 -17
- data/lib/httpx/plugins/response_cache.rb +6 -6
- data/lib/httpx/plugins/retries.rb +61 -12
- data/lib/httpx/plugins/ssrf_filter.rb +142 -0
- data/lib/httpx/plugins/stream.rb +27 -32
- data/lib/httpx/plugins/upgrade/h2.rb +4 -4
- data/lib/httpx/plugins/upgrade.rb +8 -10
- data/lib/httpx/plugins/webdav.rb +10 -8
- data/lib/httpx/pool.rb +85 -23
- data/lib/httpx/punycode.rb +9 -291
- data/lib/httpx/request/body.rb +158 -0
- data/lib/httpx/request.rb +86 -121
- data/lib/httpx/resolver/https.rb +54 -17
- data/lib/httpx/resolver/multi.rb +8 -12
- data/lib/httpx/resolver/native.rb +163 -70
- data/lib/httpx/resolver/resolver.rb +28 -13
- data/lib/httpx/resolver/system.rb +15 -10
- data/lib/httpx/resolver.rb +38 -16
- data/lib/httpx/response/body.rb +242 -0
- data/lib/httpx/response/buffer.rb +96 -0
- data/lib/httpx/response.rb +113 -211
- data/lib/httpx/selector.rb +2 -4
- data/lib/httpx/session.rb +91 -64
- data/lib/httpx/session_extensions.rb +4 -1
- data/lib/httpx/timers.rb +28 -8
- data/lib/httpx/transcoder/body.rb +0 -2
- data/lib/httpx/transcoder/chunker.rb +0 -1
- data/lib/httpx/transcoder/deflate.rb +37 -0
- data/lib/httpx/transcoder/form.rb +52 -33
- data/lib/httpx/transcoder/gzip.rb +74 -0
- data/lib/httpx/transcoder/json.rb +2 -5
- data/lib/httpx/transcoder/multipart/decoder.rb +139 -0
- data/lib/httpx/{plugins → transcoder}/multipart/encoder.rb +3 -3
- data/lib/httpx/{plugins → transcoder}/multipart/mime_type_detector.rb +1 -1
- data/lib/httpx/{plugins → transcoder}/multipart/part.rb +3 -2
- data/lib/httpx/transcoder/multipart.rb +17 -0
- data/lib/httpx/transcoder/utils/body_reader.rb +46 -0
- data/lib/httpx/transcoder/utils/deflater.rb +72 -0
- data/lib/httpx/transcoder/utils/inflater.rb +19 -0
- data/lib/httpx/transcoder/xml.rb +0 -5
- data/lib/httpx/transcoder.rb +4 -6
- data/lib/httpx/utils.rb +36 -16
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +12 -14
- data/sig/altsvc.rbs +33 -0
- data/sig/buffer.rbs +1 -0
- data/sig/callbacks.rbs +3 -3
- data/sig/chainable.rbs +10 -9
- data/sig/connection/http1.rbs +5 -4
- data/sig/connection/http2.rbs +1 -1
- data/sig/connection.rbs +46 -24
- data/sig/errors.rbs +9 -3
- data/sig/httpx.rbs +5 -4
- data/sig/io/ssl.rbs +26 -0
- data/sig/io/tcp.rbs +60 -0
- data/sig/io/udp.rbs +20 -0
- data/sig/io/unix.rbs +10 -0
- data/sig/options.rbs +28 -12
- data/sig/plugins/{authentication → auth}/basic.rbs +0 -2
- data/sig/plugins/{authentication → auth}/digest.rbs +2 -1
- data/sig/plugins/auth.rbs +13 -0
- data/sig/plugins/{basic_authentication.rbs → basic_auth.rbs} +2 -2
- data/sig/plugins/brotli.rbs +22 -0
- data/sig/plugins/callbacks.rbs +38 -0
- data/sig/plugins/circuit_breaker.rbs +13 -3
- data/sig/plugins/compression.rbs +6 -4
- data/sig/plugins/cookies/jar.rbs +2 -2
- data/sig/plugins/cookies.rbs +2 -0
- data/sig/plugins/{digest_authentication.rbs → digest_auth.rbs} +2 -2
- data/sig/plugins/follow_redirects.rbs +11 -2
- data/sig/plugins/grpc/call.rbs +19 -0
- data/sig/plugins/grpc/grpc_encoding.rbs +37 -0
- data/sig/plugins/grpc/message.rbs +17 -0
- data/sig/plugins/grpc.rbs +2 -32
- data/sig/plugins/h2c.rbs +1 -1
- data/sig/plugins/{ntlm_authentication.rbs → ntlm_auth.rbs} +2 -2
- data/sig/plugins/oauth.rbs +54 -0
- data/sig/plugins/proxy/socks4.rbs +4 -4
- data/sig/plugins/proxy/socks5.rbs +2 -2
- data/sig/plugins/proxy/ssh.rbs +1 -1
- data/sig/plugins/proxy.rbs +10 -4
- data/sig/plugins/response_cache.rbs +12 -3
- data/sig/plugins/retries.rbs +28 -8
- data/sig/plugins/stream.rbs +24 -17
- data/sig/plugins/upgrade.rbs +5 -3
- data/sig/pool.rbs +5 -4
- data/sig/request/body.rbs +40 -0
- data/sig/request.rbs +12 -28
- data/sig/resolver/https.rbs +7 -2
- data/sig/resolver/native.rbs +10 -4
- data/sig/resolver/resolver.rbs +6 -4
- data/sig/resolver/system.rbs +2 -0
- data/sig/resolver.rbs +9 -5
- data/sig/response/body.rbs +53 -0
- data/sig/response/buffer.rbs +24 -0
- data/sig/response.rbs +17 -38
- data/sig/session.rbs +24 -18
- data/sig/timers.rbs +17 -7
- data/sig/transcoder/body.rbs +4 -3
- data/sig/transcoder/deflate.rbs +11 -0
- data/sig/transcoder/form.rbs +5 -3
- data/sig/transcoder/gzip.rbs +24 -0
- data/sig/transcoder/json.rbs +4 -2
- data/sig/{plugins → transcoder}/multipart.rbs +3 -12
- data/sig/transcoder/utils/body_reader.rbs +15 -0
- data/sig/transcoder/utils/deflater.rbs +29 -0
- data/sig/transcoder/utils/inflater.rbs +12 -0
- data/sig/transcoder/xml.rbs +1 -1
- data/sig/transcoder.rbs +22 -7
- data/sig/utils.rbs +2 -0
- metadata +127 -40
- data/lib/httpx/plugins/authentication.rb +0 -20
- data/lib/httpx/plugins/basic_authentication.rb +0 -30
- data/lib/httpx/plugins/compression/brotli.rb +0 -54
- data/lib/httpx/plugins/compression/deflate.rb +0 -49
- data/lib/httpx/plugins/compression/gzip.rb +0 -88
- data/lib/httpx/plugins/compression.rb +0 -164
- data/lib/httpx/plugins/multipart/decoder.rb +0 -187
- data/lib/httpx/plugins/multipart.rb +0 -84
- data/lib/httpx/registry.rb +0 -85
- data/sig/plugins/authentication.rbs +0 -11
- data/sig/plugins/compression/brotli.rbs +0 -21
- data/sig/plugins/compression/deflate.rbs +0 -17
- data/sig/plugins/compression/gzip.rbs +0 -29
- data/sig/registry.rbs +0 -13
- /data/sig/plugins/{authentication → auth}/ntlm.rbs +0 -0
- /data/sig/plugins/{authentication → auth}/socks5.rbs +0 -0
|
@@ -32,6 +32,15 @@ module HTTPX
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
+
module NativeResolverMethods
|
|
36
|
+
def transition(nextstate)
|
|
37
|
+
state = @state
|
|
38
|
+
val = super
|
|
39
|
+
meter_elapsed_time("Resolver::Native: #{state} -> #{nextstate}")
|
|
40
|
+
val
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
35
44
|
module InstanceMethods
|
|
36
45
|
def self.included(klass)
|
|
37
46
|
klass.prepend TrackTimeMethods
|
|
@@ -42,6 +51,13 @@ module HTTPX
|
|
|
42
51
|
meter_elapsed_time("Session: initializing...")
|
|
43
52
|
super
|
|
44
53
|
meter_elapsed_time("Session: initialized!!!")
|
|
54
|
+
resolver_type = @options.resolver_class
|
|
55
|
+
resolver_type = Resolver.resolver_for(resolver_type)
|
|
56
|
+
return unless resolver_type <= Resolver::Native
|
|
57
|
+
|
|
58
|
+
resolver_type.prepend TrackTimeMethods
|
|
59
|
+
resolver_type.prepend NativeResolverMethods
|
|
60
|
+
@options = @options.merge(resolver_class: resolver_type)
|
|
45
61
|
end
|
|
46
62
|
|
|
47
63
|
def close(*)
|
|
@@ -3,12 +3,12 @@
|
|
|
3
3
|
module HTTPX
|
|
4
4
|
module Plugins
|
|
5
5
|
#
|
|
6
|
-
# https://gitlab.com/
|
|
6
|
+
# https://gitlab.com/os85/httpx/wikis/Auth#ntlm-auth
|
|
7
7
|
#
|
|
8
8
|
module NTLMAuth
|
|
9
9
|
class << self
|
|
10
10
|
def load_dependencies(_klass)
|
|
11
|
-
require_relative "
|
|
11
|
+
require_relative "auth/ntlm"
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def extra_options(options)
|
|
@@ -25,11 +25,11 @@ module HTTPX
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
module InstanceMethods
|
|
28
|
-
def
|
|
28
|
+
def ntlm_auth(user, password, domain = nil)
|
|
29
29
|
with(ntlm: Authentication::Ntlm.new(user, password, domain: domain))
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
private
|
|
33
33
|
|
|
34
34
|
def send_requests(*requests)
|
|
35
35
|
requests.flat_map do |request|
|
|
@@ -39,6 +39,8 @@ module HTTPX
|
|
|
39
39
|
request.headers["authorization"] = ntlm.negotiate
|
|
40
40
|
probe_response = wrap { super(request).first }
|
|
41
41
|
|
|
42
|
+
return probe_response unless probe_response.is_a?(Response)
|
|
43
|
+
|
|
42
44
|
if probe_response.status == 401 && ntlm.can_authenticate?(probe_response.headers["www-authenticate"])
|
|
43
45
|
request.transition(:idle)
|
|
44
46
|
request.headers["authorization"] = ntlm.authenticate(request, probe_response.headers["www-authenticate"])
|
|
@@ -53,6 +55,6 @@ module HTTPX
|
|
|
53
55
|
end
|
|
54
56
|
end
|
|
55
57
|
end
|
|
56
|
-
register_plugin :
|
|
58
|
+
register_plugin :ntlm_auth, NTLMAuth
|
|
57
59
|
end
|
|
58
60
|
end
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module HTTPX
|
|
4
|
+
module Plugins
|
|
5
|
+
#
|
|
6
|
+
# https://gitlab.com/os85/httpx/wikis/OAuth
|
|
7
|
+
#
|
|
8
|
+
module OAuth
|
|
9
|
+
class << self
|
|
10
|
+
def load_dependencies(_klass)
|
|
11
|
+
require_relative "auth/basic"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
SUPPORTED_GRANT_TYPES = %w[client_credentials refresh_token].freeze
|
|
16
|
+
SUPPORTED_AUTH_METHODS = %w[client_secret_basic client_secret_post].freeze
|
|
17
|
+
|
|
18
|
+
class OAuthSession
|
|
19
|
+
attr_reader :token_endpoint_auth_method, :grant_type, :client_id, :client_secret, :access_token, :refresh_token, :scope
|
|
20
|
+
|
|
21
|
+
def initialize(
|
|
22
|
+
issuer:,
|
|
23
|
+
client_id:,
|
|
24
|
+
client_secret:,
|
|
25
|
+
access_token: nil,
|
|
26
|
+
refresh_token: nil,
|
|
27
|
+
scope: nil,
|
|
28
|
+
token_endpoint: nil,
|
|
29
|
+
response_type: nil,
|
|
30
|
+
grant_type: nil,
|
|
31
|
+
token_endpoint_auth_method: "client_secret_basic"
|
|
32
|
+
)
|
|
33
|
+
@issuer = URI(issuer)
|
|
34
|
+
@client_id = client_id
|
|
35
|
+
@client_secret = client_secret
|
|
36
|
+
@token_endpoint = URI(token_endpoint) if token_endpoint
|
|
37
|
+
@response_type = response_type
|
|
38
|
+
@scope = case scope
|
|
39
|
+
when String
|
|
40
|
+
scope.split
|
|
41
|
+
when Array
|
|
42
|
+
scope
|
|
43
|
+
end
|
|
44
|
+
@access_token = access_token
|
|
45
|
+
@refresh_token = refresh_token
|
|
46
|
+
@token_endpoint_auth_method = String(token_endpoint_auth_method)
|
|
47
|
+
@grant_type = grant_type || (@refresh_token ? "refresh_token" : "client_credentials")
|
|
48
|
+
|
|
49
|
+
unless SUPPORTED_AUTH_METHODS.include?(@token_endpoint_auth_method)
|
|
50
|
+
raise Error, "#{@token_endpoint_auth_method} is not a supported auth method"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
return if SUPPORTED_GRANT_TYPES.include?(@grant_type)
|
|
54
|
+
|
|
55
|
+
raise Error, "#{@grant_type} is not a supported grant type"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def token_endpoint
|
|
59
|
+
@token_endpoint || "#{@issuer}/token"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def load(http)
|
|
63
|
+
return unless @token_endpoint && @token_endpoint_auth_method && @grant_type && @scope
|
|
64
|
+
|
|
65
|
+
metadata = http.get("#{issuer}/.well-known/oauth-authorization-server").raise_for_status.json
|
|
66
|
+
|
|
67
|
+
@token_endpoint = metadata["token_endpoint"]
|
|
68
|
+
@scope = metadata["scopes_supported"]
|
|
69
|
+
@grant_type = Array(metadata["grant_types_supported"]).find { |gr| SUPPORTED_GRANT_TYPES.include?(gr) }
|
|
70
|
+
@token_endpoint_auth_method = Array(metadata["token_endpoint_auth_methods_supported"]).find do |am|
|
|
71
|
+
SUPPORTED_AUTH_METHODS.include?(am)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def merge(other)
|
|
76
|
+
obj = dup
|
|
77
|
+
|
|
78
|
+
case other
|
|
79
|
+
when OAuthSession
|
|
80
|
+
other.instance_variables.each do |ivar|
|
|
81
|
+
val = other.instance_variable_get(ivar)
|
|
82
|
+
next unless val
|
|
83
|
+
|
|
84
|
+
obj.instance_variable_set(ivar, val)
|
|
85
|
+
end
|
|
86
|
+
when Hash
|
|
87
|
+
other.each do |k, v|
|
|
88
|
+
obj.instance_variable_set(:"@#{k}", v) if obj.instance_variable_defined?(:"@#{k}")
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
obj
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
module OptionsMethods
|
|
96
|
+
def option_oauth_session(value)
|
|
97
|
+
case value
|
|
98
|
+
when Hash
|
|
99
|
+
OAuthSession.new(**value)
|
|
100
|
+
when OAuthSession
|
|
101
|
+
value
|
|
102
|
+
else
|
|
103
|
+
raise TypeError, ":oauth_session must be a #{OAuthSession}"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
module InstanceMethods
|
|
109
|
+
def oauth_auth(**args)
|
|
110
|
+
with(oauth_session: OAuthSession.new(**args))
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def with_access_token
|
|
114
|
+
oauth_session = @options.oauth_session
|
|
115
|
+
|
|
116
|
+
oauth_session.load(self)
|
|
117
|
+
|
|
118
|
+
grant_type = oauth_session.grant_type
|
|
119
|
+
|
|
120
|
+
headers = {}
|
|
121
|
+
form_post = { "grant_type" => grant_type, "scope" => Array(oauth_session.scope).join(" ") }.compact
|
|
122
|
+
|
|
123
|
+
# auth
|
|
124
|
+
case oauth_session.token_endpoint_auth_method
|
|
125
|
+
when "client_secret_basic"
|
|
126
|
+
headers["authorization"] = Authentication::Basic.new(oauth_session.client_id, oauth_session.client_secret).authenticate
|
|
127
|
+
when "client_secret_post"
|
|
128
|
+
form_post["client_id"] = oauth_session.client_id
|
|
129
|
+
form_post["client_secret"] = oauth_session.client_secret
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
case grant_type
|
|
133
|
+
when "client_credentials"
|
|
134
|
+
# do nothing
|
|
135
|
+
when "refresh_token"
|
|
136
|
+
form_post["refresh_token"] = oauth_session.refresh_token
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
token_request = build_request("POST", oauth_session.token_endpoint, headers: headers, form: form_post)
|
|
140
|
+
token_request.headers.delete("authorization") unless oauth_session.token_endpoint_auth_method == "client_secret_basic"
|
|
141
|
+
|
|
142
|
+
token_response = request(token_request)
|
|
143
|
+
token_response.raise_for_status
|
|
144
|
+
|
|
145
|
+
payload = token_response.json
|
|
146
|
+
|
|
147
|
+
access_token = payload["access_token"]
|
|
148
|
+
refresh_token = payload["refresh_token"]
|
|
149
|
+
|
|
150
|
+
with(oauth_session: oauth_session.merge(access_token: access_token, refresh_token: refresh_token))
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def build_request(*, _)
|
|
154
|
+
request = super
|
|
155
|
+
|
|
156
|
+
return request if request.headers.key?("authorization")
|
|
157
|
+
|
|
158
|
+
oauth_session = @options.oauth_session
|
|
159
|
+
|
|
160
|
+
return request unless oauth_session && oauth_session.access_token
|
|
161
|
+
|
|
162
|
+
request.headers["authorization"] = "Bearer #{oauth_session.access_token}"
|
|
163
|
+
|
|
164
|
+
request
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
register_plugin :oauth, OAuth
|
|
169
|
+
end
|
|
170
|
+
end
|
|
@@ -15,7 +15,7 @@ module HTTPX
|
|
|
15
15
|
# This plugin is also not recommendable when connecting to >9000 (like, a lot) different origins.
|
|
16
16
|
# So when you use this, make sure that you don't fall into this trap.
|
|
17
17
|
#
|
|
18
|
-
# https://gitlab.com/
|
|
18
|
+
# https://gitlab.com/os85/httpx/wikis/Persistent
|
|
19
19
|
#
|
|
20
20
|
module Persistent
|
|
21
21
|
def self.load_dependencies(klass)
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "base64"
|
|
4
|
-
|
|
5
3
|
module HTTPX
|
|
6
4
|
module Plugins
|
|
7
5
|
module Proxy
|
|
8
6
|
module HTTP
|
|
7
|
+
class << self
|
|
8
|
+
def extra_options(options)
|
|
9
|
+
options.merge(supported_proxy_protocols: options.supported_proxy_protocols + %w[http])
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
9
13
|
module InstanceMethods
|
|
10
14
|
def with_proxy_basic_auth(opts)
|
|
11
15
|
with(proxy: opts.merge(scheme: "basic"))
|
|
@@ -23,6 +27,7 @@ module HTTPX
|
|
|
23
27
|
response = super
|
|
24
28
|
|
|
25
29
|
if response &&
|
|
30
|
+
response.is_a?(Response) &&
|
|
26
31
|
response.status == 407 &&
|
|
27
32
|
!request.headers.key?("proxy-authorization") &&
|
|
28
33
|
response.headers.key?("proxy-authenticate")
|
|
@@ -33,7 +38,7 @@ module HTTPX
|
|
|
33
38
|
request.transition(:idle)
|
|
34
39
|
request.headers["proxy-authorization"] =
|
|
35
40
|
connection.options.proxy.authenticate(request, response.headers["proxy-authenticate"])
|
|
36
|
-
|
|
41
|
+
send_request(request, connections)
|
|
37
42
|
return
|
|
38
43
|
end
|
|
39
44
|
end
|
|
@@ -60,7 +65,7 @@ module HTTPX
|
|
|
60
65
|
return unless @io.connected?
|
|
61
66
|
|
|
62
67
|
@parser || begin
|
|
63
|
-
@parser =
|
|
68
|
+
@parser = self.class.parser_type(@io.protocol).new(@write_buffer, @options.merge(max_concurrent_requests: 1))
|
|
64
69
|
parser = @parser
|
|
65
70
|
parser.extend(ProxyParser)
|
|
66
71
|
parser.on(:response, &method(:__http_on_connect))
|
|
@@ -71,7 +76,6 @@ module HTTPX
|
|
|
71
76
|
else
|
|
72
77
|
transition(:closing)
|
|
73
78
|
transition(:closed)
|
|
74
|
-
emit(:reset)
|
|
75
79
|
|
|
76
80
|
parser.reset if @parser
|
|
77
81
|
transition(:idle)
|
|
@@ -113,13 +117,14 @@ module HTTPX
|
|
|
113
117
|
|
|
114
118
|
def __http_on_connect(request, response)
|
|
115
119
|
@inflight -= 1
|
|
116
|
-
if response.status == 200
|
|
120
|
+
if response.is_a?(Response) && response.status == 200
|
|
117
121
|
req = @pending.first
|
|
118
122
|
request_uri = req.uri
|
|
119
123
|
@io = ProxySSL.new(@io, request_uri, @options)
|
|
120
124
|
transition(:connected)
|
|
121
125
|
throw(:called)
|
|
122
|
-
elsif response.
|
|
126
|
+
elsif response.is_a?(Response) &&
|
|
127
|
+
response.status == 407 &&
|
|
123
128
|
!request.headers.key?("proxy-authorization") &&
|
|
124
129
|
@options.proxy.can_authenticate?(response.headers["proxy-authenticate"])
|
|
125
130
|
|
|
@@ -139,9 +144,9 @@ module HTTPX
|
|
|
139
144
|
|
|
140
145
|
module ProxyParser
|
|
141
146
|
def join_headline(request)
|
|
142
|
-
return super if request.verb ==
|
|
147
|
+
return super if request.verb == "CONNECT"
|
|
143
148
|
|
|
144
|
-
"#{request.verb
|
|
149
|
+
"#{request.verb} #{request.uri} HTTP/#{@version.join(".")}"
|
|
145
150
|
end
|
|
146
151
|
|
|
147
152
|
def set_protocol_headers(request)
|
|
@@ -159,7 +164,7 @@ module HTTPX
|
|
|
159
164
|
|
|
160
165
|
class ConnectRequest < Request
|
|
161
166
|
def initialize(uri, _options)
|
|
162
|
-
super(
|
|
167
|
+
super("CONNECT", uri, {})
|
|
163
168
|
@headers.delete("accept")
|
|
164
169
|
end
|
|
165
170
|
|
|
@@ -4,7 +4,7 @@ require "resolv"
|
|
|
4
4
|
require "ipaddr"
|
|
5
5
|
|
|
6
6
|
module HTTPX
|
|
7
|
-
class Socks4Error <
|
|
7
|
+
class Socks4Error < HTTPProxyError; end
|
|
8
8
|
|
|
9
9
|
module Plugins
|
|
10
10
|
module Proxy
|
|
@@ -16,6 +16,12 @@ module HTTPX
|
|
|
16
16
|
|
|
17
17
|
Error = Socks4Error
|
|
18
18
|
|
|
19
|
+
class << self
|
|
20
|
+
def extra_options(options)
|
|
21
|
+
options.merge(supported_proxy_protocols: options.supported_proxy_protocols + PROTOCOLS)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
19
25
|
module ConnectionMethods
|
|
20
26
|
def interests
|
|
21
27
|
if @state == :connecting
|
|
@@ -79,17 +85,13 @@ module HTTPX
|
|
|
79
85
|
end
|
|
80
86
|
|
|
81
87
|
class SocksParser
|
|
82
|
-
include Callbacks
|
|
88
|
+
include HTTPX::Callbacks
|
|
83
89
|
|
|
84
90
|
def initialize(buffer, options)
|
|
85
91
|
@buffer = buffer
|
|
86
92
|
@options = Options.new(options)
|
|
87
93
|
end
|
|
88
94
|
|
|
89
|
-
def timeout
|
|
90
|
-
@options.timeout[:operation_timeout]
|
|
91
|
-
end
|
|
92
|
-
|
|
93
95
|
def close; end
|
|
94
96
|
|
|
95
97
|
def consume(*); end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module HTTPX
|
|
4
|
-
class Socks5Error <
|
|
4
|
+
class Socks5Error < HTTPProxyError; end
|
|
5
5
|
|
|
6
6
|
module Plugins
|
|
7
7
|
module Proxy
|
|
@@ -18,8 +18,14 @@ module HTTPX
|
|
|
18
18
|
|
|
19
19
|
Error = Socks5Error
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
class << self
|
|
22
|
+
def load_dependencies(*)
|
|
23
|
+
require_relative "../auth/socks5"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def extra_options(options)
|
|
27
|
+
options.merge(supported_proxy_protocols: options.supported_proxy_protocols + %w[socks5])
|
|
28
|
+
end
|
|
23
29
|
end
|
|
24
30
|
|
|
25
31
|
module ConnectionMethods
|
|
@@ -131,17 +137,13 @@ module HTTPX
|
|
|
131
137
|
end
|
|
132
138
|
|
|
133
139
|
class SocksParser
|
|
134
|
-
include Callbacks
|
|
140
|
+
include HTTPX::Callbacks
|
|
135
141
|
|
|
136
142
|
def initialize(buffer, options)
|
|
137
143
|
@buffer = buffer
|
|
138
144
|
@options = Options.new(options)
|
|
139
145
|
end
|
|
140
146
|
|
|
141
|
-
def timeout
|
|
142
|
-
@options.timeout[:operation_timeout]
|
|
143
|
-
end
|
|
144
|
-
|
|
145
147
|
def close; end
|
|
146
148
|
|
|
147
149
|
def consume(*); end
|
data/lib/httpx/plugins/proxy.rb
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module HTTPX
|
|
4
|
-
class HTTPProxyError <
|
|
4
|
+
class HTTPProxyError < ConnectionError; end
|
|
5
5
|
|
|
6
6
|
module Plugins
|
|
7
7
|
#
|
|
@@ -12,12 +12,24 @@ module HTTPX
|
|
|
12
12
|
# * Socks4/4a proxies
|
|
13
13
|
# * Socks5 proxies
|
|
14
14
|
#
|
|
15
|
-
# https://gitlab.com/
|
|
15
|
+
# https://gitlab.com/os85/httpx/wikis/Proxy
|
|
16
16
|
#
|
|
17
17
|
module Proxy
|
|
18
18
|
Error = HTTPProxyError
|
|
19
19
|
PROXY_ERRORS = [TimeoutError, IOError, SystemCallError, Error].freeze
|
|
20
20
|
|
|
21
|
+
class << self
|
|
22
|
+
def configure(klass)
|
|
23
|
+
klass.plugin(:"proxy/http")
|
|
24
|
+
klass.plugin(:"proxy/socks4")
|
|
25
|
+
klass.plugin(:"proxy/socks5")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def extra_options(options)
|
|
29
|
+
options.merge(supported_proxy_protocols: [])
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
21
33
|
class Parameters
|
|
22
34
|
attr_reader :uri, :username, :password, :scheme
|
|
23
35
|
|
|
@@ -41,7 +53,7 @@ module HTTPX
|
|
|
41
53
|
|
|
42
54
|
auth_scheme = scheme.to_s.capitalize
|
|
43
55
|
|
|
44
|
-
require_relative "
|
|
56
|
+
require_relative "auth/#{scheme}" unless defined?(Authentication) && Authentication.const_defined?(auth_scheme, false)
|
|
45
57
|
|
|
46
58
|
@authenticator = Authentication.const_get(auth_scheme).new(@username, @password, **extra)
|
|
47
59
|
end
|
|
@@ -77,54 +89,61 @@ module HTTPX
|
|
|
77
89
|
end
|
|
78
90
|
end
|
|
79
91
|
|
|
80
|
-
class << self
|
|
81
|
-
def configure(klass)
|
|
82
|
-
klass.plugin(:"proxy/http")
|
|
83
|
-
klass.plugin(:"proxy/socks4")
|
|
84
|
-
klass.plugin(:"proxy/socks5")
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
92
|
module OptionsMethods
|
|
89
93
|
def option_proxy(value)
|
|
90
94
|
value.is_a?(Parameters) ? value : Hash[value]
|
|
91
95
|
end
|
|
96
|
+
|
|
97
|
+
def option_supported_proxy_protocols(value)
|
|
98
|
+
raise TypeError, ":supported_proxy_protocols must be an Array" unless value.is_a?(Array)
|
|
99
|
+
|
|
100
|
+
value.map(&:to_s)
|
|
101
|
+
end
|
|
92
102
|
end
|
|
93
103
|
|
|
94
104
|
module InstanceMethods
|
|
95
105
|
private
|
|
96
106
|
|
|
97
|
-
def
|
|
98
|
-
|
|
99
|
-
uris = options.proxy ? Array(options.proxy[:uri]) : []
|
|
100
|
-
if uris.empty?
|
|
101
|
-
uri = URI(uri).find_proxy
|
|
102
|
-
uris << uri if uri
|
|
103
|
-
end
|
|
104
|
-
uris
|
|
105
|
-
end
|
|
106
|
-
return if @_proxy_uris.empty?
|
|
107
|
+
def find_connection(request, connections, options)
|
|
108
|
+
return super unless options.respond_to?(:proxy)
|
|
107
109
|
|
|
108
|
-
|
|
110
|
+
uri = URI(request.uri)
|
|
109
111
|
|
|
110
|
-
|
|
112
|
+
proxy_opts = if (next_proxy = uri.find_proxy)
|
|
113
|
+
{ uri: next_proxy }
|
|
114
|
+
else
|
|
115
|
+
proxy = options.proxy
|
|
111
116
|
|
|
112
|
-
|
|
113
|
-
proxy_opts = options.proxy.merge(proxy_opts) if options.proxy
|
|
114
|
-
proxy_opts
|
|
115
|
-
end
|
|
117
|
+
return super unless proxy
|
|
116
118
|
|
|
117
|
-
|
|
118
|
-
return super unless options.respond_to?(:proxy)
|
|
119
|
+
return super(request, connections, options.merge(proxy: nil)) unless proxy.key?(:uri)
|
|
119
120
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
@_proxy_uris ||= Array(proxy[:uri])
|
|
122
|
+
|
|
123
|
+
next_proxy = @_proxy_uris.first
|
|
124
|
+
raise Error, "Failed to connect to proxy" unless next_proxy
|
|
125
|
+
|
|
126
|
+
next_proxy = URI(next_proxy)
|
|
127
|
+
|
|
128
|
+
raise Error,
|
|
129
|
+
"#{next_proxy.scheme}: unsupported proxy protocol" unless options.supported_proxy_protocols.include?(next_proxy.scheme)
|
|
123
130
|
|
|
124
|
-
|
|
131
|
+
if proxy.key?(:no_proxy)
|
|
132
|
+
|
|
133
|
+
no_proxy = proxy[:no_proxy]
|
|
134
|
+
no_proxy = no_proxy.join(",") if no_proxy.is_a?(Array)
|
|
135
|
+
|
|
136
|
+
return super(request, connections, options.merge(proxy: nil)) unless URI::Generic.use_proxy?(uri.host, next_proxy.host,
|
|
137
|
+
next_proxy.port, no_proxy)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
proxy.merge(uri: next_proxy)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
proxy = Parameters.new(**proxy_opts)
|
|
125
144
|
|
|
126
145
|
proxy_options = options.merge(proxy: proxy)
|
|
127
|
-
connection = pool.find_connection(uri, proxy_options) ||
|
|
146
|
+
connection = pool.find_connection(uri, proxy_options) || init_connection(uri, proxy_options)
|
|
128
147
|
unless connections.nil? || connections.include?(connection)
|
|
129
148
|
connections << connection
|
|
130
149
|
set_connection_callbacks(connection, connections, options)
|
|
@@ -132,40 +151,24 @@ module HTTPX
|
|
|
132
151
|
connection
|
|
133
152
|
end
|
|
134
153
|
|
|
135
|
-
def build_connection(uri, options)
|
|
136
|
-
proxy = options.proxy
|
|
137
|
-
return super unless proxy
|
|
138
|
-
|
|
139
|
-
connection = options.connection_class.new("tcp", uri, options)
|
|
140
|
-
catch(:coalesced) do
|
|
141
|
-
pool.init_connection(connection, options)
|
|
142
|
-
connection
|
|
143
|
-
end
|
|
144
|
-
end
|
|
145
|
-
|
|
146
154
|
def fetch_response(request, connections, options)
|
|
147
155
|
response = super
|
|
148
156
|
|
|
149
|
-
if response.is_a?(ErrorResponse) &&
|
|
150
|
-
__proxy_error?(response) && !@_proxy_uris.empty?
|
|
157
|
+
if response.is_a?(ErrorResponse) && proxy_error?(request, response)
|
|
151
158
|
@_proxy_uris.shift
|
|
159
|
+
|
|
160
|
+
# return last error response if no more proxies to try
|
|
161
|
+
return response if @_proxy_uris.empty?
|
|
162
|
+
|
|
152
163
|
log { "failed connecting to proxy, trying next..." }
|
|
153
164
|
request.transition(:idle)
|
|
154
|
-
|
|
155
|
-
connections << connection unless connections.include?(connection)
|
|
156
|
-
connection.send(request)
|
|
165
|
+
send_request(request, connections, options)
|
|
157
166
|
return
|
|
158
167
|
end
|
|
159
168
|
response
|
|
160
169
|
end
|
|
161
170
|
|
|
162
|
-
def
|
|
163
|
-
return if options.proxy
|
|
164
|
-
|
|
165
|
-
super
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
def __proxy_error?(response)
|
|
171
|
+
def proxy_error?(_request, response)
|
|
169
172
|
error = response.error
|
|
170
173
|
case error
|
|
171
174
|
when NativeResolveError
|
|
@@ -222,13 +225,6 @@ module HTTPX
|
|
|
222
225
|
end
|
|
223
226
|
end
|
|
224
227
|
|
|
225
|
-
def send(request)
|
|
226
|
-
return super unless @options.proxy
|
|
227
|
-
return super unless connecting?
|
|
228
|
-
|
|
229
|
-
@pending << request
|
|
230
|
-
end
|
|
231
|
-
|
|
232
228
|
def connecting?
|
|
233
229
|
return super unless @options.proxy
|
|
234
230
|
|
|
@@ -250,13 +246,19 @@ module HTTPX
|
|
|
250
246
|
return super unless @options.proxy
|
|
251
247
|
|
|
252
248
|
@state = :open
|
|
253
|
-
|
|
254
|
-
|
|
249
|
+
|
|
250
|
+
super
|
|
255
251
|
emit(:close)
|
|
256
252
|
end
|
|
257
253
|
|
|
258
254
|
private
|
|
259
255
|
|
|
256
|
+
def initialize_type(uri, options)
|
|
257
|
+
return super unless options.proxy
|
|
258
|
+
|
|
259
|
+
"tcp"
|
|
260
|
+
end
|
|
261
|
+
|
|
260
262
|
def connect
|
|
261
263
|
return super unless @options.proxy
|
|
262
264
|
|
|
@@ -284,7 +286,7 @@ module HTTPX
|
|
|
284
286
|
register_plugin :proxy, Proxy
|
|
285
287
|
end
|
|
286
288
|
|
|
287
|
-
class ProxySSL <
|
|
289
|
+
class ProxySSL < SSL
|
|
288
290
|
def initialize(tcp, request_uri, options)
|
|
289
291
|
@io = tcp.to_io
|
|
290
292
|
super(request_uri, tcp.addresses, options)
|
|
@@ -8,7 +8,7 @@ module HTTPX
|
|
|
8
8
|
# In order to benefit from this, requests are sent one at a time, so that
|
|
9
9
|
# no push responses are received after corresponding request has been sent.
|
|
10
10
|
#
|
|
11
|
-
# https://gitlab.com/
|
|
11
|
+
# https://gitlab.com/os85/httpx/wikis/Server-Push
|
|
12
12
|
#
|
|
13
13
|
module PushPromise
|
|
14
14
|
def self.extra_options(options)
|
|
@@ -9,7 +9,7 @@ module HTTPX
|
|
|
9
9
|
# * when the server is unavailable (503);
|
|
10
10
|
# * when a 3xx request comes with a "retry-after" value
|
|
11
11
|
#
|
|
12
|
-
# https://gitlab.com/
|
|
12
|
+
# https://gitlab.com/os85/httpx/wikis/Rate-Limiter
|
|
13
13
|
#
|
|
14
14
|
module RateLimiter
|
|
15
15
|
class << self
|
|
@@ -23,6 +23,8 @@ module HTTPX
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def retry_on_rate_limited_response(response)
|
|
26
|
+
return false unless response.is_a?(Response)
|
|
27
|
+
|
|
26
28
|
status = response.status
|
|
27
29
|
|
|
28
30
|
RATE_LIMIT_CODES.include?(status)
|