httpx 0.17.0 → 0.18.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_18_0.md +69 -0
- data/lib/httpx/adapters/datadog.rb +1 -1
- data/lib/httpx/adapters/faraday.rb +5 -3
- data/lib/httpx/adapters/webmock.rb +7 -1
- data/lib/httpx/altsvc.rb +2 -2
- data/lib/httpx/chainable.rb +3 -3
- data/lib/httpx/connection/http1.rb +3 -3
- data/lib/httpx/connection/http2.rb +6 -4
- data/lib/httpx/connection.rb +67 -70
- data/lib/httpx/domain_name.rb +1 -1
- data/lib/httpx/extensions.rb +50 -4
- data/lib/httpx/io/ssl.rb +1 -1
- data/lib/httpx/io/tls.rb +7 -7
- data/lib/httpx/loggable.rb +5 -5
- data/lib/httpx/options.rb +7 -7
- data/lib/httpx/plugins/aws_sdk_authentication.rb +42 -18
- data/lib/httpx/plugins/aws_sigv4.rb +9 -11
- data/lib/httpx/plugins/compression.rb +5 -3
- data/lib/httpx/plugins/cookies/jar.rb +1 -1
- data/lib/httpx/plugins/expect.rb +7 -3
- data/lib/httpx/plugins/grpc/message.rb +2 -2
- data/lib/httpx/plugins/grpc.rb +3 -3
- data/lib/httpx/plugins/internal_telemetry.rb +8 -8
- data/lib/httpx/plugins/multipart.rb +2 -2
- data/lib/httpx/plugins/response_cache/store.rb +55 -0
- data/lib/httpx/plugins/response_cache.rb +88 -0
- data/lib/httpx/plugins/retries.rb +22 -3
- data/lib/httpx/plugins/stream.rb +1 -1
- data/lib/httpx/pool.rb +39 -13
- data/lib/httpx/request.rb +6 -6
- data/lib/httpx/resolver/https.rb +5 -7
- data/lib/httpx/resolver/native.rb +4 -2
- data/lib/httpx/resolver/system.rb +2 -0
- data/lib/httpx/resolver.rb +2 -2
- data/lib/httpx/response.rb +23 -14
- data/lib/httpx/selector.rb +5 -17
- data/lib/httpx/session.rb +7 -2
- data/lib/httpx/session2.rb +1 -1
- data/lib/httpx/timers.rb +84 -0
- data/lib/httpx/transcoder/body.rb +2 -1
- data/lib/httpx/transcoder/form.rb +1 -1
- data/lib/httpx/transcoder/json.rb +1 -1
- data/lib/httpx/utils.rb +8 -0
- data/lib/httpx/version.rb +1 -1
- data/lib/httpx.rb +1 -0
- data/sig/chainable.rbs +1 -0
- data/sig/connection.rbs +12 -6
- data/sig/plugins/aws_sdk_authentication.rbs +22 -4
- data/sig/plugins/response_cache.rbs +35 -0
- data/sig/plugins/retries.rbs +3 -0
- data/sig/pool.rbs +6 -0
- data/sig/resolver/native.rbs +3 -4
- data/sig/resolver/system.rbs +2 -0
- data/sig/response.rbs +3 -2
- data/sig/timers.rbs +32 -0
- data/sig/utils.rbs +4 -0
- metadata +10 -17
@@ -8,6 +8,23 @@ module HTTPX
|
|
8
8
|
# It requires the "aws-sdk-core" gem.
|
9
9
|
#
|
10
10
|
module AwsSdkAuthentication
|
11
|
+
# Mock configuration, to be used only when resolving credentials
|
12
|
+
class Configuration
|
13
|
+
attr_reader :profile
|
14
|
+
|
15
|
+
def initialize(profile)
|
16
|
+
@profile = profile
|
17
|
+
end
|
18
|
+
|
19
|
+
def respond_to_missing?(*)
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def method_missing(*)
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
11
28
|
#
|
12
29
|
# encapsulates access to an AWS SDK credentials store.
|
13
30
|
#
|
@@ -30,23 +47,8 @@ module HTTPX
|
|
30
47
|
end
|
31
48
|
|
32
49
|
class << self
|
33
|
-
attr_reader :credentials, :region
|
34
|
-
|
35
50
|
def load_dependencies(_klass)
|
36
51
|
require "aws-sdk-core"
|
37
|
-
|
38
|
-
client = Class.new(Seahorse::Client::Base) do
|
39
|
-
@identifier = :httpx
|
40
|
-
set_api(Aws::S3::ClientApi::API)
|
41
|
-
add_plugin(Aws::Plugins::CredentialsConfiguration)
|
42
|
-
add_plugin(Aws::Plugins::RegionalEndpoint)
|
43
|
-
class << self
|
44
|
-
attr_reader :identifier
|
45
|
-
end
|
46
|
-
end.new
|
47
|
-
|
48
|
-
@credentials = Credentials.new(client.config[:credentials])
|
49
|
-
@region = client.config[:region]
|
50
52
|
end
|
51
53
|
|
52
54
|
def configure(klass)
|
@@ -56,6 +58,26 @@ module HTTPX
|
|
56
58
|
def extra_options(options)
|
57
59
|
options.merge(max_concurrent_requests: 1)
|
58
60
|
end
|
61
|
+
|
62
|
+
def credentials(profile)
|
63
|
+
mock_configuration = Configuration.new(profile)
|
64
|
+
Credentials.new(Aws::CredentialProviderChain.new(mock_configuration).resolve)
|
65
|
+
end
|
66
|
+
|
67
|
+
def region(profile)
|
68
|
+
# https://github.com/aws/aws-sdk-ruby/blob/version-3/gems/aws-sdk-core/lib/aws-sdk-core/plugins/regional_endpoint.rb#L62
|
69
|
+
keys = %w[AWS_REGION AMAZON_REGION AWS_DEFAULT_REGION]
|
70
|
+
env_region = ENV.values_at(*keys).compact.first
|
71
|
+
env_region = nil if env_region == ""
|
72
|
+
cfg_region = Aws.shared_config.region(profile: profile)
|
73
|
+
env_region || cfg_region
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
module OptionsMethods
|
78
|
+
def option_aws_profile(value)
|
79
|
+
String(value)
|
80
|
+
end
|
59
81
|
end
|
60
82
|
|
61
83
|
module InstanceMethods
|
@@ -64,9 +86,11 @@ module HTTPX
|
|
64
86
|
# aws_authentication(credentials: Aws::Credentials.new('akid', 'secret'))
|
65
87
|
# aws_authentication()
|
66
88
|
#
|
67
|
-
def aws_sdk_authentication(
|
68
|
-
credentials
|
69
|
-
region
|
89
|
+
def aws_sdk_authentication(
|
90
|
+
credentials: AwsSdkAuthentication.credentials(@options.aws_profile),
|
91
|
+
region: AwsSdkAuthentication.region(@options.aws_profile),
|
92
|
+
**options
|
93
|
+
)
|
70
94
|
|
71
95
|
aws_sigv4_authentication(
|
72
96
|
credentials: credentials,
|
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "set"
|
4
|
-
require "aws-sdk-s3"
|
5
|
-
|
6
3
|
module HTTPX
|
7
4
|
module Plugins
|
8
5
|
#
|
@@ -75,16 +72,16 @@ module HTTPX
|
|
75
72
|
|
76
73
|
# canonical request
|
77
74
|
creq = "#{request.verb.to_s.upcase}" \
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
75
|
+
"\n#{request.canonical_path}" \
|
76
|
+
"\n#{request.canonical_query}" \
|
77
|
+
"\n#{canonical_headers}" \
|
78
|
+
"\n#{signed_headers}" \
|
79
|
+
"\n#{content_hashed}"
|
83
80
|
|
84
81
|
credential_scope = "#{date}" \
|
85
|
-
|
86
|
-
|
87
|
-
|
82
|
+
"/#{@region}" \
|
83
|
+
"/#{@service}" \
|
84
|
+
"/#{lower_provider_prefix}_request"
|
88
85
|
|
89
86
|
algo_line = "#{upper_provider_prefix}-HMAC-#{@algorithm}"
|
90
87
|
# string to sign
|
@@ -142,6 +139,7 @@ module HTTPX
|
|
142
139
|
|
143
140
|
class << self
|
144
141
|
def load_dependencies(*)
|
142
|
+
require "set"
|
145
143
|
require "digest/sha2"
|
146
144
|
require "openssl"
|
147
145
|
end
|
@@ -72,6 +72,8 @@ module HTTPX
|
|
72
72
|
end
|
73
73
|
|
74
74
|
module ResponseBodyMethods
|
75
|
+
using ArrayExtensions
|
76
|
+
|
75
77
|
attr_reader :encodings
|
76
78
|
|
77
79
|
def initialize(*)
|
@@ -90,7 +92,7 @@ module HTTPX
|
|
90
92
|
Float::INFINITY
|
91
93
|
end
|
92
94
|
|
93
|
-
@_inflaters = @headers.get("content-encoding").
|
95
|
+
@_inflaters = @headers.get("content-encoding").filter_map do |encoding|
|
94
96
|
next if encoding == "identity"
|
95
97
|
|
96
98
|
inflater = @options.encodings.registry(encoding).inflater(compressed_length)
|
@@ -100,7 +102,7 @@ module HTTPX
|
|
100
102
|
|
101
103
|
@encodings << encoding
|
102
104
|
inflater
|
103
|
-
end
|
105
|
+
end
|
104
106
|
|
105
107
|
# this can happen if the only declared encoding is "identity"
|
106
108
|
remove_instance_variable(:@_inflaters) if @_inflaters.empty?
|
@@ -134,7 +136,7 @@ module HTTPX
|
|
134
136
|
end
|
135
137
|
|
136
138
|
def each(&blk)
|
137
|
-
return enum_for(__method__) unless
|
139
|
+
return enum_for(__method__) unless blk
|
138
140
|
|
139
141
|
return deflate(&blk) if @buffer.size.zero?
|
140
142
|
|
data/lib/httpx/plugins/expect.rb
CHANGED
@@ -69,9 +69,14 @@ module HTTPX
|
|
69
69
|
end
|
70
70
|
|
71
71
|
module ConnectionMethods
|
72
|
-
def
|
72
|
+
def send_request_to_parser(request)
|
73
|
+
super
|
74
|
+
|
75
|
+
return unless request.headers["expect"] == "100-continue"
|
76
|
+
|
73
77
|
request.once(:expect) do
|
74
|
-
@timers.after(
|
78
|
+
@timers.after(request.options.expect_timeout) do
|
79
|
+
# expect timeout expired
|
75
80
|
if request.state == :expect && !request.expects?
|
76
81
|
Expect.no_expect_store << request.origin
|
77
82
|
request.headers.delete("expect")
|
@@ -79,7 +84,6 @@ module HTTPX
|
|
79
84
|
end
|
80
85
|
end
|
81
86
|
end
|
82
|
-
super
|
83
87
|
end
|
84
88
|
end
|
85
89
|
|
@@ -17,7 +17,7 @@ module HTTPX
|
|
17
17
|
|
18
18
|
# lazy decodes a grpc stream response
|
19
19
|
def stream(response, &block)
|
20
|
-
return enum_for(__method__, response) unless
|
20
|
+
return enum_for(__method__, response) unless block
|
21
21
|
|
22
22
|
response.each do |frame|
|
23
23
|
decode(frame, encodings: response.headers.get("grpc-encoding"), encoders: response.encoders, &block)
|
@@ -57,7 +57,7 @@ module HTTPX
|
|
57
57
|
|
58
58
|
yield data
|
59
59
|
|
60
|
-
message = message.byteslice(5 + size..-1)
|
60
|
+
message = message.byteslice((5 + size)..-1)
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
data/lib/httpx/plugins/grpc.rb
CHANGED
@@ -143,9 +143,9 @@ module HTTPX
|
|
143
143
|
|
144
144
|
session_class = Class.new(self.class) do
|
145
145
|
class_eval(<<-OUT, __FILE__, __LINE__ + 1)
|
146
|
-
def #{rpc_name}(input, **opts)
|
147
|
-
rpc_execute("#{rpc_name}", input, **opts)
|
148
|
-
end
|
146
|
+
def #{rpc_name}(input, **opts) # def grpc_action(input, **opts)
|
147
|
+
rpc_execute("#{rpc_name}", input, **opts) # rpc_execute("grpc_action", input, **opts)
|
148
|
+
end # end
|
149
149
|
OUT
|
150
150
|
end
|
151
151
|
|
@@ -44,6 +44,11 @@ module HTTPX
|
|
44
44
|
meter_elapsed_time("Session: initialized!!!")
|
45
45
|
end
|
46
46
|
|
47
|
+
def close(*)
|
48
|
+
super
|
49
|
+
meter_elapsed_time("Session -> close")
|
50
|
+
end
|
51
|
+
|
47
52
|
private
|
48
53
|
|
49
54
|
def build_requests(*)
|
@@ -55,11 +60,6 @@ module HTTPX
|
|
55
60
|
meter_elapsed_time("Session -> response") if response
|
56
61
|
response
|
57
62
|
end
|
58
|
-
|
59
|
-
def close(*)
|
60
|
-
super
|
61
|
-
meter_elapsed_time("Session -> close")
|
62
|
-
end
|
63
63
|
end
|
64
64
|
|
65
65
|
module RequestMethods
|
@@ -69,9 +69,9 @@ module HTTPX
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def transition(nextstate)
|
72
|
-
|
72
|
+
prev_state = @state
|
73
73
|
super
|
74
|
-
meter_elapsed_time("Request##{object_id}[#{@verb} #{@uri}: #{
|
74
|
+
meter_elapsed_time("Request##{object_id}[#{@verb} #{@uri}: #{prev_state}] -> #{@state}") if prev_state != @state
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
@@ -84,7 +84,7 @@ module HTTPX
|
|
84
84
|
def transition(nextstate)
|
85
85
|
state = @state
|
86
86
|
super
|
87
|
-
meter_elapsed_time("Connection[#{@origin}]: #{state} -> #{nextstate}") if nextstate == @state
|
87
|
+
meter_elapsed_time("Connection##{object_id}[#{@origin}]: #{state} -> #{nextstate}") if nextstate == @state
|
88
88
|
end
|
89
89
|
end
|
90
90
|
end
|
@@ -29,8 +29,8 @@ module HTTPX
|
|
29
29
|
# in order not to break legacy code, we'll keep loading http/form_data for them.
|
30
30
|
require "http/form_data"
|
31
31
|
warn "httpx: http/form_data is no longer a requirement to use HTTPX :multipart plugin. See migration instructions under" \
|
32
|
-
|
33
|
-
|
32
|
+
"https://honeyryderchuck.gitlab.io/httpx/wiki/Multipart-Uploads.html#notes. \n\n" \
|
33
|
+
"If you'd like to stop seeing this message, require 'http/form_data' yourself."
|
34
34
|
end
|
35
35
|
rescue LoadError
|
36
36
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "forwardable"
|
4
|
+
|
5
|
+
module HTTPX::Plugins
|
6
|
+
module ResponseCache
|
7
|
+
class Store
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegator :@store, :clear
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@store = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def lookup(uri)
|
17
|
+
@store[uri]
|
18
|
+
end
|
19
|
+
|
20
|
+
def cached?(uri)
|
21
|
+
@store.key?(uri)
|
22
|
+
end
|
23
|
+
|
24
|
+
def cache(uri, response)
|
25
|
+
@store[uri] = response
|
26
|
+
end
|
27
|
+
|
28
|
+
def prepare(request)
|
29
|
+
cached_response = @store[request.uri]
|
30
|
+
|
31
|
+
return unless cached_response
|
32
|
+
|
33
|
+
original_request = cached_response.instance_variable_get(:@request)
|
34
|
+
|
35
|
+
if (vary = cached_response.headers["vary"])
|
36
|
+
if vary == "*"
|
37
|
+
return unless request.headers.same_headers?(original_request.headers)
|
38
|
+
else
|
39
|
+
return unless vary.split(/ *, */).all? do |cache_field|
|
40
|
+
!original_request.headers.key?(cache_field) || request.headers[cache_field] == original_request.headers[cache_field]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
if !request.headers.key?("if-modified-since") && (last_modified = cached_response.headers["last-modified"])
|
46
|
+
request.headers.add("if-modified-since", last_modified)
|
47
|
+
end
|
48
|
+
|
49
|
+
if !request.headers.key?("if-none-match") && (etag = cached_response.headers["etag"]) # rubocop:disable Style/GuardClause
|
50
|
+
request.headers.add("if-none-match", etag)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module HTTPX
|
4
|
+
module Plugins
|
5
|
+
#
|
6
|
+
# This plugin adds support for retrying requests when certain errors happen.
|
7
|
+
#
|
8
|
+
# https://gitlab.com/honeyryderchuck/httpx/wikis/Response-Cache
|
9
|
+
#
|
10
|
+
module ResponseCache
|
11
|
+
CACHEABLE_VERBS = %i[get head].freeze
|
12
|
+
private_constant :CACHEABLE_VERBS
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def load_dependencies(*)
|
16
|
+
require_relative "response_cache/store"
|
17
|
+
end
|
18
|
+
|
19
|
+
def cacheable_request?(request)
|
20
|
+
CACHEABLE_VERBS.include?(request.verb)
|
21
|
+
end
|
22
|
+
|
23
|
+
def cacheable_response?(response)
|
24
|
+
response.is_a?(Response) &&
|
25
|
+
# partial responses shall not be cached, only full ones.
|
26
|
+
response.status != 206 && (
|
27
|
+
response.headers.key?("etag") || response.headers.key?("last-modified-at")
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def cached_response?(response)
|
32
|
+
response.is_a?(Response) && response.status == 304
|
33
|
+
end
|
34
|
+
|
35
|
+
def extra_options(options)
|
36
|
+
options.merge(response_cache_store: Store.new)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
module OptionsMethods
|
41
|
+
def option_response_cache_store(value)
|
42
|
+
raise TypeError, "must be an instance of #{Store}" unless value.is_a?(Store)
|
43
|
+
|
44
|
+
value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module InstanceMethods
|
49
|
+
def clear_response_cache
|
50
|
+
@options.response_cache_store.clear
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_request(*)
|
54
|
+
request = super
|
55
|
+
return request unless ResponseCache.cacheable_request?(request) && @options.response_cache_store.cached?(request.uri)
|
56
|
+
|
57
|
+
@options.response_cache_store.prepare(request)
|
58
|
+
|
59
|
+
request
|
60
|
+
end
|
61
|
+
|
62
|
+
def fetch_response(request, *)
|
63
|
+
response = super
|
64
|
+
|
65
|
+
if response && ResponseCache.cached_response?(response)
|
66
|
+
log { "returning cached response for #{request.uri}" }
|
67
|
+
cached_response = @options.response_cache_store.lookup(request.uri)
|
68
|
+
|
69
|
+
response.copy_from_cached(cached_response)
|
70
|
+
end
|
71
|
+
|
72
|
+
@options.response_cache_store.cache(request.uri, response) if response && ResponseCache.cacheable_response?(response)
|
73
|
+
|
74
|
+
response
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module ResponseMethods
|
79
|
+
def copy_from_cached(other)
|
80
|
+
@body = other.body
|
81
|
+
|
82
|
+
@body.__send__(:rewind)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
register_plugin :response_cache, ResponseCache
|
87
|
+
end
|
88
|
+
end
|
@@ -22,9 +22,16 @@ module HTTPX
|
|
22
22
|
Parser::Error,
|
23
23
|
Errno::EINVAL,
|
24
24
|
Errno::ETIMEDOUT].freeze
|
25
|
+
DEFAULT_JITTER = ->(interval) { interval * (0.5 * (1 + rand)) }
|
25
26
|
|
26
|
-
|
27
|
-
|
27
|
+
if ENV.key?("HTTPX_NO_JITTER")
|
28
|
+
def self.extra_options(options)
|
29
|
+
options.merge(max_retries: MAX_RETRIES)
|
30
|
+
end
|
31
|
+
else
|
32
|
+
def self.extra_options(options)
|
33
|
+
options.merge(max_retries: MAX_RETRIES, retry_jitter: DEFAULT_JITTER)
|
34
|
+
end
|
28
35
|
end
|
29
36
|
|
30
37
|
module OptionsMethods
|
@@ -38,6 +45,13 @@ module HTTPX
|
|
38
45
|
value
|
39
46
|
end
|
40
47
|
|
48
|
+
def option_retry_jitter(value)
|
49
|
+
# return early if callable
|
50
|
+
raise TypeError, ":retry_jitter must be callable" unless value.respond_to?(:call)
|
51
|
+
|
52
|
+
value
|
53
|
+
end
|
54
|
+
|
41
55
|
def option_max_retries(value)
|
42
56
|
num = Integer(value)
|
43
57
|
raise TypeError, ":max_retries must be positive" unless num.positive?
|
@@ -87,10 +101,15 @@ module HTTPX
|
|
87
101
|
retry_after = retry_after.call(request, response) if retry_after.respond_to?(:call)
|
88
102
|
|
89
103
|
if retry_after
|
104
|
+
# apply jitter
|
105
|
+
if (jitter = request.options.retry_jitter)
|
106
|
+
retry_after = jitter.call(retry_after)
|
107
|
+
end
|
90
108
|
|
109
|
+
retry_start = Utils.now
|
91
110
|
log { "retrying after #{retry_after} secs..." }
|
92
111
|
pool.after(retry_after) do
|
93
|
-
log { "retrying!!" }
|
112
|
+
log { "retrying (elapsed time: #{Utils.elapsed_time(retry_start)})!!" }
|
94
113
|
connection = find_connection(request, connections, options)
|
95
114
|
connection.send(request)
|
96
115
|
end
|
data/lib/httpx/plugins/stream.rb
CHANGED
data/lib/httpx/pool.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "forwardable"
|
4
|
-
require "timers"
|
5
4
|
require "httpx/selector"
|
6
5
|
require "httpx/connection"
|
7
6
|
require "httpx/resolver"
|
8
7
|
|
9
8
|
module HTTPX
|
10
9
|
class Pool
|
10
|
+
using ArrayExtensions
|
11
11
|
extend Forwardable
|
12
12
|
|
13
13
|
def_delegator :@timers, :after
|
@@ -15,7 +15,7 @@ module HTTPX
|
|
15
15
|
def initialize
|
16
16
|
@resolvers = {}
|
17
17
|
@_resolver_ios = {}
|
18
|
-
@timers = Timers
|
18
|
+
@timers = Timers.new
|
19
19
|
@selector = Selector.new
|
20
20
|
@connections = []
|
21
21
|
@connected_connections = 0
|
@@ -27,15 +27,18 @@ module HTTPX
|
|
27
27
|
|
28
28
|
def next_tick
|
29
29
|
catch(:jump_tick) do
|
30
|
-
timeout =
|
30
|
+
timeout = next_timeout
|
31
31
|
if timeout && timeout.negative?
|
32
32
|
@timers.fire
|
33
33
|
throw(:jump_tick)
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
|
-
|
38
|
-
|
36
|
+
begin
|
37
|
+
@selector.select(timeout, &:call)
|
38
|
+
@timers.fire
|
39
|
+
rescue TimeoutError => e
|
40
|
+
@timers.fire(e)
|
41
|
+
end
|
39
42
|
end
|
40
43
|
rescue StandardError => e
|
41
44
|
@connections.each do |connection|
|
@@ -64,6 +67,16 @@ module HTTPX
|
|
64
67
|
connection.on(:open) do
|
65
68
|
@connected_connections += 1
|
66
69
|
end
|
70
|
+
connection.on(:activate) do
|
71
|
+
select_connection(connection)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def deactivate(connections)
|
76
|
+
connections.each do |connection|
|
77
|
+
connection.deactivate
|
78
|
+
deselect_connection(connection) if connection.state == :inactive
|
79
|
+
end
|
67
80
|
end
|
68
81
|
|
69
82
|
# opens a connection to the IP reachable through +uri+.
|
@@ -81,7 +94,7 @@ module HTTPX
|
|
81
94
|
def resolve_connection(connection)
|
82
95
|
@connections << connection unless @connections.include?(connection)
|
83
96
|
|
84
|
-
if connection.addresses || connection.
|
97
|
+
if connection.addresses || connection.open?
|
85
98
|
#
|
86
99
|
# there are two cases in which we want to activate initialization of
|
87
100
|
# connection immediately:
|
@@ -98,7 +111,7 @@ module HTTPX
|
|
98
111
|
resolver << connection
|
99
112
|
return if resolver.empty?
|
100
113
|
|
101
|
-
@_resolver_ios[resolver] ||=
|
114
|
+
@_resolver_ios[resolver] ||= select_connection(resolver)
|
102
115
|
end
|
103
116
|
|
104
117
|
def on_resolver_connection(connection)
|
@@ -107,7 +120,7 @@ module HTTPX
|
|
107
120
|
end
|
108
121
|
return register_connection(connection) unless found_connection
|
109
122
|
|
110
|
-
if found_connection.
|
123
|
+
if found_connection.open?
|
111
124
|
coalesce_connections(found_connection, connection)
|
112
125
|
throw(:coalesced, found_connection)
|
113
126
|
else
|
@@ -129,7 +142,7 @@ module HTTPX
|
|
129
142
|
|
130
143
|
@resolvers.delete(resolver_type)
|
131
144
|
|
132
|
-
|
145
|
+
deselect_connection(resolver)
|
133
146
|
@_resolver_ios.delete(resolver)
|
134
147
|
resolver.close unless resolver.closed?
|
135
148
|
end
|
@@ -140,7 +153,7 @@ module HTTPX
|
|
140
153
|
# consider it connected already.
|
141
154
|
@connected_connections += 1
|
142
155
|
end
|
143
|
-
|
156
|
+
select_connection(connection)
|
144
157
|
connection.on(:close) do
|
145
158
|
unregister_connection(connection)
|
146
159
|
end
|
@@ -148,10 +161,18 @@ module HTTPX
|
|
148
161
|
|
149
162
|
def unregister_connection(connection)
|
150
163
|
@connections.delete(connection)
|
151
|
-
|
164
|
+
deselect_connection(connection)
|
152
165
|
@connected_connections -= 1
|
153
166
|
end
|
154
167
|
|
168
|
+
def select_connection(connection)
|
169
|
+
@selector.register(connection)
|
170
|
+
end
|
171
|
+
|
172
|
+
def deselect_connection(connection)
|
173
|
+
@selector.deregister(connection)
|
174
|
+
end
|
175
|
+
|
155
176
|
def coalesce_connections(conn1, conn2)
|
156
177
|
if conn1.coalescable?(conn2)
|
157
178
|
conn1.merge(conn2)
|
@@ -162,7 +183,11 @@ module HTTPX
|
|
162
183
|
end
|
163
184
|
|
164
185
|
def next_timeout
|
165
|
-
|
186
|
+
[
|
187
|
+
@timers.wait_interval,
|
188
|
+
*@resolvers.values.reject(&:closed?).filter_map(&:timeout),
|
189
|
+
*@connections.filter_map(&:timeout),
|
190
|
+
].compact.min
|
166
191
|
end
|
167
192
|
|
168
193
|
def find_resolver_for(connection)
|
@@ -172,6 +197,7 @@ module HTTPX
|
|
172
197
|
|
173
198
|
@resolvers[resolver_type] ||= begin
|
174
199
|
resolver = resolver_type.new(connection_options)
|
200
|
+
resolver.pool = self if resolver.respond_to?(:pool=)
|
175
201
|
resolver.on(:resolve, &method(:on_resolver_connection))
|
176
202
|
resolver.on(:error, &method(:on_resolver_error))
|
177
203
|
resolver.on(:close) { on_resolver_close(resolver) }
|
data/lib/httpx/request.rb
CHANGED
@@ -148,10 +148,10 @@ module HTTPX
|
|
148
148
|
# :nocov:
|
149
149
|
def inspect
|
150
150
|
"#<HTTPX::Request:#{object_id} " \
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
151
|
+
"#{@verb.to_s.upcase} " \
|
152
|
+
"#{uri} " \
|
153
|
+
"@headers=#{@headers} " \
|
154
|
+
"@body=#{@body}>"
|
155
155
|
end
|
156
156
|
# :nocov:
|
157
157
|
|
@@ -181,7 +181,7 @@ module HTTPX
|
|
181
181
|
end
|
182
182
|
|
183
183
|
def each(&block)
|
184
|
-
return enum_for(__method__) unless
|
184
|
+
return enum_for(__method__) unless block
|
185
185
|
return if @body.nil?
|
186
186
|
|
187
187
|
body = stream(@body)
|
@@ -236,7 +236,7 @@ module HTTPX
|
|
236
236
|
# :nocov:
|
237
237
|
def inspect
|
238
238
|
"#<HTTPX::Request::Body:#{object_id} " \
|
239
|
-
|
239
|
+
"#{unbounded_body? ? "stream" : "@bytesize=#{bytesize}"}>"
|
240
240
|
end
|
241
241
|
# :nocov:
|
242
242
|
end
|