httpx 0.10.2 → 0.11.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +11 -3
- data/doc/release_notes/0_11_0.md +76 -0
- data/lib/httpx/adapters/datadog.rb +205 -0
- data/lib/httpx/adapters/faraday.rb +0 -2
- data/lib/httpx/adapters/webmock.rb +123 -0
- data/lib/httpx/chainable.rb +1 -1
- data/lib/httpx/connection/http2.rb +4 -4
- data/lib/httpx/domain_name.rb +1 -3
- data/lib/httpx/errors.rb +2 -0
- data/lib/httpx/headers.rb +1 -0
- data/lib/httpx/io/ssl.rb +4 -8
- data/lib/httpx/io/udp.rb +1 -1
- data/lib/httpx/plugins/expect.rb +33 -8
- data/lib/httpx/plugins/multipart.rb +40 -35
- data/lib/httpx/plugins/multipart/encoder.rb +115 -0
- data/lib/httpx/plugins/multipart/mime_type_detector.rb +64 -0
- data/lib/httpx/plugins/multipart/part.rb +34 -0
- data/lib/httpx/plugins/proxy/socks5.rb +3 -2
- data/lib/httpx/plugins/push_promise.rb +2 -2
- data/lib/httpx/request.rb +21 -11
- data/lib/httpx/resolver.rb +7 -4
- data/lib/httpx/resolver/https.rb +4 -2
- data/lib/httpx/resolver/native.rb +10 -6
- data/lib/httpx/resolver/system.rb +1 -1
- data/lib/httpx/selector.rb +1 -0
- data/lib/httpx/session.rb +15 -18
- data/lib/httpx/transcoder.rb +6 -4
- data/lib/httpx/version.rb +1 -1
- data/sig/connection/http2.rbs +3 -4
- data/sig/headers.rbs +3 -0
- data/sig/plugins/multipart.rbs +27 -4
- data/sig/request.rbs +1 -1
- data/sig/resolver/https.rbs +2 -0
- data/sig/response.rbs +1 -1
- data/sig/session.rbs +1 -1
- data/sig/transcoder.rbs +2 -2
- data/sig/transcoder/body.rbs +2 -0
- data/sig/transcoder/form.rbs +7 -1
- data/sig/transcoder/json.rbs +3 -1
- metadata +9 -23
- data/sig/missing.rbs +0 -12
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
module Plugins::Multipart
|
5
|
+
module Part
|
6
|
+
module_function
|
7
|
+
|
8
|
+
def call(value)
|
9
|
+
# take out specialized objects of the way
|
10
|
+
if value.respond_to?(:filename) && value.respond_to?(:content_type) && value.respond_to?(:read)
|
11
|
+
return [value, value.content_type, value.filename]
|
12
|
+
end
|
13
|
+
|
14
|
+
content_type = filename = nil
|
15
|
+
|
16
|
+
if value.is_a?(Hash)
|
17
|
+
content_type = value[:content_type]
|
18
|
+
filename = value[:filename]
|
19
|
+
value = value[:body]
|
20
|
+
end
|
21
|
+
|
22
|
+
value = value.open(:binmode => true) if value.is_a?(Pathname)
|
23
|
+
|
24
|
+
if value.is_a?(File)
|
25
|
+
filename ||= File.basename(value.path)
|
26
|
+
content_type ||= MimeTypeDetector.call(value, filename) || "application/octet-stream"
|
27
|
+
[value, content_type, filename]
|
28
|
+
else
|
29
|
+
[StringIO.new(value.to_s), "text/plain"]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -159,9 +159,10 @@ module HTTPX
|
|
159
159
|
packet = [VERSION, CONNECT, 0].pack("C*")
|
160
160
|
begin
|
161
161
|
ip = IPAddr.new(uri.host)
|
162
|
-
raise Error, "Socks4 connection to #{ip} not supported" unless ip.ipv4?
|
163
162
|
|
164
|
-
|
163
|
+
ipcode = ip.ipv6? ? IPV6 : IPV4
|
164
|
+
|
165
|
+
packet << [ipcode].pack("C") << ip.hton
|
165
166
|
rescue IPAddr::InvalidAddressError
|
166
167
|
packet << [DOMAIN, uri.host.bytesize, uri.host].pack("CCA*")
|
167
168
|
end
|
@@ -70,8 +70,8 @@ module HTTPX
|
|
70
70
|
request.transition(:done)
|
71
71
|
response = request.response
|
72
72
|
response.mark_as_pushed!
|
73
|
-
stream.on(:data, &parser.method(:on_stream_data).curry[stream, request])
|
74
|
-
stream.on(:close, &parser.method(:on_stream_close).curry[stream, request])
|
73
|
+
stream.on(:data, &parser.method(:on_stream_data).curry(3)[stream, request])
|
74
|
+
stream.on(:close, &parser.method(:on_stream_close).curry(3)[stream, request])
|
75
75
|
end
|
76
76
|
end
|
77
77
|
end
|
data/lib/httpx/request.rb
CHANGED
@@ -81,6 +81,10 @@ module HTTPX
|
|
81
81
|
def response=(response)
|
82
82
|
return unless response
|
83
83
|
|
84
|
+
if response.status == 100
|
85
|
+
@informational_status = response.status
|
86
|
+
return
|
87
|
+
end
|
84
88
|
@response = response
|
85
89
|
end
|
86
90
|
|
@@ -170,6 +174,12 @@ module HTTPX
|
|
170
174
|
end
|
171
175
|
end
|
172
176
|
|
177
|
+
def rewind
|
178
|
+
return if empty?
|
179
|
+
|
180
|
+
@body.rewind if @body.respond_to?(:rewind)
|
181
|
+
end
|
182
|
+
|
173
183
|
def empty?
|
174
184
|
return true if @body.nil?
|
175
185
|
return false if chunked?
|
@@ -212,6 +222,7 @@ module HTTPX
|
|
212
222
|
def transition(nextstate)
|
213
223
|
case nextstate
|
214
224
|
when :idle
|
225
|
+
@body.rewind
|
215
226
|
@response = nil
|
216
227
|
@drainer = nil
|
217
228
|
when :headers
|
@@ -221,15 +232,15 @@ module HTTPX
|
|
221
232
|
@state == :expect
|
222
233
|
|
223
234
|
if @headers.key?("expect")
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
235
|
+
if @informational_status && @informational_status == 100
|
236
|
+
# check for 100 Continue response, and deallocate the var
|
237
|
+
# if @informational_status == 100
|
238
|
+
# @response = nil
|
239
|
+
# end
|
240
|
+
else
|
241
|
+
return if @state == :expect # do not re-set it
|
242
|
+
|
243
|
+
nextstate = :expect
|
233
244
|
end
|
234
245
|
end
|
235
246
|
when :done
|
@@ -241,8 +252,7 @@ module HTTPX
|
|
241
252
|
end
|
242
253
|
|
243
254
|
def expects?
|
244
|
-
@headers["expect"] == "100-continue" &&
|
245
|
-
@response && @response.status == 100
|
255
|
+
@headers["expect"] == "100-continue" && @informational_status == 100 && !@response
|
246
256
|
end
|
247
257
|
|
248
258
|
class ProcIO
|
data/lib/httpx/resolver.rb
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "resolv"
|
4
|
-
require "httpx/resolver/resolver_mixin"
|
5
|
-
require "httpx/resolver/system"
|
6
|
-
require "httpx/resolver/native"
|
7
|
-
require "httpx/resolver/https"
|
8
4
|
|
9
5
|
module HTTPX
|
10
6
|
module Resolver
|
11
7
|
extend Registry
|
12
8
|
|
9
|
+
RESOLVE_TIMEOUT = 5
|
10
|
+
|
11
|
+
require "httpx/resolver/resolver_mixin"
|
12
|
+
require "httpx/resolver/system"
|
13
|
+
require "httpx/resolver/native"
|
14
|
+
require "httpx/resolver/https"
|
15
|
+
|
13
16
|
register :system, System
|
14
17
|
register :native, Native
|
15
18
|
register :https, HTTPS
|
data/lib/httpx/resolver/https.rb
CHANGED
@@ -37,12 +37,14 @@ module HTTPX
|
|
37
37
|
@connections = []
|
38
38
|
@uri = URI(@resolver_options[:uri])
|
39
39
|
@uri_addresses = nil
|
40
|
+
@resolver = Resolv::DNS.new
|
41
|
+
@resolver.timeouts = @resolver_options.fetch(:timeouts, Resolver::RESOLVE_TIMEOUT)
|
40
42
|
end
|
41
43
|
|
42
44
|
def <<(connection)
|
43
45
|
return if @uri.origin == connection.origin.to_s
|
44
46
|
|
45
|
-
@uri_addresses ||=
|
47
|
+
@uri_addresses ||= ip_resolve(@uri.host) || system_resolve(@uri.host) || @resolver.getaddresses(@uri.host)
|
46
48
|
|
47
49
|
if @uri_addresses.empty?
|
48
50
|
ex = ResolveError.new("Can't resolve DNS server #{@uri.host}")
|
@@ -99,7 +101,7 @@ module HTTPX
|
|
99
101
|
log { "resolver: query #{type} for #{hostname}" }
|
100
102
|
begin
|
101
103
|
request = build_request(hostname, type)
|
102
|
-
request.on(:response, &method(:on_response).curry[request])
|
104
|
+
request.on(:response, &method(:on_response).curry(2)[request])
|
103
105
|
request.on(:promise, &method(:on_promise))
|
104
106
|
@requests[request] = connection
|
105
107
|
resolver_connection.send(request)
|
@@ -9,7 +9,6 @@ module HTTPX
|
|
9
9
|
include Resolver::ResolverMixin
|
10
10
|
using URIExtensions
|
11
11
|
|
12
|
-
RESOLVE_TIMEOUT = 5
|
13
12
|
RECORD_TYPES = {
|
14
13
|
"A" => Resolv::DNS::Resource::IN::A,
|
15
14
|
"AAAA" => Resolv::DNS::Resource::IN::AAAA,
|
@@ -19,7 +18,7 @@ module HTTPX
|
|
19
18
|
{
|
20
19
|
**Resolv::DNS::Config.default_config_hash,
|
21
20
|
packet_size: 512,
|
22
|
-
timeouts: RESOLVE_TIMEOUT,
|
21
|
+
timeouts: Resolver::RESOLVE_TIMEOUT,
|
23
22
|
record_types: RECORD_TYPES.keys,
|
24
23
|
}.freeze
|
25
24
|
else
|
@@ -27,7 +26,7 @@ module HTTPX
|
|
27
26
|
nameserver: nil,
|
28
27
|
**Resolv::DNS::Config.default_config_hash,
|
29
28
|
packet_size: 512,
|
30
|
-
timeouts: RESOLVE_TIMEOUT,
|
29
|
+
timeouts: Resolver::RESOLVE_TIMEOUT,
|
31
30
|
record_types: RECORD_TYPES.keys,
|
32
31
|
}.freeze
|
33
32
|
end
|
@@ -148,14 +147,19 @@ module HTTPX
|
|
148
147
|
queries[h] = connection
|
149
148
|
next
|
150
149
|
end
|
150
|
+
|
151
151
|
@timeouts[host].shift
|
152
152
|
if @timeouts[host].empty?
|
153
153
|
@timeouts.delete(host)
|
154
154
|
@connections.delete(connection)
|
155
|
-
|
155
|
+
# This loop_time passed to the exception is bogus. Ideally we would pass the total
|
156
|
+
# resolve timeout, including from the previous retries.
|
157
|
+
raise ResolveTimeoutError.new(loop_time, "Timed out")
|
158
|
+
# raise NativeResolveError.new(connection, host)
|
156
159
|
else
|
160
|
+
log { "resolver: timeout after #{timeout}s, retry(#{@timeouts[host].first}) #{host}..." }
|
157
161
|
connections << connection
|
158
|
-
|
162
|
+
queries[h] = connection
|
159
163
|
end
|
160
164
|
end
|
161
165
|
@queries = queries
|
@@ -279,7 +283,7 @@ module HTTPX
|
|
279
283
|
@io.connect
|
280
284
|
return unless @io.connected?
|
281
285
|
|
282
|
-
resolve if @queries.empty?
|
286
|
+
resolve if @queries.empty? && !@connections.empty?
|
283
287
|
when :closed
|
284
288
|
return unless @state == :open
|
285
289
|
|
@@ -20,7 +20,7 @@ module HTTPX
|
|
20
20
|
timeouts = resolv_options.delete(:timeouts)
|
21
21
|
resolv_options.delete(:cache)
|
22
22
|
@resolver = Resolv::DNS.new(resolv_options.empty? ? nil : resolv_options)
|
23
|
-
@resolver.timeouts = timeouts
|
23
|
+
@resolver.timeouts = timeouts || Resolver::RESOLVE_TIMEOUT
|
24
24
|
end
|
25
25
|
|
26
26
|
def closed?
|
data/lib/httpx/selector.rb
CHANGED
data/lib/httpx/session.rb
CHANGED
@@ -41,7 +41,7 @@ module HTTPX
|
|
41
41
|
def build_request(verb, uri, options = EMPTY_HASH)
|
42
42
|
rklass = @options.request_class
|
43
43
|
request = rklass.new(verb, uri, @options.merge(options).merge(persistent: @persistent))
|
44
|
-
request.on(:response, &method(:on_response).curry[request])
|
44
|
+
request.on(:response, &method(:on_response).curry(2)[request])
|
45
45
|
request.on(:promise, &method(:on_promise))
|
46
46
|
request
|
47
47
|
end
|
@@ -136,23 +136,20 @@ module HTTPX
|
|
136
136
|
def build_requests(*args, options)
|
137
137
|
request_options = @options.merge(options)
|
138
138
|
|
139
|
-
requests =
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
end
|
154
|
-
else
|
155
|
-
raise ArgumentError, "unsupported number of arguments"
|
139
|
+
requests = if args.size == 1
|
140
|
+
reqs = args.first
|
141
|
+
reqs.map do |verb, uri, opts = EMPTY_HASH|
|
142
|
+
build_request(verb, uri, request_options.merge(opts))
|
143
|
+
end
|
144
|
+
else
|
145
|
+
verb, uris = args
|
146
|
+
if uris.respond_to?(:each)
|
147
|
+
uris.enum_for(:each).map do |uri, opts = EMPTY_HASH|
|
148
|
+
build_request(verb, uri, request_options.merge(opts))
|
149
|
+
end
|
150
|
+
else
|
151
|
+
[build_request(verb, uris, request_options)]
|
152
|
+
end
|
156
153
|
end
|
157
154
|
raise ArgumentError, "wrong number of URIs (given 0, expect 1..+1)" if requests.empty?
|
158
155
|
|
data/lib/httpx/transcoder.rb
CHANGED
@@ -4,18 +4,20 @@ module HTTPX
|
|
4
4
|
module Transcoder
|
5
5
|
extend Registry
|
6
6
|
|
7
|
-
def self.normalize_keys(key, value, &block)
|
8
|
-
if
|
7
|
+
def self.normalize_keys(key, value, cond = nil, &block)
|
8
|
+
if (cond && cond.call(value))
|
9
|
+
block.call(key.to_s, value)
|
10
|
+
elsif value.respond_to?(:to_ary)
|
9
11
|
if value.empty?
|
10
12
|
block.call("#{key}[]")
|
11
13
|
else
|
12
14
|
value.to_ary.each do |element|
|
13
|
-
normalize_keys("#{key}[]", element, &block)
|
15
|
+
normalize_keys("#{key}[]", element, cond, &block)
|
14
16
|
end
|
15
17
|
end
|
16
18
|
elsif value.respond_to?(:to_hash)
|
17
19
|
value.to_hash.each do |child_key, child_value|
|
18
|
-
normalize_keys("#{key}[#{child_key}]", child_value, &block)
|
20
|
+
normalize_keys("#{key}[#{child_key}]", child_value, cond, &block)
|
19
21
|
end
|
20
22
|
else
|
21
23
|
block.call(key.to_s, value)
|
data/lib/httpx/version.rb
CHANGED
data/sig/connection/http2.rbs
CHANGED
@@ -53,12 +53,11 @@ module HTTPX
|
|
53
53
|
|
54
54
|
def join_body: (HTTP2Next::Stream stream, Request request) -> void
|
55
55
|
|
56
|
+
def on_stream_headers: (HTTP2Next::Stream stream, Request request, Array[[String, String]] headers) -> void
|
56
57
|
|
57
|
-
|
58
|
+
def on_stream_data: (HTTP2Next::Stream stream, Request request, string data) -> void
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
# def on_stream_close: (HTTP2Next::Stream stream, Request request, Symbol? error) -> void
|
60
|
+
def on_stream_close: (HTTP2Next::Stream stream, Request request, Symbol? error) -> void
|
62
61
|
|
63
62
|
def on_frame: (string bytes) -> void
|
64
63
|
|
data/sig/headers.rbs
CHANGED
data/sig/plugins/multipart.rbs
CHANGED
@@ -3,18 +3,41 @@ module HTTPX
|
|
3
3
|
module Multipart
|
4
4
|
def self.load_dependencies: (singleton(Session)) -> void
|
5
5
|
def self.configure: (*untyped) -> void
|
6
|
-
def self?.encode: (untyped) -> Encoder
|
6
|
+
def self?.encode: (untyped) -> (Encoder | Transcoder::Form::Encoder)
|
7
|
+
|
8
|
+
type multipart_value = string | Pathname | File | _Reader
|
9
|
+
|
10
|
+
type record_multipart_value = multipart_value |
|
11
|
+
{ content_type: String, filename: String, body: multipart_value } |
|
12
|
+
{ content_type: String, body: multipart_value }
|
13
|
+
|
14
|
+
type multipart_nested_value = multipart_value | _ToAry[multipart_value] | _ToHash[string, multipart_value]
|
15
|
+
|
16
|
+
type multipart_input = Enumerable[[string, multipart_value], untyped]
|
7
17
|
|
8
18
|
class Encoder
|
9
19
|
include Transcoder::_Encoder
|
10
|
-
include _ToS
|
11
20
|
include _Reader
|
12
21
|
|
22
|
+
def content_type: () -> String
|
23
|
+
|
13
24
|
private
|
14
25
|
|
15
|
-
def initialize: (Hash[Symbol | string,
|
26
|
+
def initialize: (Hash[Symbol | string, multipart_nested_value] multipart_data) -> untyped
|
27
|
+
|
28
|
+
def header_part: (string key, String content_type, String? filename) -> StringIO
|
29
|
+
|
30
|
+
def read_chunks: (String buffer, Integer? length) -> void
|
31
|
+
|
32
|
+
def read_from_part: (Integer? max_length) -> void
|
33
|
+
end
|
34
|
+
|
35
|
+
module Part
|
36
|
+
def self?.call: (multipart_nested_value) -> ([_Reader, String, String?] | [_Reader, String])
|
37
|
+
end
|
16
38
|
|
17
|
-
|
39
|
+
module MimeTypeDetector
|
40
|
+
def self?.call: (IO file, ?String filename) -> String?
|
18
41
|
end
|
19
42
|
end
|
20
43
|
end
|
data/sig/request.rbs
CHANGED
data/sig/resolver/https.rbs
CHANGED
data/sig/response.rbs
CHANGED
data/sig/session.rbs
CHANGED
@@ -29,7 +29,7 @@ module HTTPX
|
|
29
29
|
| (?options?) -> untyped
|
30
30
|
|
31
31
|
def pool: -> Pool
|
32
|
-
|
32
|
+
def on_response: (Request, response) -> void
|
33
33
|
def on_promise: (untyped, untyped) -> void
|
34
34
|
def fetch_response: (Request, *untyped) -> response?
|
35
35
|
def set_connection_callbacks: (Connection, Array[Connection], Options) -> void
|
data/sig/transcoder.rbs
CHANGED
@@ -4,8 +4,8 @@ module HTTPX
|
|
4
4
|
module Transcoder
|
5
5
|
extend HTTPX::Registry[String, Class]
|
6
6
|
|
7
|
-
def self.normalize_keys: (string | Symbol, top) { (string, top) -> void } -> void
|
8
|
-
| (string | Symbol, top) { (string) -> void } -> void
|
7
|
+
def self.normalize_keys: (string | Symbol, top, ?Proc?) { (string, top) -> void } -> void
|
8
|
+
| (string | Symbol, top, ?Proc?) { (string) -> void } -> void
|
9
9
|
|
10
10
|
interface _Encoder
|
11
11
|
def bytesize: () -> Numeric
|