httpx 1.3.4 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/doc/release_notes/1_4_0.md +43 -0
- data/lib/httpx/adapters/faraday.rb +2 -0
- data/lib/httpx/adapters/webmock.rb +11 -5
- data/lib/httpx/callbacks.rb +0 -5
- data/lib/httpx/chainable.rb +3 -1
- data/lib/httpx/connection/http2.rb +11 -7
- data/lib/httpx/connection.rb +128 -16
- data/lib/httpx/errors.rb +12 -0
- data/lib/httpx/loggable.rb +5 -5
- data/lib/httpx/options.rb +26 -16
- data/lib/httpx/plugins/aws_sigv4.rb +31 -16
- data/lib/httpx/plugins/callbacks.rb +12 -2
- data/lib/httpx/plugins/circuit_breaker.rb +0 -5
- data/lib/httpx/plugins/content_digest.rb +202 -0
- data/lib/httpx/plugins/expect.rb +4 -3
- data/lib/httpx/plugins/follow_redirects.rb +7 -8
- data/lib/httpx/plugins/h2c.rb +23 -20
- data/lib/httpx/plugins/internal_telemetry.rb +27 -0
- data/lib/httpx/plugins/persistent.rb +16 -0
- data/lib/httpx/plugins/proxy/http.rb +17 -19
- data/lib/httpx/plugins/proxy.rb +91 -93
- data/lib/httpx/plugins/retries.rb +5 -8
- 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 +16 -12
- data/lib/httpx/request.rb +1 -1
- data/lib/httpx/resolver/https.rb +12 -19
- data/lib/httpx/resolver/multi.rb +34 -16
- data/lib/httpx/resolver/native.rb +36 -13
- data/lib/httpx/resolver/resolver.rb +49 -11
- data/lib/httpx/resolver/system.rb +29 -11
- data/lib/httpx/resolver.rb +21 -14
- data/lib/httpx/response.rb +5 -3
- data/lib/httpx/selector.rb +164 -95
- data/lib/httpx/session.rb +296 -139
- data/lib/httpx/transcoder/gzip.rb +0 -3
- data/lib/httpx/transcoder/json.rb +14 -2
- data/lib/httpx/transcoder/utils/deflater.rb +7 -4
- data/lib/httpx/transcoder/utils/inflater.rb +2 -0
- data/lib/httpx/transcoder.rb +0 -1
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +19 -20
- data/sig/callbacks.rbs +0 -1
- data/sig/chainable.rbs +4 -0
- data/sig/connection/http2.rbs +1 -1
- data/sig/connection.rbs +14 -3
- data/sig/errors.rbs +6 -0
- data/sig/loggable.rbs +2 -0
- data/sig/options.rbs +7 -0
- 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/grpc/call.rbs +4 -0
- data/sig/plugins/persistent.rbs +4 -1
- data/sig/plugins/proxy/socks5.rbs +11 -3
- data/sig/plugins/proxy.rbs +18 -11
- data/sig/plugins/push_promise.rbs +3 -0
- 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/webdav.rbs +23 -0
- data/sig/plugins/xml.rbs +37 -0
- data/sig/pool.rbs +25 -33
- data/sig/request/body.rbs +5 -1
- data/sig/resolver/multi.rbs +26 -1
- data/sig/resolver/native.rbs +0 -2
- data/sig/resolver/resolver.rbs +21 -2
- data/sig/resolver.rbs +5 -1
- data/sig/response/buffer.rbs +1 -1
- data/sig/selector.rbs +30 -4
- data/sig/session.rbs +45 -18
- data/sig/transcoder/body.rbs +1 -1
- 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/utils/body_reader.rbs +2 -2
- data/sig/transcoder/utils/deflater.rbs +2 -2
- metadata +10 -4
- data/lib/httpx/transcoder/xml.rb +0 -52
- data/sig/transcoder/xml.rbs +0 -22
@@ -135,7 +135,7 @@ module HTTPX
|
|
135
135
|
return unless query
|
136
136
|
|
137
137
|
h, connection = query
|
138
|
-
host = connection.
|
138
|
+
host = connection.peer.host
|
139
139
|
timeout = (@timeouts[host][0] -= loop_time)
|
140
140
|
|
141
141
|
return unless timeout <= 0
|
@@ -164,7 +164,10 @@ module HTTPX
|
|
164
164
|
@connections.delete(connection)
|
165
165
|
# This loop_time passed to the exception is bogus. Ideally we would pass the total
|
166
166
|
# resolve timeout, including from the previous retries.
|
167
|
-
|
167
|
+
ex = ResolveTimeoutError.new(loop_time, "Timed out while resolving #{connection.peer.host}")
|
168
|
+
ex.set_backtrace(ex ? ex.backtrace : caller)
|
169
|
+
emit_resolve_error(connection, host, ex)
|
170
|
+
emit(:close, self)
|
168
171
|
end
|
169
172
|
end
|
170
173
|
|
@@ -248,7 +251,10 @@ module HTTPX
|
|
248
251
|
|
249
252
|
unless @queries.value?(connection)
|
250
253
|
@connections.delete(connection)
|
251
|
-
|
254
|
+
ex = NativeResolveError.new(connection, connection.peer.host, "name or service not known")
|
255
|
+
ex.set_backtrace(ex ? ex.backtrace : caller)
|
256
|
+
emit_resolve_error(connection, connection.peer.host, ex)
|
257
|
+
emit(:close, self)
|
252
258
|
end
|
253
259
|
|
254
260
|
resolve
|
@@ -265,13 +271,13 @@ module HTTPX
|
|
265
271
|
hostname, connection = @queries.first
|
266
272
|
reset_hostname(hostname)
|
267
273
|
@connections.delete(connection)
|
268
|
-
ex = NativeResolveError.new(connection, connection.
|
274
|
+
ex = NativeResolveError.new(connection, connection.peer.host, "unknown DNS error (error code #{result})")
|
269
275
|
raise ex
|
270
276
|
when :decode_error
|
271
277
|
hostname, connection = @queries.first
|
272
278
|
reset_hostname(hostname)
|
273
279
|
@connections.delete(connection)
|
274
|
-
ex = NativeResolveError.new(connection, connection.
|
280
|
+
ex = NativeResolveError.new(connection, connection.peer.host, result.message)
|
275
281
|
ex.set_backtrace(result.backtrace)
|
276
282
|
raise ex
|
277
283
|
end
|
@@ -283,7 +289,7 @@ module HTTPX
|
|
283
289
|
hostname, connection = @queries.first
|
284
290
|
reset_hostname(hostname)
|
285
291
|
@connections.delete(connection)
|
286
|
-
raise NativeResolveError.new(connection, connection.
|
292
|
+
raise NativeResolveError.new(connection, connection.peer.host)
|
287
293
|
else
|
288
294
|
address = addresses.first
|
289
295
|
name = address["name"]
|
@@ -309,9 +315,9 @@ module HTTPX
|
|
309
315
|
if address.key?("alias") # CNAME
|
310
316
|
hostname_alias = address["alias"]
|
311
317
|
# clean up intermediate queries
|
312
|
-
@timeouts.delete(name) unless connection.
|
318
|
+
@timeouts.delete(name) unless connection.peer.host == name
|
313
319
|
|
314
|
-
if
|
320
|
+
if early_resolve(connection, hostname: hostname_alias)
|
315
321
|
@connections.delete(connection)
|
316
322
|
else
|
317
323
|
if @socket_type == :tcp
|
@@ -326,13 +332,13 @@ module HTTPX
|
|
326
332
|
end
|
327
333
|
else
|
328
334
|
reset_hostname(name, connection: connection)
|
329
|
-
@timeouts.delete(connection.
|
335
|
+
@timeouts.delete(connection.peer.host)
|
330
336
|
@connections.delete(connection)
|
331
|
-
Resolver.cached_lookup_set(connection.
|
337
|
+
Resolver.cached_lookup_set(connection.peer.host, @family, addresses) if @resolver_options[:cache]
|
332
338
|
catch(:coalesced) { emit_addresses(connection, @family, addresses.map { |addr| addr["data"] }) }
|
333
339
|
end
|
334
340
|
end
|
335
|
-
return emit(:close) if @connections.empty?
|
341
|
+
return emit(:close, self) if @connections.empty?
|
336
342
|
|
337
343
|
resolve
|
338
344
|
end
|
@@ -345,8 +351,8 @@ module HTTPX
|
|
345
351
|
hostname ||= @queries.key(connection)
|
346
352
|
|
347
353
|
if hostname.nil?
|
348
|
-
hostname = connection.
|
349
|
-
log { "resolver: resolve IDN #{connection.
|
354
|
+
hostname = connection.peer.host
|
355
|
+
log { "resolver: resolve IDN #{connection.peer.non_ascii_hostname} as #{hostname}" } if connection.peer.non_ascii_hostname
|
350
356
|
|
351
357
|
hostname = generate_candidates(hostname).each do |name|
|
352
358
|
@queries[name] = connection
|
@@ -358,7 +364,10 @@ module HTTPX
|
|
358
364
|
begin
|
359
365
|
@write_buffer << encode_dns_query(hostname)
|
360
366
|
rescue Resolv::DNS::EncodeError => e
|
367
|
+
reset_hostname(hostname, connection: connection)
|
368
|
+
@connections.delete(connection)
|
361
369
|
emit_resolve_error(connection, hostname, e)
|
370
|
+
emit(:close, self) if @connections.empty?
|
362
371
|
end
|
363
372
|
end
|
364
373
|
|
@@ -430,17 +439,31 @@ module HTTPX
|
|
430
439
|
@read_buffer.clear
|
431
440
|
end
|
432
441
|
@state = nextstate
|
442
|
+
rescue Errno::ECONNREFUSED,
|
443
|
+
Errno::EADDRNOTAVAIL,
|
444
|
+
Errno::EHOSTUNREACH,
|
445
|
+
SocketError,
|
446
|
+
IOError,
|
447
|
+
ConnectTimeoutError => e
|
448
|
+
# these errors may happen during TCP handshake
|
449
|
+
# treat them as resolve errors.
|
450
|
+
handle_error(e)
|
433
451
|
end
|
434
452
|
|
435
453
|
def handle_error(error)
|
436
454
|
if error.respond_to?(:connection) &&
|
437
455
|
error.respond_to?(:host)
|
456
|
+
reset_hostname(error.host, connection: error.connection)
|
457
|
+
@connections.delete(error.connection)
|
438
458
|
emit_resolve_error(error.connection, error.host, error)
|
439
459
|
else
|
440
460
|
@queries.each do |host, connection|
|
461
|
+
reset_hostname(host, connection: connection)
|
462
|
+
@connections.delete(connection)
|
441
463
|
emit_resolve_error(connection, host, error)
|
442
464
|
end
|
443
465
|
end
|
466
|
+
emit(:close, self) if @connections.empty?
|
444
467
|
end
|
445
468
|
|
446
469
|
def reset_hostname(hostname, connection: @queries.delete(hostname), reset_candidates: true)
|
@@ -26,14 +26,26 @@ module HTTPX
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
attr_reader :family
|
29
|
+
attr_reader :family, :options
|
30
30
|
|
31
|
-
attr_writer :
|
31
|
+
attr_writer :current_selector, :current_session
|
32
|
+
|
33
|
+
attr_accessor :multi
|
32
34
|
|
33
35
|
def initialize(family, options)
|
34
36
|
@family = family
|
35
37
|
@record_type = RECORD_TYPES[family]
|
36
38
|
@options = options
|
39
|
+
|
40
|
+
set_resolver_callbacks
|
41
|
+
end
|
42
|
+
|
43
|
+
def each_connection(&block)
|
44
|
+
enum_for(__method__) unless block
|
45
|
+
|
46
|
+
return unless @connections
|
47
|
+
|
48
|
+
@connections.each(&block)
|
37
49
|
end
|
38
50
|
|
39
51
|
def close; end
|
@@ -48,6 +60,10 @@ module HTTPX
|
|
48
60
|
true
|
49
61
|
end
|
50
62
|
|
63
|
+
def inflight?
|
64
|
+
false
|
65
|
+
end
|
66
|
+
|
51
67
|
def emit_addresses(connection, family, addresses, early_resolve = false)
|
52
68
|
addresses.map! do |address|
|
53
69
|
address.is_a?(IPAddr) ? address : IPAddr.new(address.to_s)
|
@@ -56,14 +72,14 @@ module HTTPX
|
|
56
72
|
# double emission check, but allow early resolution to work
|
57
73
|
return if !early_resolve && connection.addresses && !addresses.intersect?(connection.addresses)
|
58
74
|
|
59
|
-
log { "resolver: answer #{FAMILY_TYPES[RECORD_TYPES[family]]} #{connection.
|
60
|
-
if @
|
75
|
+
log { "resolver: answer #{FAMILY_TYPES[RECORD_TYPES[family]]} #{connection.peer.host}: #{addresses.inspect}" }
|
76
|
+
if @current_selector && # if triggered by early resolve, session may not be here yet
|
61
77
|
!connection.io &&
|
62
78
|
connection.options.ip_families.size > 1 &&
|
63
79
|
family == Socket::AF_INET &&
|
64
|
-
addresses.first.to_s != connection.
|
80
|
+
addresses.first.to_s != connection.peer.host.to_s
|
65
81
|
log { "resolver: A response, applying resolution delay..." }
|
66
|
-
@
|
82
|
+
@current_selector.after(0.05) do
|
67
83
|
unless connection.state == :closed ||
|
68
84
|
# double emission check
|
69
85
|
(connection.addresses && addresses.intersect?(connection.addresses))
|
@@ -92,20 +108,22 @@ module HTTPX
|
|
92
108
|
end
|
93
109
|
end
|
94
110
|
|
95
|
-
def early_resolve(connection, hostname: connection.
|
111
|
+
def early_resolve(connection, hostname: connection.peer.host)
|
96
112
|
addresses = @resolver_options[:cache] && (connection.addresses || HTTPX::Resolver.nolookup_resolve(hostname))
|
97
113
|
|
98
|
-
return unless addresses
|
114
|
+
return false unless addresses
|
99
115
|
|
100
116
|
addresses = addresses.select { |addr| addr.family == @family }
|
101
117
|
|
102
|
-
return if addresses.empty?
|
118
|
+
return false if addresses.empty?
|
103
119
|
|
104
120
|
emit_addresses(connection, @family, addresses, true)
|
121
|
+
|
122
|
+
true
|
105
123
|
end
|
106
124
|
|
107
|
-
def emit_resolve_error(connection, hostname = connection.
|
108
|
-
|
125
|
+
def emit_resolve_error(connection, hostname = connection.peer.host, ex = nil)
|
126
|
+
emit_connection_error(connection, resolve_error(hostname, ex))
|
109
127
|
end
|
110
128
|
|
111
129
|
def resolve_error(hostname, ex = nil)
|
@@ -116,5 +134,25 @@ module HTTPX
|
|
116
134
|
error.set_backtrace(ex ? ex.backtrace : caller)
|
117
135
|
error
|
118
136
|
end
|
137
|
+
|
138
|
+
def set_resolver_callbacks
|
139
|
+
on(:resolve, &method(:resolve_connection))
|
140
|
+
on(:error, &method(:emit_connection_error))
|
141
|
+
on(:close, &method(:close_resolver))
|
142
|
+
end
|
143
|
+
|
144
|
+
def resolve_connection(connection)
|
145
|
+
@current_session.__send__(:on_resolver_connection, connection, @current_selector)
|
146
|
+
end
|
147
|
+
|
148
|
+
def emit_connection_error(connection, error)
|
149
|
+
return connection.emit(:connect_error, error) if connection.connecting? && connection.callbacks_for?(:connect_error)
|
150
|
+
|
151
|
+
connection.emit(:error, error)
|
152
|
+
end
|
153
|
+
|
154
|
+
def close_resolver(resolver)
|
155
|
+
@current_session.__send__(:on_resolver_close, resolver, @current_selector)
|
156
|
+
end
|
119
157
|
end
|
120
158
|
end
|
@@ -47,8 +47,12 @@ module HTTPX
|
|
47
47
|
yield self
|
48
48
|
end
|
49
49
|
|
50
|
-
def
|
51
|
-
|
50
|
+
def multi
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def empty?
|
55
|
+
true
|
52
56
|
end
|
53
57
|
|
54
58
|
def close
|
@@ -84,7 +88,7 @@ module HTTPX
|
|
84
88
|
|
85
89
|
return unless connection
|
86
90
|
|
87
|
-
@timeouts[connection.
|
91
|
+
@timeouts[connection.peer.host].first
|
88
92
|
end
|
89
93
|
|
90
94
|
def <<(connection)
|
@@ -92,6 +96,11 @@ module HTTPX
|
|
92
96
|
resolve
|
93
97
|
end
|
94
98
|
|
99
|
+
def early_resolve(connection, **)
|
100
|
+
self << connection
|
101
|
+
true
|
102
|
+
end
|
103
|
+
|
95
104
|
def handle_socket_timeout(interval)
|
96
105
|
error = HTTPX::ResolveTimeoutError.new(interval, "timed out while waiting on select")
|
97
106
|
error.set_backtrace(caller)
|
@@ -120,23 +129,26 @@ module HTTPX
|
|
120
129
|
def consume
|
121
130
|
return if @connections.empty?
|
122
131
|
|
123
|
-
|
132
|
+
if @pipe_read.wait_readable
|
133
|
+
event = @pipe_read.getbyte
|
134
|
+
|
124
135
|
case event
|
125
136
|
when DONE
|
126
137
|
*pair, addrs = @pipe_mutex.synchronize { @ips.pop }
|
127
138
|
@queries.delete(pair)
|
139
|
+
_, connection = pair
|
140
|
+
@connections.delete(connection)
|
128
141
|
|
129
142
|
family, connection = pair
|
130
143
|
catch(:coalesced) { emit_addresses(connection, family, addrs) }
|
131
144
|
when ERROR
|
132
145
|
*pair, error = @pipe_mutex.synchronize { @ips.pop }
|
133
146
|
@queries.delete(pair)
|
147
|
+
@connections.delete(connection)
|
134
148
|
|
135
|
-
|
136
|
-
emit_resolve_error(connection, connection.
|
149
|
+
_, connection = pair
|
150
|
+
emit_resolve_error(connection, connection.peer.host, error)
|
137
151
|
end
|
138
|
-
|
139
|
-
@connections.delete(connection) if @queries.empty?
|
140
152
|
end
|
141
153
|
|
142
154
|
return emit(:close, self) if @connections.empty?
|
@@ -148,9 +160,9 @@ module HTTPX
|
|
148
160
|
raise Error, "no URI to resolve" unless connection
|
149
161
|
return unless @queries.empty?
|
150
162
|
|
151
|
-
hostname = connection.
|
163
|
+
hostname = connection.peer.host
|
152
164
|
scheme = connection.origin.scheme
|
153
|
-
log { "resolver: resolve IDN #{connection.
|
165
|
+
log { "resolver: resolve IDN #{connection.peer.non_ascii_hostname} as #{hostname}" } if connection.peer.non_ascii_hostname
|
154
166
|
|
155
167
|
transition(:open)
|
156
168
|
|
@@ -164,7 +176,7 @@ module HTTPX
|
|
164
176
|
def async_resolve(connection, hostname, scheme)
|
165
177
|
families = connection.options.ip_families
|
166
178
|
log { "resolver: query for #{hostname}" }
|
167
|
-
timeouts = @timeouts[connection.
|
179
|
+
timeouts = @timeouts[connection.peer.host]
|
168
180
|
resolve_timeout = timeouts.first
|
169
181
|
|
170
182
|
Thread.start do
|
@@ -210,5 +222,11 @@ module HTTPX
|
|
210
222
|
def __addrinfo_resolve(host, scheme)
|
211
223
|
Addrinfo.getaddrinfo(host, scheme, Socket::AF_UNSPEC, Socket::SOCK_STREAM)
|
212
224
|
end
|
225
|
+
|
226
|
+
def emit_connection_error(_, error)
|
227
|
+
throw(:resolve_error, error)
|
228
|
+
end
|
229
|
+
|
230
|
+
def close_resolver(resolver); end
|
213
231
|
end
|
214
232
|
end
|
data/lib/httpx/resolver.rb
CHANGED
@@ -53,8 +53,8 @@ module HTTPX
|
|
53
53
|
|
54
54
|
def cached_lookup(hostname)
|
55
55
|
now = Utils.now
|
56
|
-
|
57
|
-
lookup(hostname, now)
|
56
|
+
lookup_synchronize do |lookups|
|
57
|
+
lookup(hostname, lookups, now)
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
@@ -63,37 +63,37 @@ module HTTPX
|
|
63
63
|
entries.each do |entry|
|
64
64
|
entry["TTL"] += now
|
65
65
|
end
|
66
|
-
|
66
|
+
lookup_synchronize do |lookups|
|
67
67
|
case family
|
68
68
|
when Socket::AF_INET6
|
69
|
-
|
69
|
+
lookups[hostname].concat(entries)
|
70
70
|
when Socket::AF_INET
|
71
|
-
|
71
|
+
lookups[hostname].unshift(*entries)
|
72
72
|
end
|
73
73
|
entries.each do |entry|
|
74
74
|
next unless entry["name"] != hostname
|
75
75
|
|
76
76
|
case family
|
77
77
|
when Socket::AF_INET6
|
78
|
-
|
78
|
+
lookups[entry["name"]] << entry
|
79
79
|
when Socket::AF_INET
|
80
|
-
|
80
|
+
lookups[entry["name"]].unshift(entry)
|
81
81
|
end
|
82
82
|
end
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
86
|
# do not use directly!
|
87
|
-
def lookup(hostname, ttl)
|
88
|
-
return unless
|
87
|
+
def lookup(hostname, lookups, ttl)
|
88
|
+
return unless lookups.key?(hostname)
|
89
89
|
|
90
|
-
entries =
|
90
|
+
entries = lookups[hostname] = lookups[hostname].select do |address|
|
91
91
|
address["TTL"] > ttl
|
92
92
|
end
|
93
93
|
|
94
94
|
ips = entries.flat_map do |address|
|
95
95
|
if address.key?("alias")
|
96
|
-
lookup(address["alias"], ttl)
|
96
|
+
lookup(address["alias"], lookups, ttl)
|
97
97
|
else
|
98
98
|
IPAddr.new(address["data"])
|
99
99
|
end
|
@@ -103,12 +103,11 @@ module HTTPX
|
|
103
103
|
end
|
104
104
|
|
105
105
|
def generate_id
|
106
|
-
|
106
|
+
id_synchronize { @identifier = (@identifier + 1) & 0xFFFF }
|
107
107
|
end
|
108
108
|
|
109
109
|
def encode_dns_query(hostname, type: Resolv::DNS::Resource::IN::A, message_id: generate_id)
|
110
|
-
Resolv::DNS::Message.new.tap do |query|
|
111
|
-
query.id = message_id
|
110
|
+
Resolv::DNS::Message.new(message_id).tap do |query|
|
112
111
|
query.rd = 1
|
113
112
|
query.add_question(hostname, type)
|
114
113
|
end.encode
|
@@ -150,5 +149,13 @@ module HTTPX
|
|
150
149
|
|
151
150
|
[:ok, addresses]
|
152
151
|
end
|
152
|
+
|
153
|
+
def lookup_synchronize
|
154
|
+
@lookup_mutex.synchronize { yield(@lookups) }
|
155
|
+
end
|
156
|
+
|
157
|
+
def id_synchronize(&block)
|
158
|
+
@identifier_mutex.synchronize(&block)
|
159
|
+
end
|
153
160
|
end
|
154
161
|
end
|
data/lib/httpx/response.rb
CHANGED
@@ -166,10 +166,12 @@ module HTTPX
|
|
166
166
|
decode(Transcoder::Form)
|
167
167
|
end
|
168
168
|
|
169
|
-
# decodes the response payload into a Nokogiri::XML::Node object **if** the payload is valid
|
170
|
-
# "application/xml" (requires the "nokogiri" gem).
|
171
169
|
def xml
|
172
|
-
|
170
|
+
# TODO: remove at next major version.
|
171
|
+
warn "DEPRECATION WARNING: calling `.#{__method__}` on plain HTTPX responses is deprecated. " \
|
172
|
+
"Use HTTPX.plugin(:xml) sessions and call `.#{__method__}` in its responses instead."
|
173
|
+
require "httpx/plugins/xml"
|
174
|
+
decode(Plugins::XML::Transcoder)
|
173
175
|
end
|
174
176
|
|
175
177
|
private
|