httpx 0.19.6 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/doc/release_notes/0_19_7.md +5 -0
- data/doc/release_notes/0_19_8.md +5 -0
- data/doc/release_notes/0_20_0.md +36 -0
- data/lib/httpx/adapters/datadog.rb +112 -58
- data/lib/httpx/adapters/sentry.rb +102 -0
- data/lib/httpx/connection.rb +10 -2
- data/lib/httpx/io/ssl.rb +8 -3
- data/lib/httpx/options.rb +5 -0
- data/lib/httpx/plugins/authentication/basic.rb +24 -0
- data/lib/httpx/plugins/authentication/digest.rb +102 -0
- data/lib/httpx/plugins/authentication/ntlm.rb +37 -0
- data/lib/httpx/plugins/authentication/socks5.rb +24 -0
- data/lib/httpx/plugins/basic_authentication.rb +6 -6
- data/lib/httpx/plugins/digest_authentication.rb +15 -111
- data/lib/httpx/plugins/follow_redirects.rb +17 -5
- data/lib/httpx/plugins/ntlm_authentication.rb +8 -18
- data/lib/httpx/plugins/proxy/http.rb +76 -13
- data/lib/httpx/plugins/proxy/socks5.rb +6 -4
- data/lib/httpx/plugins/proxy.rb +35 -9
- data/lib/httpx/plugins/response_cache/store.rb +1 -0
- data/lib/httpx/request.rb +3 -1
- data/lib/httpx/session.rb +7 -2
- data/lib/httpx/version.rb +1 -1
- data/sig/chainable.rbs +4 -4
- data/sig/plugins/authentication/basic.rbs +19 -0
- data/sig/plugins/authentication/digest.rbs +24 -0
- data/sig/plugins/authentication/ntlm.rbs +20 -0
- data/sig/plugins/authentication/socks5.rbs +18 -0
- data/sig/plugins/basic_authentication.rbs +2 -2
- data/sig/plugins/digest_authentication.rbs +3 -13
- data/sig/plugins/ntlm_authentication.rbs +3 -8
- data/sig/plugins/proxy/http.rbs +13 -3
- data/sig/plugins/proxy.rbs +5 -3
- data/sig/session.rbs +1 -1
- metadata +17 -2
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
|
5
|
+
module HTTPX
|
6
|
+
module Plugins
|
7
|
+
module Authentication
|
8
|
+
class Socks5
|
9
|
+
def initialize(user, password, **)
|
10
|
+
@user = user
|
11
|
+
@password = password
|
12
|
+
end
|
13
|
+
|
14
|
+
def can_authenticate?(*)
|
15
|
+
@user && @password
|
16
|
+
end
|
17
|
+
|
18
|
+
def authenticate(*)
|
19
|
+
[0x01, @user.bytesize, @user, @password.bytesize, @password].pack("CCA*CA*")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -7,10 +7,10 @@ module HTTPX
|
|
7
7
|
#
|
8
8
|
# https://gitlab.com/honeyryderchuck/httpx/wikis/Authentication#basic-authentication
|
9
9
|
#
|
10
|
-
module
|
10
|
+
module BasicAuth
|
11
11
|
class << self
|
12
12
|
def load_dependencies(_klass)
|
13
|
-
|
13
|
+
require_relative "authentication/basic"
|
14
14
|
end
|
15
15
|
|
16
16
|
def configure(klass)
|
@@ -19,12 +19,12 @@ module HTTPX
|
|
19
19
|
end
|
20
20
|
|
21
21
|
module InstanceMethods
|
22
|
-
def
|
23
|
-
authentication(
|
22
|
+
def basic_auth(user, password)
|
23
|
+
authentication(Authentication::Basic.new(user, password).authenticate)
|
24
24
|
end
|
25
|
-
alias_method :
|
25
|
+
alias_method :basic_authentication, :basic_auth
|
26
26
|
end
|
27
27
|
end
|
28
|
-
register_plugin :basic_authentication,
|
28
|
+
register_plugin :basic_authentication, BasicAuth
|
29
29
|
end
|
30
30
|
end
|
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "digest"
|
4
|
-
|
5
3
|
module HTTPX
|
6
4
|
module Plugins
|
7
5
|
#
|
@@ -9,9 +7,7 @@ module HTTPX
|
|
9
7
|
#
|
10
8
|
# https://gitlab.com/honeyryderchuck/httpx/wikis/Authentication#authentication
|
11
9
|
#
|
12
|
-
module
|
13
|
-
using RegexpExtensions unless Regexp.method_defined?(:match?)
|
14
|
-
|
10
|
+
module DigestAuth
|
15
11
|
DigestError = Class.new(Error)
|
16
12
|
|
17
13
|
class << self
|
@@ -20,14 +16,13 @@ module HTTPX
|
|
20
16
|
end
|
21
17
|
|
22
18
|
def load_dependencies(*)
|
23
|
-
|
24
|
-
require "digest"
|
19
|
+
require_relative "authentication/digest"
|
25
20
|
end
|
26
21
|
end
|
27
22
|
|
28
23
|
module OptionsMethods
|
29
24
|
def option_digest(value)
|
30
|
-
raise TypeError, ":digest must be a Digest" unless value.is_a?(Digest)
|
25
|
+
raise TypeError, ":digest must be a Digest" unless value.is_a?(Authentication::Digest)
|
31
26
|
|
32
27
|
value
|
33
28
|
end
|
@@ -35,7 +30,7 @@ module HTTPX
|
|
35
30
|
|
36
31
|
module InstanceMethods
|
37
32
|
def digest_authentication(user, password)
|
38
|
-
with(digest: Digest.new(user, password))
|
33
|
+
with(digest: Authentication::Digest.new(user, password))
|
39
34
|
end
|
40
35
|
|
41
36
|
alias_method :digest_auth, :digest_authentication
|
@@ -44,116 +39,25 @@ module HTTPX
|
|
44
39
|
requests.flat_map do |request|
|
45
40
|
digest = request.options.digest
|
46
41
|
|
47
|
-
|
48
|
-
probe_response = wrap { super(request).first }
|
49
|
-
|
50
|
-
if digest && !probe_response.is_a?(ErrorResponse) &&
|
51
|
-
probe_response.status == 401 && probe_response.headers.key?("www-authenticate") &&
|
52
|
-
/Digest .*/.match?(probe_response.headers["www-authenticate"])
|
53
|
-
|
54
|
-
request.transition(:idle)
|
55
|
-
|
56
|
-
token = digest.generate_header(request, probe_response)
|
57
|
-
request.headers["authorization"] = "Digest #{token}"
|
58
|
-
|
59
|
-
super(request)
|
60
|
-
else
|
61
|
-
probe_response
|
62
|
-
end
|
63
|
-
else
|
42
|
+
unless digest
|
64
43
|
super(request)
|
44
|
+
next
|
65
45
|
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
class Digest
|
71
|
-
def initialize(user, password)
|
72
|
-
@user = user
|
73
|
-
@password = password
|
74
|
-
@nonce = 0
|
75
|
-
end
|
76
|
-
|
77
|
-
def generate_header(request, response, _iis = false)
|
78
|
-
meth = request.verb.to_s.upcase
|
79
|
-
www = response.headers["www-authenticate"]
|
80
|
-
|
81
|
-
# discard first token, it's Digest
|
82
|
-
auth_info = www[/^(\w+) (.*)/, 2]
|
83
|
-
|
84
|
-
uri = request.path
|
85
|
-
|
86
|
-
params = Hash[auth_info.split(/ *, */)
|
87
|
-
.map { |val| val.split("=") }
|
88
|
-
.map { |k, v| [k, v.delete("\"")] }]
|
89
|
-
nonce = params["nonce"]
|
90
|
-
nc = next_nonce
|
91
46
|
|
92
|
-
|
93
|
-
qop = params["qop"]
|
47
|
+
probe_response = wrap { super(request).first }
|
94
48
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
else
|
103
|
-
algorithm = ::Digest::MD5
|
104
|
-
end
|
105
|
-
|
106
|
-
if qop || sess
|
107
|
-
cnonce = make_cnonce
|
108
|
-
nc = format("%<nonce>08x", nonce: nc)
|
109
|
-
end
|
110
|
-
|
111
|
-
a1 = if sess
|
112
|
-
[algorithm.hexdigest("#{@user}:#{params["realm"]}:#{@password}"),
|
113
|
-
nonce,
|
114
|
-
cnonce].join ":"
|
115
|
-
else
|
116
|
-
"#{@user}:#{params["realm"]}:#{@password}"
|
49
|
+
if probe_response.status == 401 && digest.can_authenticate?(probe_response.headers["www-authenticate"])
|
50
|
+
request.transition(:idle)
|
51
|
+
request.headers["authorization"] = digest.authenticate(request, probe_response.headers["www-authenticate"])
|
52
|
+
super(request)
|
53
|
+
else
|
54
|
+
probe_response
|
55
|
+
end
|
117
56
|
end
|
118
|
-
|
119
|
-
ha1 = algorithm.hexdigest(a1)
|
120
|
-
ha2 = algorithm.hexdigest("#{meth}:#{uri}")
|
121
|
-
request_digest = [ha1, nonce]
|
122
|
-
request_digest.push(nc, cnonce, qop) if qop
|
123
|
-
request_digest << ha2
|
124
|
-
request_digest = request_digest.join(":")
|
125
|
-
|
126
|
-
header = [
|
127
|
-
%(username="#{@user}"),
|
128
|
-
%(nonce="#{nonce}"),
|
129
|
-
%(uri="#{uri}"),
|
130
|
-
%(response="#{algorithm.hexdigest(request_digest)}"),
|
131
|
-
]
|
132
|
-
header << %(realm="#{params["realm"]}") if params.key?("realm")
|
133
|
-
header << %(algorithm=#{params["algorithm"]}") if params.key?("algorithm")
|
134
|
-
header << %(opaque="#{params["opaque"]}") if params.key?("opaque")
|
135
|
-
header << %(cnonce="#{cnonce}") if cnonce
|
136
|
-
header << %(nc=#{nc})
|
137
|
-
header << %(qop=#{qop}) if qop
|
138
|
-
header.join ", "
|
139
|
-
end
|
140
|
-
|
141
|
-
private
|
142
|
-
|
143
|
-
def make_cnonce
|
144
|
-
::Digest::MD5.hexdigest [
|
145
|
-
Time.now.to_i,
|
146
|
-
Process.pid,
|
147
|
-
SecureRandom.random_number(2**32),
|
148
|
-
].join ":"
|
149
|
-
end
|
150
|
-
|
151
|
-
def next_nonce
|
152
|
-
@nonce += 1
|
153
57
|
end
|
154
58
|
end
|
155
59
|
end
|
156
60
|
|
157
|
-
register_plugin :digest_authentication,
|
61
|
+
register_plugin :digest_authentication, DigestAuth
|
158
62
|
end
|
159
63
|
end
|
@@ -44,7 +44,7 @@ module HTTPX
|
|
44
44
|
|
45
45
|
max_redirects = redirect_request.max_redirects
|
46
46
|
|
47
|
-
return response unless REDIRECT_STATUS.include?(response.status)
|
47
|
+
return response unless REDIRECT_STATUS.include?(response.status) && response.headers.key?("location")
|
48
48
|
return response unless max_redirects.positive?
|
49
49
|
|
50
50
|
retry_request = build_redirect_request(redirect_request, response, options)
|
@@ -86,10 +86,22 @@ module HTTPX
|
|
86
86
|
redirect_uri = __get_location_from_response(response)
|
87
87
|
max_redirects = request.max_redirects
|
88
88
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
89
|
+
if response.status == 305 && options.respond_to?(:proxy)
|
90
|
+
# The requested resource MUST be accessed through the proxy given by
|
91
|
+
# the Location field. The Location field gives the URI of the proxy.
|
92
|
+
retry_options = options.merge(headers: request.headers,
|
93
|
+
proxy: { uri: redirect_uri },
|
94
|
+
body: request.body,
|
95
|
+
max_redirects: max_redirects - 1)
|
96
|
+
redirect_uri = request.url
|
97
|
+
else
|
98
|
+
|
99
|
+
# redirects are **ALWAYS** GET
|
100
|
+
retry_options = options.merge(headers: request.headers,
|
101
|
+
body: request.body,
|
102
|
+
max_redirects: max_redirects - 1)
|
103
|
+
end
|
104
|
+
|
93
105
|
build_request(:get, redirect_uri, retry_options)
|
94
106
|
end
|
95
107
|
|
@@ -5,13 +5,10 @@ module HTTPX
|
|
5
5
|
#
|
6
6
|
# https://gitlab.com/honeyryderchuck/httpx/wikis/Authentication#ntlm-authentication
|
7
7
|
#
|
8
|
-
module
|
9
|
-
NTLMParams = Struct.new(:user, :domain, :password)
|
10
|
-
|
8
|
+
module NTLMAuth
|
11
9
|
class << self
|
12
10
|
def load_dependencies(_klass)
|
13
|
-
|
14
|
-
require "ntlm"
|
11
|
+
require_relative "authentication/ntlm"
|
15
12
|
end
|
16
13
|
|
17
14
|
def extra_options(options)
|
@@ -21,7 +18,7 @@ module HTTPX
|
|
21
18
|
|
22
19
|
module OptionsMethods
|
23
20
|
def option_ntlm(value)
|
24
|
-
raise TypeError, ":ntlm must be a #{
|
21
|
+
raise TypeError, ":ntlm must be a #{Authentication::Ntlm}" unless value.is_a?(Authentication::Ntlm)
|
25
22
|
|
26
23
|
value
|
27
24
|
end
|
@@ -29,7 +26,7 @@ module HTTPX
|
|
29
26
|
|
30
27
|
module InstanceMethods
|
31
28
|
def ntlm_authentication(user, password, domain = nil)
|
32
|
-
with(ntlm:
|
29
|
+
with(ntlm: Authentication::Ntlm.new(user, password, domain: domain))
|
33
30
|
end
|
34
31
|
|
35
32
|
alias_method :ntlm_auth, :ntlm_authentication
|
@@ -39,19 +36,12 @@ module HTTPX
|
|
39
36
|
ntlm = request.options.ntlm
|
40
37
|
|
41
38
|
if ntlm
|
42
|
-
request.headers["authorization"] =
|
39
|
+
request.headers["authorization"] = ntlm.negotiate
|
43
40
|
probe_response = wrap { super(request).first }
|
44
41
|
|
45
|
-
if
|
46
|
-
probe_response.headers.key?("www-authenticate") &&
|
47
|
-
(challenge = probe_response.headers["www-authenticate"][/NTLM (.*)/, 1])
|
48
|
-
|
49
|
-
challenge = Base64.decode64(challenge)
|
50
|
-
ntlm_challenge = NTLM.authenticate(challenge, ntlm.user, ntlm.domain, ntlm.password).to_base64
|
51
|
-
|
42
|
+
if probe_response.status == 401 && ntlm.can_authenticate?(probe_response.headers["www-authenticate"])
|
52
43
|
request.transition(:idle)
|
53
|
-
|
54
|
-
request.headers["authorization"] = "NTLM #{ntlm_challenge}"
|
44
|
+
request.headers["authorization"] = ntlm.authenticate(request, probe_response.headers["www-authenticate"])
|
55
45
|
super(request)
|
56
46
|
else
|
57
47
|
probe_response
|
@@ -63,6 +53,6 @@ module HTTPX
|
|
63
53
|
end
|
64
54
|
end
|
65
55
|
end
|
66
|
-
register_plugin :ntlm_authentication,
|
56
|
+
register_plugin :ntlm_authentication, NTLMAuth
|
67
57
|
end
|
68
58
|
end
|
@@ -6,6 +6,42 @@ module HTTPX
|
|
6
6
|
module Plugins
|
7
7
|
module Proxy
|
8
8
|
module HTTP
|
9
|
+
module InstanceMethods
|
10
|
+
def with_proxy_basic_auth(opts)
|
11
|
+
with(proxy: opts.merge(scheme: "basic"))
|
12
|
+
end
|
13
|
+
|
14
|
+
def with_proxy_digest_auth(opts)
|
15
|
+
with(proxy: opts.merge(scheme: "digest"))
|
16
|
+
end
|
17
|
+
|
18
|
+
def with_proxy_ntlm_auth(opts)
|
19
|
+
with(proxy: opts.merge(scheme: "ntlm"))
|
20
|
+
end
|
21
|
+
|
22
|
+
def fetch_response(request, connections, options)
|
23
|
+
response = super
|
24
|
+
|
25
|
+
if response &&
|
26
|
+
response.status == 407 &&
|
27
|
+
!request.headers.key?("proxy-authorization") &&
|
28
|
+
response.headers.key?("proxy-authenticate")
|
29
|
+
|
30
|
+
connection = find_connection(request, connections, options)
|
31
|
+
|
32
|
+
if connection.options.proxy.can_authenticate?(response.headers["proxy-authenticate"])
|
33
|
+
request.transition(:idle)
|
34
|
+
request.headers["proxy-authorization"] =
|
35
|
+
connection.options.proxy.authenticate(request, response.headers["proxy-authenticate"])
|
36
|
+
connection.send(request)
|
37
|
+
return
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
response
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
9
45
|
module ConnectionMethods
|
10
46
|
def connecting?
|
11
47
|
super || @state == :connecting || @state == :connected
|
@@ -23,11 +59,27 @@ module HTTPX
|
|
23
59
|
@io.connect
|
24
60
|
return unless @io.connected?
|
25
61
|
|
26
|
-
@parser
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
62
|
+
@parser || begin
|
63
|
+
@parser = registry(@io.protocol).new(@write_buffer, @options.merge(max_concurrent_requests: 1))
|
64
|
+
parser = @parser
|
65
|
+
parser.extend(ProxyParser)
|
66
|
+
parser.on(:response, &method(:__http_on_connect))
|
67
|
+
parser.on(:close) { transition(:closing) }
|
68
|
+
parser.on(:reset) do
|
69
|
+
if parser.empty?
|
70
|
+
reset
|
71
|
+
else
|
72
|
+
transition(:closing)
|
73
|
+
transition(:closed)
|
74
|
+
emit(:reset)
|
75
|
+
|
76
|
+
parser.reset if @parser
|
77
|
+
transition(:idle)
|
78
|
+
transition(:connecting)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
__http_proxy_connect(parser)
|
82
|
+
end
|
31
83
|
return if @state == :connected
|
32
84
|
when :connected
|
33
85
|
return unless @state == :idle || @state == :connecting
|
@@ -44,13 +96,13 @@ module HTTPX
|
|
44
96
|
super
|
45
97
|
end
|
46
98
|
|
47
|
-
def __http_proxy_connect
|
99
|
+
def __http_proxy_connect(parser)
|
48
100
|
req = @pending.first
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
101
|
+
if req && req.uri.scheme == "https"
|
102
|
+
# if the first request after CONNECT is to an https address, it is assumed that
|
103
|
+
# all requests in the queue are not only ALL HTTPS, but they also share the certificate,
|
104
|
+
# and therefore, will share the connection.
|
105
|
+
#
|
54
106
|
connect_request = ConnectRequest.new(req.uri, @options)
|
55
107
|
@inflight += 1
|
56
108
|
parser.send(connect_request)
|
@@ -59,7 +111,7 @@ module HTTPX
|
|
59
111
|
end
|
60
112
|
end
|
61
113
|
|
62
|
-
def __http_on_connect(
|
114
|
+
def __http_on_connect(request, response)
|
63
115
|
@inflight -= 1
|
64
116
|
if response.status == 200
|
65
117
|
req = @pending.first
|
@@ -67,6 +119,14 @@ module HTTPX
|
|
67
119
|
@io = ProxySSL.new(@io, request_uri, @options)
|
68
120
|
transition(:connected)
|
69
121
|
throw(:called)
|
122
|
+
elsif response.status == 407 &&
|
123
|
+
!request.headers.key?("proxy-authorization") &&
|
124
|
+
@options.proxy.can_authenticate?(response.headers["proxy-authenticate"])
|
125
|
+
|
126
|
+
request.transition(:idle)
|
127
|
+
request.headers["proxy-authorization"] = @options.proxy.authenticate(request, response.headers["proxy-authenticate"])
|
128
|
+
@parser.send(request)
|
129
|
+
@inflight += 1
|
70
130
|
else
|
71
131
|
pending = @pending + @parser.pending
|
72
132
|
while (req = pending.shift)
|
@@ -88,7 +148,10 @@ module HTTPX
|
|
88
148
|
extra_headers = super
|
89
149
|
|
90
150
|
proxy_params = @options.proxy
|
91
|
-
|
151
|
+
if proxy_params.scheme == "basic"
|
152
|
+
# opt for basic auth
|
153
|
+
extra_headers["proxy-authorization"] = proxy_params.authenticate(extra_headers)
|
154
|
+
end
|
92
155
|
extra_headers["proxy-connection"] = extra_headers.delete("connection") if extra_headers.key?("connection")
|
93
156
|
extra_headers
|
94
157
|
end
|
@@ -18,6 +18,10 @@ module HTTPX
|
|
18
18
|
|
19
19
|
Error = Socks5Error
|
20
20
|
|
21
|
+
def self.load_dependencies(*)
|
22
|
+
require_relative "../authentication/socks5"
|
23
|
+
end
|
24
|
+
|
21
25
|
module ConnectionMethods
|
22
26
|
def call
|
23
27
|
super
|
@@ -156,16 +160,14 @@ module HTTPX
|
|
156
160
|
|
157
161
|
def negotiate(parameters)
|
158
162
|
methods = [NOAUTH]
|
159
|
-
methods << PASSWD if parameters.
|
163
|
+
methods << PASSWD if parameters.can_authenticate?
|
160
164
|
methods.unshift(methods.size)
|
161
165
|
methods.unshift(VERSION)
|
162
166
|
methods.pack("C*")
|
163
167
|
end
|
164
168
|
|
165
169
|
def authenticate(parameters)
|
166
|
-
|
167
|
-
password = parameters.password
|
168
|
-
[0x01, user.bytesize, user, password.bytesize, password].pack("CCA*CA*")
|
170
|
+
parameters.authenticate
|
169
171
|
end
|
170
172
|
|
171
173
|
def connect(uri)
|
data/lib/httpx/plugins/proxy.rb
CHANGED
@@ -19,22 +19,43 @@ module HTTPX
|
|
19
19
|
PROXY_ERRORS = [TimeoutError, IOError, SystemCallError, Error].freeze
|
20
20
|
|
21
21
|
class Parameters
|
22
|
-
attr_reader :uri, :username, :password
|
22
|
+
attr_reader :uri, :username, :password, :scheme
|
23
23
|
|
24
|
-
def initialize(uri:, username: nil, password: nil)
|
24
|
+
def initialize(uri:, scheme: nil, username: nil, password: nil, **extra)
|
25
25
|
@uri = uri.is_a?(URI::Generic) ? uri : URI(uri)
|
26
26
|
@username = username || @uri.user
|
27
27
|
@password = password || @uri.password
|
28
|
+
|
29
|
+
return unless @username && @password
|
30
|
+
|
31
|
+
scheme ||= case @uri.scheme
|
32
|
+
when "socks5"
|
33
|
+
@uri.scheme
|
34
|
+
when "http", "https"
|
35
|
+
"basic"
|
36
|
+
else
|
37
|
+
return
|
38
|
+
end
|
39
|
+
|
40
|
+
@scheme = scheme
|
41
|
+
|
42
|
+
auth_scheme = scheme.to_s.capitalize
|
43
|
+
|
44
|
+
require_relative "authentication/#{scheme}" unless defined?(Authentication) && Authentication.const_defined?(auth_scheme, false)
|
45
|
+
|
46
|
+
@authenticator = Authentication.const_get(auth_scheme).new(@username, @password, **extra)
|
28
47
|
end
|
29
48
|
|
30
|
-
def
|
31
|
-
|
49
|
+
def can_authenticate?(*args)
|
50
|
+
return false unless @authenticator
|
51
|
+
|
52
|
+
@authenticator.can_authenticate?(*args)
|
32
53
|
end
|
33
54
|
|
34
|
-
def
|
35
|
-
return unless
|
55
|
+
def authenticate(*args)
|
56
|
+
return unless @authenticator
|
36
57
|
|
37
|
-
|
58
|
+
@authenticator.authenticate(*args)
|
38
59
|
end
|
39
60
|
|
40
61
|
def ==(other)
|
@@ -42,7 +63,8 @@ module HTTPX
|
|
42
63
|
when Parameters
|
43
64
|
@uri == other.uri &&
|
44
65
|
@username == other.username &&
|
45
|
-
@password == other.password
|
66
|
+
@password == other.password &&
|
67
|
+
@scheme == other.scheme
|
46
68
|
when URI::Generic, String
|
47
69
|
proxy_uri = @uri.dup
|
48
70
|
proxy_uri.user = @username
|
@@ -81,7 +103,11 @@ module HTTPX
|
|
81
103
|
end
|
82
104
|
uris
|
83
105
|
end
|
84
|
-
|
106
|
+
return if @_proxy_uris.empty?
|
107
|
+
|
108
|
+
proxy_opts = { uri: @_proxy_uris.first }
|
109
|
+
proxy_opts = options.proxy.merge(proxy_opts) if options.proxy
|
110
|
+
proxy_opts
|
85
111
|
end
|
86
112
|
|
87
113
|
def find_connection(request, connections, options)
|
@@ -37,6 +37,7 @@ module HTTPX::Plugins
|
|
37
37
|
return unless request.headers.same_headers?(original_request.headers)
|
38
38
|
else
|
39
39
|
return unless vary.split(/ *, */).all? do |cache_field|
|
40
|
+
cache_field.downcase!
|
40
41
|
!original_request.headers.key?(cache_field) || request.headers[cache_field] == original_request.headers[cache_field]
|
41
42
|
end
|
42
43
|
end
|
data/lib/httpx/request.rb
CHANGED
@@ -49,7 +49,9 @@ module HTTPX
|
|
49
49
|
origin = @options.origin
|
50
50
|
raise(Error, "invalid URI: #{@uri}") unless origin
|
51
51
|
|
52
|
-
|
52
|
+
base_path = @options.base_path
|
53
|
+
|
54
|
+
@uri = origin.merge("#{base_path}#{@uri}")
|
53
55
|
end
|
54
56
|
|
55
57
|
raise(Error, "unknown method: #{verb}") unless METHODS.include?(@verb)
|
data/lib/httpx/session.rb
CHANGED
@@ -106,16 +106,21 @@ module HTTPX
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def build_altsvc_connection(existing_connection, connections, alt_origin, origin, alt_params, options)
|
109
|
+
# do not allow security downgrades on altsvc negotiation
|
110
|
+
return if existing_connection.origin.scheme == "https" && alt_origin.scheme != "https"
|
111
|
+
|
109
112
|
altsvc = AltSvc.cached_altsvc_set(origin, alt_params.merge("origin" => alt_origin))
|
110
113
|
|
111
114
|
# altsvc already exists, somehow it wasn't advertised, probably noop
|
112
115
|
return unless altsvc
|
113
116
|
|
114
|
-
|
117
|
+
alt_options = options.merge(ssl: options.ssl.merge(hostname: URI(origin).host))
|
118
|
+
|
119
|
+
connection = pool.find_connection(alt_origin, alt_options) || build_connection(alt_origin, alt_options)
|
115
120
|
# advertised altsvc is the same origin being used, ignore
|
116
121
|
return if connection == existing_connection
|
117
122
|
|
118
|
-
set_connection_callbacks(connection, connections,
|
123
|
+
set_connection_callbacks(connection, connections, alt_options)
|
119
124
|
|
120
125
|
log(level: 1) { "#{origin} alt-svc: #{alt_origin}" }
|
121
126
|
|
data/lib/httpx/version.rb
CHANGED
data/sig/chainable.rbs
CHANGED
@@ -13,9 +13,9 @@ module HTTPX
|
|
13
13
|
| (options) { (Session) -> void } -> void
|
14
14
|
|
15
15
|
def plugin: (:authentication, ?options) -> Plugins::sessionAuthentication
|
16
|
-
| (:basic_authentication, ?options) -> Plugins::
|
17
|
-
| (:digest_authentication, ?options) -> Plugins::
|
18
|
-
| (:ntlm_authentication, ?options) -> Plugins::
|
16
|
+
| (:basic_authentication, ?options) -> Plugins::sessionBasicAuth
|
17
|
+
| (:digest_authentication, ?options) -> Plugins::sessionDigestAuth
|
18
|
+
| (:ntlm_authentication, ?options) -> Plugins::sessionNTLMAuth
|
19
19
|
| (:aws_sdk_authentication, ?options) -> Plugins::sessionAwsSdkAuthentication
|
20
20
|
| (:compression, ?options) -> Session
|
21
21
|
| (:cookies, ?options) -> Plugins::sessionCookies
|
@@ -25,7 +25,7 @@ module HTTPX
|
|
25
25
|
| (:h2c, ?options) -> Session
|
26
26
|
| (:multipart, ?options) -> Session
|
27
27
|
| (:persistent, ?options) -> Plugins::sessionPersistent
|
28
|
-
| (:proxy, ?options) -> Plugins::sessionProxy
|
28
|
+
| (:proxy, ?options) -> (Plugins::sessionProxy & Plugins::httpProxy)
|
29
29
|
| (:push_promise, ?options) -> Plugins::sessionPushPromise
|
30
30
|
| (:retries, ?options) -> Plugins::sessionRetries
|
31
31
|
| (:rate_limiter, ?options) -> Session
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module HTTPX
|
2
|
+
module Plugins
|
3
|
+
module Authentication
|
4
|
+
class Basic
|
5
|
+
@user: String
|
6
|
+
@password: String
|
7
|
+
|
8
|
+
def can_authenticate?: (String? authenticate) -> boolish
|
9
|
+
|
10
|
+
def authenticate: (*untyped) -> String
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def initialize: (string user, string password, *untyped) -> void
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|