httpx 1.2.6 → 1.4.4
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/README.md +1 -2
- 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/lib/httpx/adapters/datadog.rb +56 -80
- data/lib/httpx/adapters/faraday.rb +5 -2
- data/lib/httpx/adapters/webmock.rb +24 -8
- data/lib/httpx/callbacks.rb +2 -7
- data/lib/httpx/chainable.rb +3 -1
- data/lib/httpx/connection/http1.rb +11 -7
- data/lib/httpx/connection/http2.rb +57 -34
- data/lib/httpx/connection.rb +270 -71
- data/lib/httpx/errors.rb +15 -4
- data/lib/httpx/io/ssl.rb +6 -3
- data/lib/httpx/io/tcp.rb +1 -1
- data/lib/httpx/io/unix.rb +1 -1
- data/lib/httpx/loggable.rb +17 -10
- data/lib/httpx/options.rb +30 -23
- data/lib/httpx/plugins/aws_sdk_authentication.rb +3 -0
- data/lib/httpx/plugins/aws_sigv4.rb +36 -17
- data/lib/httpx/plugins/callbacks.rb +13 -2
- data/lib/httpx/plugins/circuit_breaker.rb +11 -5
- data/lib/httpx/plugins/content_digest.rb +202 -0
- data/lib/httpx/plugins/cookies.rb +9 -6
- data/lib/httpx/plugins/digest_auth.rb +3 -0
- data/lib/httpx/plugins/expect.rb +10 -4
- data/lib/httpx/plugins/follow_redirects.rb +68 -33
- data/lib/httpx/plugins/grpc/grpc_encoding.rb +2 -0
- data/lib/httpx/plugins/grpc.rb +2 -2
- data/lib/httpx/plugins/h2c.rb +23 -20
- data/lib/httpx/plugins/internal_telemetry.rb +48 -1
- data/lib/httpx/plugins/oauth.rb +1 -1
- data/lib/httpx/plugins/persistent.rb +16 -0
- data/lib/httpx/plugins/proxy/http.rb +19 -16
- data/lib/httpx/plugins/proxy/socks4.rb +1 -1
- data/lib/httpx/plugins/proxy/socks5.rb +1 -1
- data/lib/httpx/plugins/proxy.rb +96 -85
- data/lib/httpx/plugins/retries.rb +28 -10
- data/lib/httpx/plugins/ssrf_filter.rb +4 -1
- data/lib/httpx/plugins/stream.rb +42 -18
- data/lib/httpx/plugins/upgrade.rb +5 -10
- data/lib/httpx/plugins/webdav.rb +6 -0
- data/lib/httpx/plugins/xml.rb +76 -0
- data/lib/httpx/pool.rb +73 -244
- data/lib/httpx/request/body.rb +50 -55
- data/lib/httpx/request.rb +77 -14
- data/lib/httpx/resolver/https.rb +17 -20
- data/lib/httpx/resolver/multi.rb +34 -16
- data/lib/httpx/resolver/native.rb +140 -61
- data/lib/httpx/resolver/resolver.rb +64 -19
- data/lib/httpx/resolver/system.rb +32 -16
- data/lib/httpx/resolver.rb +21 -14
- data/lib/httpx/response/body.rb +12 -1
- data/lib/httpx/response.rb +16 -9
- data/lib/httpx/selector.rb +170 -91
- data/lib/httpx/session.rb +282 -139
- data/lib/httpx/timers.rb +17 -2
- data/lib/httpx/transcoder/body.rb +15 -29
- data/lib/httpx/transcoder/form.rb +2 -0
- data/lib/httpx/transcoder/gzip.rb +0 -3
- data/lib/httpx/transcoder/json.rb +16 -2
- data/lib/httpx/transcoder/multipart/encoder.rb +11 -2
- data/lib/httpx/transcoder/multipart/part.rb +1 -1
- data/lib/httpx/transcoder/utils/deflater.rb +7 -4
- data/lib/httpx/transcoder.rb +0 -1
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +20 -21
- data/sig/callbacks.rbs +2 -3
- data/sig/chainable.rbs +6 -2
- data/sig/connection/http1.rbs +2 -2
- data/sig/connection/http2.rbs +22 -18
- data/sig/connection.rbs +40 -9
- data/sig/errors.rbs +9 -3
- data/sig/httpx.rbs +3 -3
- data/sig/io/tcp.rbs +1 -1
- data/sig/io/unix.rbs +1 -1
- data/sig/loggable.rbs +4 -2
- data/sig/options.rbs +8 -13
- data/sig/plugins/aws_sigv4.rbs +8 -2
- data/sig/plugins/content_digest.rbs +51 -0
- data/sig/plugins/cookies/cookie.rbs +9 -0
- data/sig/plugins/follow_redirects.rbs +1 -1
- data/sig/plugins/grpc/call.rbs +4 -0
- data/sig/plugins/persistent.rbs +4 -1
- data/sig/plugins/proxy/http.rbs +3 -0
- data/sig/plugins/proxy/socks5.rbs +11 -3
- data/sig/plugins/proxy.rbs +18 -9
- data/sig/plugins/push_promise.rbs +6 -3
- data/sig/plugins/rate_limiter.rbs +2 -0
- data/sig/plugins/retries.rbs +1 -1
- data/sig/plugins/ssrf_filter.rbs +26 -0
- data/sig/plugins/stream.rbs +3 -0
- data/sig/plugins/webdav.rbs +23 -0
- data/sig/plugins/xml.rbs +37 -0
- data/sig/pool.rbs +27 -33
- data/sig/request/body.rbs +4 -10
- data/sig/request.rbs +14 -1
- data/sig/resolver/multi.rbs +26 -1
- data/sig/resolver/native.rbs +6 -3
- data/sig/resolver/resolver.rbs +22 -3
- data/sig/resolver.rbs +5 -1
- data/sig/response/body.rbs +2 -2
- data/sig/response/buffer.rbs +2 -2
- data/sig/response.rbs +9 -4
- data/sig/selector.rbs +31 -4
- data/sig/session.rbs +54 -20
- data/sig/timers.rbs +15 -4
- data/sig/transcoder/body.rbs +2 -4
- data/sig/transcoder/chunker.rbs +1 -1
- data/sig/transcoder/deflate.rbs +1 -0
- data/sig/transcoder/form.rbs +8 -0
- data/sig/transcoder/gzip.rbs +4 -1
- data/sig/transcoder/json.rbs +1 -1
- data/sig/transcoder/multipart.rbs +6 -4
- data/sig/transcoder/utils/body_reader.rbs +3 -3
- data/sig/transcoder/utils/deflater.rbs +2 -3
- metadata +32 -14
- data/lib/httpx/session2.rb +0 -23
- data/lib/httpx/transcoder/utils/inflater.rb +0 -19
- data/lib/httpx/transcoder/xml.rb +0 -52
- data/sig/transcoder/utils/inflater.rbs +0 -12
- data/sig/transcoder/xml.rbs +0 -22
data/lib/httpx/plugins/proxy.rb
CHANGED
@@ -31,31 +31,53 @@ module HTTPX
|
|
31
31
|
end
|
32
32
|
|
33
33
|
class Parameters
|
34
|
-
attr_reader :uri, :username, :password, :scheme
|
34
|
+
attr_reader :uri, :username, :password, :scheme, :no_proxy
|
35
35
|
|
36
|
-
def initialize(uri
|
37
|
-
@
|
38
|
-
@
|
39
|
-
|
36
|
+
def initialize(uri: nil, scheme: nil, username: nil, password: nil, no_proxy: nil, **extra)
|
37
|
+
@no_proxy = Array(no_proxy) if no_proxy
|
38
|
+
@uris = Array(uri)
|
39
|
+
uri = @uris.first
|
40
|
+
|
41
|
+
@username = username
|
42
|
+
@password = password
|
40
43
|
|
41
|
-
|
44
|
+
@ns = 0
|
42
45
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
"basic"
|
48
|
-
else
|
49
|
-
return
|
46
|
+
if uri
|
47
|
+
@uri = uri.is_a?(URI::Generic) ? uri : URI(uri)
|
48
|
+
@username ||= @uri.user
|
49
|
+
@password ||= @uri.password
|
50
50
|
end
|
51
51
|
|
52
52
|
@scheme = scheme
|
53
53
|
|
54
|
-
|
54
|
+
return unless @uri && @username && @password
|
55
55
|
|
56
|
-
|
56
|
+
@authenticator = nil
|
57
|
+
@scheme ||= infer_default_auth_scheme(@uri)
|
58
|
+
|
59
|
+
return unless @scheme
|
57
60
|
|
58
|
-
@authenticator =
|
61
|
+
@authenticator = load_authenticator(@scheme, @username, @password, **extra)
|
62
|
+
end
|
63
|
+
|
64
|
+
def shift
|
65
|
+
# TODO: this operation must be synchronized
|
66
|
+
@ns += 1
|
67
|
+
@uri = @uris[@ns]
|
68
|
+
|
69
|
+
return unless @uri
|
70
|
+
|
71
|
+
@uri = URI(@uri) unless @uri.is_a?(URI::Generic)
|
72
|
+
|
73
|
+
scheme = infer_default_auth_scheme(@uri)
|
74
|
+
|
75
|
+
return unless scheme != @scheme
|
76
|
+
|
77
|
+
@scheme = scheme
|
78
|
+
@username = username || @uri.user
|
79
|
+
@password = password || @uri.password
|
80
|
+
@authenticator = load_authenticator(scheme, @username, @password)
|
59
81
|
end
|
60
82
|
|
61
83
|
def can_authenticate?(*args)
|
@@ -87,11 +109,34 @@ module HTTPX
|
|
87
109
|
super
|
88
110
|
end
|
89
111
|
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def infer_default_auth_scheme(uri)
|
116
|
+
case uri.scheme
|
117
|
+
when "socks5"
|
118
|
+
uri.scheme
|
119
|
+
when "http", "https"
|
120
|
+
"basic"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def load_authenticator(scheme, username, password, **extra)
|
125
|
+
auth_scheme = scheme.to_s.capitalize
|
126
|
+
|
127
|
+
require_relative "auth/#{scheme}" unless defined?(Authentication) && Authentication.const_defined?(auth_scheme, false)
|
128
|
+
|
129
|
+
Authentication.const_get(auth_scheme).new(username, password, **extra)
|
130
|
+
end
|
90
131
|
end
|
91
132
|
|
133
|
+
# adds support for the following options:
|
134
|
+
#
|
135
|
+
# :proxy :: proxy options defining *:uri*, *:username*, *:password* or
|
136
|
+
# *:scheme* (i.e. <tt>{ uri: "http://proxy" }</tt>)
|
92
137
|
module OptionsMethods
|
93
138
|
def option_proxy(value)
|
94
|
-
value.is_a?(Parameters) ? value : Hash[value]
|
139
|
+
value.is_a?(Parameters) ? value : Parameters.new(**Hash[value])
|
95
140
|
end
|
96
141
|
|
97
142
|
def option_supported_proxy_protocols(value)
|
@@ -102,88 +147,68 @@ module HTTPX
|
|
102
147
|
end
|
103
148
|
|
104
149
|
module InstanceMethods
|
105
|
-
|
106
|
-
|
107
|
-
def find_connection(request, connections, options)
|
150
|
+
def find_connection(request_uri, selector, options)
|
108
151
|
return super unless options.respond_to?(:proxy)
|
109
152
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
{ uri: next_proxy }
|
114
|
-
else
|
115
|
-
proxy = options.proxy
|
116
|
-
|
117
|
-
return super unless proxy
|
118
|
-
|
119
|
-
return super(request, connections, options.merge(proxy: nil)) unless proxy.key?(:uri)
|
120
|
-
|
121
|
-
@_proxy_uris ||= Array(proxy[:uri])
|
153
|
+
if (next_proxy = request_uri.find_proxy)
|
154
|
+
return super(request_uri, selector, options.merge(proxy: Parameters.new(uri: next_proxy)))
|
155
|
+
end
|
122
156
|
|
123
|
-
|
124
|
-
raise Error, "Failed to connect to proxy" unless next_proxy
|
157
|
+
proxy = options.proxy
|
125
158
|
|
126
|
-
|
159
|
+
return super unless proxy
|
127
160
|
|
128
|
-
|
129
|
-
"#{next_proxy.scheme}: unsupported proxy protocol" unless options.supported_proxy_protocols.include?(next_proxy.scheme)
|
161
|
+
next_proxy = proxy.uri
|
130
162
|
|
131
|
-
|
163
|
+
raise Error, "Failed to connect to proxy" unless next_proxy
|
132
164
|
|
133
|
-
|
134
|
-
|
165
|
+
raise Error,
|
166
|
+
"#{next_proxy.scheme}: unsupported proxy protocol" unless options.supported_proxy_protocols.include?(next_proxy.scheme)
|
135
167
|
|
136
|
-
|
137
|
-
|
138
|
-
end
|
168
|
+
if (no_proxy = proxy.no_proxy)
|
169
|
+
no_proxy = no_proxy.join(",") if no_proxy.is_a?(Array)
|
139
170
|
|
140
|
-
|
171
|
+
# TODO: setting proxy to nil leaks the connection object in the pool
|
172
|
+
return super(request_uri, selector, options.merge(proxy: nil)) unless URI::Generic.use_proxy?(request_uri.host, next_proxy.host,
|
173
|
+
next_proxy.port, no_proxy)
|
141
174
|
end
|
142
175
|
|
143
|
-
|
144
|
-
|
145
|
-
proxy_options = options.merge(proxy: proxy)
|
146
|
-
connection = pool.find_connection(uri, proxy_options) || init_connection(uri, proxy_options)
|
147
|
-
unless connections.nil? || connections.include?(connection)
|
148
|
-
connections << connection
|
149
|
-
set_connection_callbacks(connection, connections, options)
|
150
|
-
end
|
151
|
-
connection
|
176
|
+
super(request_uri, selector, options.merge(proxy: proxy))
|
152
177
|
end
|
153
178
|
|
154
|
-
|
179
|
+
private
|
180
|
+
|
181
|
+
def fetch_response(request, selector, options)
|
155
182
|
response = super
|
156
183
|
|
157
|
-
if response.is_a?(ErrorResponse) && proxy_error?(request, response)
|
158
|
-
|
184
|
+
if response.is_a?(ErrorResponse) && proxy_error?(request, response, options)
|
185
|
+
options.proxy.shift
|
159
186
|
|
160
187
|
# return last error response if no more proxies to try
|
161
|
-
return response if
|
188
|
+
return response if options.proxy.uri.nil?
|
162
189
|
|
163
190
|
log { "failed connecting to proxy, trying next..." }
|
164
191
|
request.transition(:idle)
|
165
|
-
send_request(request,
|
192
|
+
send_request(request, selector, options)
|
166
193
|
return
|
167
194
|
end
|
168
195
|
response
|
169
196
|
end
|
170
197
|
|
171
|
-
def proxy_error?(_request, response)
|
198
|
+
def proxy_error?(_request, response, options)
|
199
|
+
return false unless options.proxy
|
200
|
+
|
172
201
|
error = response.error
|
173
202
|
case error
|
174
203
|
when NativeResolveError
|
175
|
-
|
176
|
-
|
177
|
-
proxy_uri = URI(@_proxy_uris.first)
|
204
|
+
proxy_uri = URI(options.proxy.uri)
|
178
205
|
|
179
|
-
|
206
|
+
peer = error.connection.peer
|
180
207
|
|
181
208
|
# failed resolving proxy domain
|
182
|
-
|
209
|
+
peer.host == proxy_uri.host && peer.port == proxy_uri.port
|
183
210
|
when ResolveError
|
184
|
-
|
185
|
-
|
186
|
-
proxy_uri = URI(@_proxy_uris.first)
|
211
|
+
proxy_uri = URI(options.proxy.uri)
|
187
212
|
|
188
213
|
error.message.end_with?(proxy_uri.to_s)
|
189
214
|
when *PROXY_ERRORS
|
@@ -204,25 +229,11 @@ module HTTPX
|
|
204
229
|
|
205
230
|
# redefining the connection origin as the proxy's URI,
|
206
231
|
# as this will be used as the tcp peer ip.
|
207
|
-
proxy_uri = URI(@options.proxy.uri)
|
208
|
-
@origin.host = proxy_uri.host
|
209
|
-
@origin.port = proxy_uri.port
|
232
|
+
@proxy_uri = URI(@options.proxy.uri)
|
210
233
|
end
|
211
234
|
|
212
|
-
def
|
213
|
-
|
214
|
-
|
215
|
-
if @io.protocol == "h2" &&
|
216
|
-
@origin.scheme == "https" &&
|
217
|
-
connection.origin.scheme == "https" &&
|
218
|
-
@io.can_verify_peer?
|
219
|
-
# in proxied connections, .origin is the proxy ; Given names
|
220
|
-
# are stored in .origins, this is what is used.
|
221
|
-
origin = URI(connection.origins.first)
|
222
|
-
@io.verify_hostname(origin.host)
|
223
|
-
else
|
224
|
-
@origin == connection.origin
|
225
|
-
end
|
235
|
+
def peer
|
236
|
+
@proxy_uri || super
|
226
237
|
end
|
227
238
|
|
228
239
|
def connecting?
|
@@ -248,7 +259,7 @@ module HTTPX
|
|
248
259
|
@state = :open
|
249
260
|
|
250
261
|
super
|
251
|
-
emit(:close)
|
262
|
+
# emit(:close)
|
252
263
|
end
|
253
264
|
|
254
265
|
private
|
@@ -3,7 +3,12 @@
|
|
3
3
|
module HTTPX
|
4
4
|
module Plugins
|
5
5
|
#
|
6
|
-
# This plugin adds support for retrying requests when
|
6
|
+
# This plugin adds support for retrying requests when errors happen.
|
7
|
+
#
|
8
|
+
# It has a default max number of retries (see *MAX_RETRIES* and the *max_retries* option),
|
9
|
+
# after which it will return the last response, error or not. It will **not** raise an exception.
|
10
|
+
#
|
11
|
+
# It does not retry which are not considered idempotent (see *retry_change_requests* to override).
|
7
12
|
#
|
8
13
|
# https://gitlab.com/os85/httpx/wikis/Retries
|
9
14
|
#
|
@@ -38,6 +43,14 @@ module HTTPX
|
|
38
43
|
end
|
39
44
|
end
|
40
45
|
|
46
|
+
# adds support for the following options:
|
47
|
+
#
|
48
|
+
# :max_retries :: max number of times a request will be retried (defaults to <tt>3</tt>).
|
49
|
+
# :retry_change_requests :: whether idempotent requests are retried (defaults to <tt>false</tt>).
|
50
|
+
# :retry_after:: seconds after which a request is retried; can also be a callable object (i.e. <tt>->(req, res) { ... } </tt>)
|
51
|
+
# :retry_jitter :: number of seconds applied to *:retry_after* (must be a callable, i.e. <tt>->(retry_after) { ... } </tt>).
|
52
|
+
# :retry_on :: callable which alternatively defines a different rule for when a response is to be retried
|
53
|
+
# (i.e. <tt>->(res) { ... }</tt>).
|
41
54
|
module OptionsMethods
|
42
55
|
def option_retry_after(value)
|
43
56
|
# return early if callable
|
@@ -76,12 +89,12 @@ module HTTPX
|
|
76
89
|
|
77
90
|
module InstanceMethods
|
78
91
|
def max_retries(n)
|
79
|
-
with(max_retries: n
|
92
|
+
with(max_retries: n)
|
80
93
|
end
|
81
94
|
|
82
95
|
private
|
83
96
|
|
84
|
-
def fetch_response(request,
|
97
|
+
def fetch_response(request, selector, options)
|
85
98
|
response = super
|
86
99
|
|
87
100
|
if response &&
|
@@ -97,7 +110,7 @@ module HTTPX
|
|
97
110
|
)
|
98
111
|
__try_partial_retry(request, response)
|
99
112
|
log { "failed to get response, #{request.retries} tries to go..." }
|
100
|
-
request.retries -= 1
|
113
|
+
request.retries -= 1 unless request.ping? # do not exhaust retries on connection liveness probes
|
101
114
|
request.transition(:idle)
|
102
115
|
|
103
116
|
retry_after = options.retry_after
|
@@ -111,12 +124,17 @@ module HTTPX
|
|
111
124
|
|
112
125
|
retry_start = Utils.now
|
113
126
|
log { "retrying after #{retry_after} secs..." }
|
114
|
-
|
115
|
-
|
116
|
-
|
127
|
+
selector.after(retry_after) do
|
128
|
+
if request.response
|
129
|
+
# request has terminated abruptly meanwhile
|
130
|
+
request.emit(:response, request.response)
|
131
|
+
else
|
132
|
+
log { "retrying (elapsed time: #{Utils.elapsed_time(retry_start)})!!" }
|
133
|
+
send_request(request, selector, options)
|
134
|
+
end
|
117
135
|
end
|
118
136
|
else
|
119
|
-
send_request(request,
|
137
|
+
send_request(request, selector, options)
|
120
138
|
end
|
121
139
|
|
122
140
|
return
|
@@ -132,7 +150,7 @@ module HTTPX
|
|
132
150
|
RETRYABLE_ERRORS.any? { |klass| ex.is_a?(klass) }
|
133
151
|
end
|
134
152
|
|
135
|
-
def proxy_error?(request, response)
|
153
|
+
def proxy_error?(request, response, _)
|
136
154
|
super && !request.retries.positive?
|
137
155
|
end
|
138
156
|
|
@@ -149,7 +167,7 @@ module HTTPX
|
|
149
167
|
unless response.headers.key?("accept-ranges") &&
|
150
168
|
response.headers["accept-ranges"] == "bytes" && # there's nothing else supported though...
|
151
169
|
(original_body = response.body)
|
152
|
-
response.
|
170
|
+
response.body.close
|
153
171
|
return
|
154
172
|
end
|
155
173
|
|
@@ -87,6 +87,9 @@ module HTTPX
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
+
# adds support for the following options:
|
91
|
+
#
|
92
|
+
# :allowed_schemes :: list of URI schemes allowed (defaults to <tt>["https", "http"]</tt>)
|
90
93
|
module OptionsMethods
|
91
94
|
def option_allowed_schemes(value)
|
92
95
|
Array(value)
|
@@ -100,7 +103,7 @@ module HTTPX
|
|
100
103
|
|
101
104
|
error = ServerSideRequestForgeryError.new("#{request.uri} URI scheme not allowed")
|
102
105
|
error.set_backtrace(caller)
|
103
|
-
response = ErrorResponse.new(request, error
|
106
|
+
response = ErrorResponse.new(request, error)
|
104
107
|
request.emit(:response, response)
|
105
108
|
response
|
106
109
|
end
|
data/lib/httpx/plugins/stream.rb
CHANGED
@@ -4,27 +4,39 @@ module HTTPX
|
|
4
4
|
class StreamResponse
|
5
5
|
def initialize(request, session)
|
6
6
|
@request = request
|
7
|
+
@options = @request.options
|
7
8
|
@session = session
|
8
|
-
@
|
9
|
+
@response_enum = nil
|
10
|
+
@buffered_chunks = []
|
9
11
|
end
|
10
12
|
|
11
13
|
def each(&block)
|
12
14
|
return enum_for(__method__) unless block
|
13
15
|
|
16
|
+
if (response_enum = @response_enum)
|
17
|
+
@response_enum = nil
|
18
|
+
# streaming already started, let's finish it
|
19
|
+
|
20
|
+
while (chunk = @buffered_chunks.shift)
|
21
|
+
block.call(chunk)
|
22
|
+
end
|
23
|
+
|
24
|
+
# consume enum til the end
|
25
|
+
begin
|
26
|
+
while (chunk = response_enum.next)
|
27
|
+
block.call(chunk)
|
28
|
+
end
|
29
|
+
rescue StopIteration
|
30
|
+
return
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
14
34
|
@request.stream = self
|
15
35
|
|
16
36
|
begin
|
17
37
|
@on_chunk = block
|
18
38
|
|
19
|
-
|
20
|
-
# if we've already started collecting the payload, yield it first
|
21
|
-
# before proceeding.
|
22
|
-
body = @request.response.body
|
23
|
-
|
24
|
-
body.each do |chunk|
|
25
|
-
on_chunk(chunk)
|
26
|
-
end
|
27
|
-
end
|
39
|
+
response = @session.request(@request)
|
28
40
|
|
29
41
|
response.raise_for_status
|
30
42
|
ensure
|
@@ -64,27 +76,39 @@ module HTTPX
|
|
64
76
|
# :nocov:
|
65
77
|
|
66
78
|
def to_s
|
67
|
-
response
|
79
|
+
if @request.response
|
80
|
+
@request.response.to_s
|
81
|
+
else
|
82
|
+
@buffered_chunks.join
|
83
|
+
end
|
68
84
|
end
|
69
85
|
|
70
86
|
private
|
71
87
|
|
72
88
|
def response
|
73
|
-
return @response if @response
|
74
|
-
|
75
89
|
@request.response || begin
|
76
|
-
|
90
|
+
response_enum = each
|
91
|
+
while (chunk = response_enum.next)
|
92
|
+
@buffered_chunks << chunk
|
93
|
+
break if @request.response
|
94
|
+
end
|
95
|
+
@response_enum = response_enum
|
96
|
+
@request.response
|
77
97
|
end
|
78
98
|
end
|
79
99
|
|
80
|
-
def respond_to_missing?(meth,
|
81
|
-
response.
|
100
|
+
def respond_to_missing?(meth, include_private)
|
101
|
+
if (response = @request.response)
|
102
|
+
response.respond_to_missing?(meth, include_private)
|
103
|
+
else
|
104
|
+
@options.response_class.method_defined?(meth) || (include_private && @options.response_class.private_method_defined?(meth))
|
105
|
+
end || super
|
82
106
|
end
|
83
107
|
|
84
|
-
def method_missing(meth, *args, &block)
|
108
|
+
def method_missing(meth, *args, **kwargs, &block)
|
85
109
|
return super unless response.respond_to?(meth)
|
86
110
|
|
87
|
-
response.__send__(meth, *args, &block)
|
111
|
+
response.__send__(meth, *args, **kwargs, &block)
|
88
112
|
end
|
89
113
|
end
|
90
114
|
|
@@ -28,7 +28,7 @@ module HTTPX
|
|
28
28
|
end
|
29
29
|
|
30
30
|
module InstanceMethods
|
31
|
-
def fetch_response(request,
|
31
|
+
def fetch_response(request, selector, options)
|
32
32
|
response = super
|
33
33
|
|
34
34
|
if response
|
@@ -45,7 +45,7 @@ module HTTPX
|
|
45
45
|
return response unless protocol_handler
|
46
46
|
|
47
47
|
log { "upgrading to #{upgrade_protocol}..." }
|
48
|
-
connection = find_connection(request,
|
48
|
+
connection = find_connection(request.uri, selector, options)
|
49
49
|
|
50
50
|
# do not upgrade already upgraded connections
|
51
51
|
return if connection.upgrade_protocol == upgrade_protocol
|
@@ -60,14 +60,6 @@ module HTTPX
|
|
60
60
|
|
61
61
|
response
|
62
62
|
end
|
63
|
-
|
64
|
-
def close(*args)
|
65
|
-
return super if args.empty?
|
66
|
-
|
67
|
-
connections, = args
|
68
|
-
|
69
|
-
pool.close(connections.reject(&:hijacked))
|
70
|
-
end
|
71
63
|
end
|
72
64
|
|
73
65
|
module ConnectionMethods
|
@@ -75,6 +67,9 @@ module HTTPX
|
|
75
67
|
|
76
68
|
def hijack_io
|
77
69
|
@hijacked = true
|
70
|
+
|
71
|
+
# connection is taken away from selector and not given back to the pool.
|
72
|
+
@current_session.deselect_connection(self, @current_selector, true)
|
78
73
|
end
|
79
74
|
end
|
80
75
|
end
|
data/lib/httpx/plugins/webdav.rb
CHANGED
@@ -8,6 +8,10 @@ module HTTPX
|
|
8
8
|
# https://gitlab.com/os85/httpx/wikis/WebDav
|
9
9
|
#
|
10
10
|
module WebDav
|
11
|
+
def self.configure(klass)
|
12
|
+
klass.plugin(:xml)
|
13
|
+
end
|
14
|
+
|
11
15
|
module InstanceMethods
|
12
16
|
def copy(src, dest)
|
13
17
|
request("COPY", src, headers: { "destination" => @options.origin.merge(dest) })
|
@@ -43,6 +47,8 @@ module HTTPX
|
|
43
47
|
ensure
|
44
48
|
unlock(path, lock_token)
|
45
49
|
end
|
50
|
+
|
51
|
+
response
|
46
52
|
end
|
47
53
|
|
48
54
|
def unlock(path, lock_token)
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
module Plugins
|
5
|
+
#
|
6
|
+
# This plugin supports request XML encoding/response decoding using the nokogiri gem.
|
7
|
+
#
|
8
|
+
# https://gitlab.com/os85/httpx/wikis/XML
|
9
|
+
#
|
10
|
+
module XML
|
11
|
+
MIME_TYPES = %r{\b(application|text)/(.+\+)?xml\b}.freeze
|
12
|
+
module Transcoder
|
13
|
+
module_function
|
14
|
+
|
15
|
+
class Encoder
|
16
|
+
def initialize(xml)
|
17
|
+
@raw = xml
|
18
|
+
end
|
19
|
+
|
20
|
+
def content_type
|
21
|
+
charset = @raw.respond_to?(:encoding) && @raw.encoding ? @raw.encoding.to_s.downcase : "utf-8"
|
22
|
+
"application/xml; charset=#{charset}"
|
23
|
+
end
|
24
|
+
|
25
|
+
def bytesize
|
26
|
+
@raw.to_s.bytesize
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
@raw.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def encode(xml)
|
35
|
+
Encoder.new(xml)
|
36
|
+
end
|
37
|
+
|
38
|
+
def decode(response)
|
39
|
+
content_type = response.content_type.mime_type
|
40
|
+
|
41
|
+
raise HTTPX::Error, "invalid form mime type (#{content_type})" unless MIME_TYPES.match?(content_type)
|
42
|
+
|
43
|
+
Nokogiri::XML.method(:parse)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class << self
|
48
|
+
def load_dependencies(*)
|
49
|
+
require "nokogiri"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module ResponseMethods
|
54
|
+
# decodes the response payload into a Nokogiri::XML::Node object **if** the payload is valid
|
55
|
+
# "application/xml" (requires the "nokogiri" gem).
|
56
|
+
def xml
|
57
|
+
decode(Transcoder)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
module RequestBodyClassMethods
|
62
|
+
# ..., xml: Nokogiri::XML::Node #=> xml encoder
|
63
|
+
def initialize_body(params)
|
64
|
+
if (xml = params.delete(:xml))
|
65
|
+
# @type var xml: Nokogiri::XML::Node | String
|
66
|
+
return Transcoder.encode(xml)
|
67
|
+
end
|
68
|
+
|
69
|
+
super
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
register_plugin(:xml, XML)
|
75
|
+
end
|
76
|
+
end
|