excon 0.62.0 → 0.92.3
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 +5 -5
- data/CONTRIBUTING.md +0 -1
- data/LICENSE.md +1 -1
- data/README.md +7 -6
- data/data/cacert.pem +1220 -1828
- data/excon.gemspec +19 -3
- data/lib/excon/connection.rb +216 -139
- data/lib/excon/constants.rb +38 -13
- data/lib/excon/error.rb +15 -0
- data/lib/excon/headers.rb +4 -3
- data/lib/excon/instrumentors/logging_instrumentor.rb +5 -16
- data/lib/excon/instrumentors/standard_instrumentor.rb +2 -9
- data/lib/excon/middlewares/base.rb +6 -0
- data/lib/excon/middlewares/capture_cookies.rb +1 -1
- data/lib/excon/middlewares/decompress.rb +11 -4
- data/lib/excon/middlewares/expects.rb +7 -1
- data/lib/excon/middlewares/idempotent.rb +20 -3
- data/lib/excon/middlewares/instrumentor.rb +8 -0
- data/lib/excon/middlewares/mock.rb +12 -3
- data/lib/excon/middlewares/redirect_follower.rb +25 -3
- data/lib/excon/middlewares/response_parser.rb +3 -0
- data/lib/excon/pretty_printer.rb +1 -8
- data/lib/excon/response.rb +12 -9
- data/lib/excon/socket.rb +59 -42
- data/lib/excon/ssl_socket.rb +37 -15
- data/lib/excon/test/plugin/server/exec.rb +5 -2
- data/lib/excon/test/plugin/server/puma.rb +4 -1
- data/lib/excon/test/plugin/server/unicorn.rb +5 -0
- data/lib/excon/test/plugin/server/webrick.rb +4 -1
- data/lib/excon/test/server.rb +1 -1
- data/lib/excon/unix_socket.rb +1 -0
- data/lib/excon/utils.rb +59 -5
- data/lib/excon/version.rb +1 -1
- data/lib/excon.rb +25 -17
- metadata +27 -98
- data/.document +0 -5
- data/.gitignore +0 -13
- data/.rspec +0 -3
- data/.travis.yml +0 -29
- data/Gemfile +0 -19
- data/Rakefile +0 -41
- data/benchmarks/class_vs_lambda.rb +0 -50
- data/benchmarks/concat_vs_insert.rb +0 -21
- data/benchmarks/concat_vs_interpolate.rb +0 -22
- data/benchmarks/cr_lf.rb +0 -21
- data/benchmarks/downcase-eq-eq_vs_casecmp.rb +0 -169
- data/benchmarks/excon.rb +0 -69
- data/benchmarks/excon_vs.rb +0 -165
- data/benchmarks/for_vs_array_each.rb +0 -27
- data/benchmarks/for_vs_hash_each.rb +0 -27
- data/benchmarks/has_key-vs-lookup.rb +0 -177
- data/benchmarks/headers_case_sensitivity.rb +0 -83
- data/benchmarks/headers_split_vs_match.rb +0 -34
- data/benchmarks/implicit_block-vs-explicit_block.rb +0 -98
- data/benchmarks/merging.rb +0 -21
- data/benchmarks/single_vs_double_quotes.rb +0 -21
- data/benchmarks/string_ranged_index.rb +0 -87
- data/benchmarks/strip_newline.rb +0 -115
- data/benchmarks/vs_stdlib.rb +0 -82
- data/changelog.txt +0 -1083
- data/spec/excon/error_spec.rb +0 -139
- data/spec/excon/test/server_spec.rb +0 -28
- data/spec/excon_spec.rb +0 -7
- data/spec/helpers/file_path_helpers.rb +0 -22
- data/spec/requests/basic_spec.rb +0 -40
- data/spec/requests/eof_requests_spec.rb +0 -36
- data/spec/requests/unix_socket_spec.rb +0 -46
- data/spec/spec_helper.rb +0 -24
- data/spec/support/shared_contexts/test_server_context.rb +0 -83
- data/spec/support/shared_examples/shared_example_for_clients.rb +0 -218
- data/spec/support/shared_examples/shared_example_for_streaming_clients.rb +0 -20
- data/spec/support/shared_examples/shared_example_for_test_servers.rb +0 -16
- data/tests/authorization_header_tests.rb +0 -29
- data/tests/bad_tests.rb +0 -47
- data/tests/basic_tests.rb +0 -351
- data/tests/batch_requests.rb +0 -133
- data/tests/complete_responses.rb +0 -31
- data/tests/data/127.0.0.1.cert.crt +0 -20
- data/tests/data/127.0.0.1.cert.key +0 -27
- data/tests/data/excon.cert.crt +0 -20
- data/tests/data/excon.cert.key +0 -27
- data/tests/data/xs +0 -1
- data/tests/error_tests.rb +0 -145
- data/tests/header_tests.rb +0 -119
- data/tests/middlewares/canned_response_tests.rb +0 -34
- data/tests/middlewares/capture_cookies_tests.rb +0 -34
- data/tests/middlewares/decompress_tests.rb +0 -157
- data/tests/middlewares/escape_path_tests.rb +0 -36
- data/tests/middlewares/idempotent_tests.rb +0 -206
- data/tests/middlewares/instrumentation_tests.rb +0 -315
- data/tests/middlewares/mock_tests.rb +0 -304
- data/tests/middlewares/redirect_follower_tests.rb +0 -112
- data/tests/pipeline_tests.rb +0 -40
- data/tests/proxy_tests.rb +0 -306
- data/tests/query_string_tests.rb +0 -87
- data/tests/rackups/basic.rb +0 -41
- data/tests/rackups/basic.ru +0 -3
- data/tests/rackups/basic_auth.ru +0 -14
- data/tests/rackups/deflater.ru +0 -4
- data/tests/rackups/proxy.ru +0 -18
- data/tests/rackups/query_string.ru +0 -13
- data/tests/rackups/redirecting.ru +0 -23
- data/tests/rackups/redirecting_with_cookie.ru +0 -40
- data/tests/rackups/request_headers.ru +0 -15
- data/tests/rackups/request_methods.ru +0 -21
- data/tests/rackups/response_header.ru +0 -18
- data/tests/rackups/ssl.ru +0 -16
- data/tests/rackups/ssl_mismatched_cn.ru +0 -15
- data/tests/rackups/ssl_verify_peer.ru +0 -16
- data/tests/rackups/streaming.ru +0 -30
- data/tests/rackups/thread_safety.ru +0 -17
- data/tests/rackups/timeout.ru +0 -14
- data/tests/rackups/webrick_patch.rb +0 -34
- data/tests/request_headers_tests.rb +0 -21
- data/tests/request_method_tests.rb +0 -47
- data/tests/request_tests.rb +0 -59
- data/tests/response_tests.rb +0 -197
- data/tests/servers/bad.rb +0 -20
- data/tests/servers/eof.rb +0 -17
- data/tests/servers/error.rb +0 -20
- data/tests/servers/good.rb +0 -350
- data/tests/test_helper.rb +0 -306
- data/tests/thread_safety_tests.rb +0 -39
- data/tests/timeout_tests.rb +0 -12
- data/tests/utils_tests.rb +0 -81
data/lib/excon/constants.rb
CHANGED
|
@@ -12,8 +12,16 @@ module Excon
|
|
|
12
12
|
CHUNK_SIZE = DEFAULT_CHUNK_SIZE
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
+
DEFAULT_REDIRECT_LIMIT = 10
|
|
16
|
+
|
|
15
17
|
DEFAULT_RETRY_LIMIT = 4
|
|
16
18
|
|
|
19
|
+
DEFAULT_RETRY_ERRORS = [
|
|
20
|
+
Excon::Error::Timeout,
|
|
21
|
+
Excon::Error::Socket,
|
|
22
|
+
Excon::Error::HTTPStatus
|
|
23
|
+
]
|
|
24
|
+
|
|
17
25
|
FORCE_ENC = CR_NL.respond_to?(:force_encoding)
|
|
18
26
|
|
|
19
27
|
HTTP_1_1 = " HTTP/1.1\r\n"
|
|
@@ -33,19 +41,17 @@ module Excon
|
|
|
33
41
|
VERSIONS = "#{USER_AGENT} (#{RUBY_PLATFORM}) ruby/#{RUBY_VERSION}"
|
|
34
42
|
|
|
35
43
|
VALID_REQUEST_KEYS = [
|
|
44
|
+
:allow_unstubbed_requests,
|
|
36
45
|
:body,
|
|
37
|
-
:captures,
|
|
38
46
|
:chunk_size,
|
|
39
47
|
:debug_request,
|
|
40
48
|
:debug_response,
|
|
41
|
-
:expects,
|
|
42
49
|
:headers,
|
|
43
|
-
:
|
|
44
|
-
:
|
|
45
|
-
:instrumentor_name,
|
|
50
|
+
:instrumentor, # Used for setting logging within Connection
|
|
51
|
+
:logger,
|
|
46
52
|
:method,
|
|
47
53
|
:middlewares,
|
|
48
|
-
:
|
|
54
|
+
:password,
|
|
49
55
|
:path,
|
|
50
56
|
:persistent,
|
|
51
57
|
:pipeline,
|
|
@@ -53,9 +59,8 @@ module Excon
|
|
|
53
59
|
:read_timeout,
|
|
54
60
|
:request_block,
|
|
55
61
|
:response_block,
|
|
56
|
-
:
|
|
57
|
-
:
|
|
58
|
-
:retry_interval,
|
|
62
|
+
:stubs,
|
|
63
|
+
:user,
|
|
59
64
|
:versions,
|
|
60
65
|
:write_timeout
|
|
61
66
|
]
|
|
@@ -74,12 +79,12 @@ module Excon
|
|
|
74
79
|
:private_key_path,
|
|
75
80
|
:connect_timeout,
|
|
76
81
|
:family,
|
|
82
|
+
:keepalive,
|
|
77
83
|
:host,
|
|
78
84
|
:hostname,
|
|
79
85
|
:omit_default_port,
|
|
80
86
|
:nonblock,
|
|
81
87
|
:reuseaddr,
|
|
82
|
-
:password,
|
|
83
88
|
:port,
|
|
84
89
|
:proxy,
|
|
85
90
|
:scheme,
|
|
@@ -90,13 +95,30 @@ module Excon
|
|
|
90
95
|
:ssl_verify_callback,
|
|
91
96
|
:ssl_verify_peer,
|
|
92
97
|
:ssl_verify_peer_host,
|
|
98
|
+
:ssl_verify_hostname,
|
|
93
99
|
:ssl_version,
|
|
100
|
+
:ssl_min_version,
|
|
101
|
+
:ssl_max_version,
|
|
102
|
+
:ssl_security_level,
|
|
103
|
+
:ssl_proxy_headers,
|
|
104
|
+
:ssl_uri_schemes,
|
|
94
105
|
:tcp_nodelay,
|
|
95
106
|
:thread_safe_sockets,
|
|
96
107
|
:uri_parser,
|
|
97
|
-
:user
|
|
98
108
|
]
|
|
99
109
|
|
|
110
|
+
DEPRECATED_VALID_REQUEST_KEYS = {
|
|
111
|
+
:captures => 'Mock',
|
|
112
|
+
:expects => 'Expects',
|
|
113
|
+
:idempotent => 'Idempotent',
|
|
114
|
+
:instrumentor_name => 'Instrumentor',
|
|
115
|
+
:mock => 'Mock',
|
|
116
|
+
:retries_remaining => 'Idempotent', # referenced in Instrumentor, but only relevant with Idempotent
|
|
117
|
+
:retry_errors => 'Idempotent',
|
|
118
|
+
:retry_interval => 'Idempotent',
|
|
119
|
+
:retry_limit => 'Idempotent' # referenced in Instrumentor, but only relevant with Idempotent
|
|
120
|
+
}
|
|
121
|
+
|
|
100
122
|
unless ::IO.const_defined?(:WaitReadable)
|
|
101
123
|
class ::IO
|
|
102
124
|
module WaitReadable; end
|
|
@@ -112,12 +134,14 @@ module Excon
|
|
|
112
134
|
DEFAULTS = {
|
|
113
135
|
:chunk_size => CHUNK_SIZE || DEFAULT_CHUNK_SIZE,
|
|
114
136
|
# see https://wiki.mozilla.org/Security/Server_Side_TLS#Intermediate_compatibility_.28default.29
|
|
115
|
-
|
|
137
|
+
# list provided then had DES related things sorted to the end
|
|
138
|
+
:ciphers => 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:DES-CBC3-SHA:!DSS',
|
|
116
139
|
:connect_timeout => 60,
|
|
117
140
|
:debug_request => false,
|
|
118
141
|
:debug_response => false,
|
|
119
142
|
:headers => {
|
|
120
|
-
'User-Agent' => USER_AGENT
|
|
143
|
+
'User-Agent' => USER_AGENT,
|
|
144
|
+
'Accept' => '*/*'
|
|
121
145
|
},
|
|
122
146
|
:idempotent => false,
|
|
123
147
|
:instrumentor_name => 'excon',
|
|
@@ -133,6 +157,7 @@ module Excon
|
|
|
133
157
|
:omit_default_port => false,
|
|
134
158
|
:persistent => false,
|
|
135
159
|
:read_timeout => 60,
|
|
160
|
+
:retry_errors => DEFAULT_RETRY_ERRORS,
|
|
136
161
|
:retry_limit => DEFAULT_RETRY_LIMIT,
|
|
137
162
|
:ssl_verify_peer => true,
|
|
138
163
|
:ssl_uri_schemes => [HTTPS],
|
data/lib/excon/error.rb
CHANGED
|
@@ -6,6 +6,7 @@ module Excon
|
|
|
6
6
|
|
|
7
7
|
class StubNotFound < Error; end
|
|
8
8
|
class InvalidStub < Error; end
|
|
9
|
+
class Warning < Error; end
|
|
9
10
|
|
|
10
11
|
# Socket related errors
|
|
11
12
|
class Socket < Error
|
|
@@ -45,9 +46,23 @@ or:
|
|
|
45
46
|
end
|
|
46
47
|
end
|
|
47
48
|
|
|
49
|
+
class InvalidHeaderKey < Error; end
|
|
50
|
+
class InvalidHeaderValue < Error; end
|
|
48
51
|
class Timeout < Error; end
|
|
49
52
|
class ResponseParse < Error; end
|
|
53
|
+
|
|
54
|
+
class ProxyConnectionError < Error
|
|
55
|
+
attr_reader :request, :response
|
|
56
|
+
|
|
57
|
+
def initialize(msg, request = nil, response = nil)
|
|
58
|
+
super(msg)
|
|
59
|
+
@request = request
|
|
60
|
+
@response = response
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
50
64
|
class ProxyParse < Error; end
|
|
65
|
+
class TooManyRedirects < Error; end
|
|
51
66
|
|
|
52
67
|
# Base class for HTTP Error classes
|
|
53
68
|
class HTTPStatus < Error
|
data/lib/excon/headers.rb
CHANGED
|
@@ -22,6 +22,7 @@ module Excon
|
|
|
22
22
|
alias_method :raw_values_at, :values_at
|
|
23
23
|
|
|
24
24
|
def initialize
|
|
25
|
+
super
|
|
25
26
|
@downcased = {}
|
|
26
27
|
end
|
|
27
28
|
|
|
@@ -29,11 +30,11 @@ module Excon
|
|
|
29
30
|
@downcased[key.to_s.downcase]
|
|
30
31
|
end
|
|
31
32
|
|
|
32
|
-
alias_method :[]=, :store
|
|
33
33
|
def []=(key, value)
|
|
34
34
|
raw_writer(key, value)
|
|
35
35
|
@downcased[key.to_s.downcase] = value
|
|
36
36
|
end
|
|
37
|
+
alias_method :store, :[]=
|
|
37
38
|
|
|
38
39
|
if SENTINEL.respond_to? :assoc
|
|
39
40
|
def assoc(obj)
|
|
@@ -54,11 +55,11 @@ module Excon
|
|
|
54
55
|
end
|
|
55
56
|
end
|
|
56
57
|
|
|
57
|
-
alias_method :has_key?, :key?
|
|
58
|
-
alias_method :has_key?, :member?
|
|
59
58
|
def has_key?(key)
|
|
60
59
|
raw_key?(key) || @downcased.has_key?(key.to_s.downcase)
|
|
61
60
|
end
|
|
61
|
+
alias_method :key?, :has_key?
|
|
62
|
+
alias_method :member?, :has_key?
|
|
62
63
|
|
|
63
64
|
def merge(other_hash)
|
|
64
65
|
self.dup.merge!(other_hash)
|
|
@@ -2,23 +2,12 @@ require 'logger'
|
|
|
2
2
|
|
|
3
3
|
module Excon
|
|
4
4
|
class LoggingInstrumentor
|
|
5
|
-
# Returns the Logger object for the LoggingInstrumentor. If one doesn't
|
|
6
|
-
# already exist, then one will be created using $stderr as the output
|
|
7
|
-
# stream.
|
|
8
|
-
#
|
|
9
|
-
def self.logger
|
|
10
|
-
@logger ||= Logger.new($stderr)
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# Sets the logger object for the LoggingInstrumentor.
|
|
14
|
-
#
|
|
15
|
-
def self.logger=(logger)
|
|
16
|
-
@logger = logger
|
|
17
|
-
end
|
|
18
5
|
|
|
19
|
-
def self.instrument(name, params = {}
|
|
6
|
+
def self.instrument(name, params = {})
|
|
20
7
|
params = params.dup
|
|
21
8
|
|
|
9
|
+
logger = params[:logger] || Logger.new($stderr)
|
|
10
|
+
|
|
22
11
|
# reduce duplication/noise of output
|
|
23
12
|
params.delete(:connection)
|
|
24
13
|
params.delete(:stack)
|
|
@@ -39,7 +28,7 @@ module Excon
|
|
|
39
28
|
info << "?"
|
|
40
29
|
|
|
41
30
|
if params[:query].is_a?(Hash)
|
|
42
|
-
info << params.to_a.map{ |key,value| "#{key}=#{value}" }.join('&')
|
|
31
|
+
info << params[:query].to_a.map { |key,value| "#{key}=#{value}" }.join('&')
|
|
43
32
|
else
|
|
44
33
|
info << params[:query]
|
|
45
34
|
end
|
|
@@ -51,7 +40,7 @@ module Excon
|
|
|
51
40
|
end
|
|
52
41
|
end
|
|
53
42
|
|
|
54
|
-
|
|
43
|
+
logger.info(info) if info
|
|
55
44
|
|
|
56
45
|
yield if block_given?
|
|
57
46
|
end
|
|
@@ -1,21 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
module Excon
|
|
3
3
|
class StandardInstrumentor
|
|
4
|
-
def self.instrument(name, params = {}
|
|
4
|
+
def self.instrument(name, params = {})
|
|
5
5
|
params = params.dup
|
|
6
6
|
|
|
7
7
|
# reduce duplication/noise of output
|
|
8
8
|
params.delete(:connection)
|
|
9
9
|
params.delete(:stack)
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
params[:headers] = params[:headers].dup
|
|
13
|
-
params[:headers]['Authorization'] = REDACTED
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
if params.has_key?(:password)
|
|
17
|
-
params[:password] = REDACTED
|
|
18
|
-
end
|
|
11
|
+
params = Utils.redact(params)
|
|
19
12
|
|
|
20
13
|
$stderr.puts(name)
|
|
21
14
|
Excon::PrettyPrinter.pp($stderr, params)
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
module Excon
|
|
3
3
|
module Middleware
|
|
4
4
|
class Base
|
|
5
|
+
# Returns the list of parameters that this middleware uses that are valid
|
|
6
|
+
# as arguments to `Connection#request` or `Connection#new`.
|
|
7
|
+
def self.valid_parameter_keys
|
|
8
|
+
[]
|
|
9
|
+
end
|
|
10
|
+
|
|
5
11
|
def initialize(stack)
|
|
6
12
|
@stack = stack
|
|
7
13
|
end
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
module Excon
|
|
3
3
|
module Middleware
|
|
4
4
|
class Decompress < Excon::Middleware::Base
|
|
5
|
+
|
|
6
|
+
INFLATE_ZLIB_OR_GZIP = 47 # Zlib::MAX_WBITS + 32
|
|
7
|
+
INFLATE_RAW = -15 # Zlib::MAX_WBITS * -1
|
|
8
|
+
|
|
5
9
|
def request_call(datum)
|
|
6
10
|
unless datum.has_key?(:response_block)
|
|
7
11
|
key = datum[:headers].keys.detect {|k| k.to_s.casecmp('Accept-Encoding') == 0 } || 'Accept-Encoding'
|
|
@@ -15,12 +19,15 @@ module Excon
|
|
|
15
19
|
def response_call(datum)
|
|
16
20
|
body = datum[:response][:body]
|
|
17
21
|
unless datum.has_key?(:response_block) || body.nil? || body.empty?
|
|
18
|
-
if key = datum[:response][:headers].keys.detect {|k| k.casecmp('Content-Encoding') == 0 }
|
|
22
|
+
if (key = datum[:response][:headers].keys.detect {|k| k.casecmp('Content-Encoding') == 0 })
|
|
19
23
|
encodings = Utils.split_header_value(datum[:response][:headers][key])
|
|
20
|
-
if encoding = encodings.last
|
|
24
|
+
if (encoding = encodings.last)
|
|
21
25
|
if encoding.casecmp('deflate') == 0
|
|
22
|
-
|
|
23
|
-
|
|
26
|
+
datum[:response][:body] = begin
|
|
27
|
+
Zlib::Inflate.new(INFLATE_ZLIB_OR_GZIP).inflate(body)
|
|
28
|
+
rescue Zlib::DataError # fallback to raw on error
|
|
29
|
+
Zlib::Inflate.new(INFLATE_RAW).inflate(body)
|
|
30
|
+
end
|
|
24
31
|
encodings.pop
|
|
25
32
|
elsif encoding.casecmp('gzip') == 0 || encoding.casecmp('x-gzip') == 0
|
|
26
33
|
datum[:response][:body] = Zlib::GzipReader.new(StringIO.new(body)).read
|
|
@@ -2,11 +2,17 @@
|
|
|
2
2
|
module Excon
|
|
3
3
|
module Middleware
|
|
4
4
|
class Expects < Excon::Middleware::Base
|
|
5
|
+
def self.valid_parameter_keys
|
|
6
|
+
[
|
|
7
|
+
:expects
|
|
8
|
+
]
|
|
9
|
+
end
|
|
10
|
+
|
|
5
11
|
def response_call(datum)
|
|
6
12
|
if datum.has_key?(:expects) && ![*datum[:expects]].include?(datum[:response][:status])
|
|
7
13
|
raise(
|
|
8
14
|
Excon::Errors.status_error(
|
|
9
|
-
datum.reject {|key,
|
|
15
|
+
datum.reject {|key,_| key == :response},
|
|
10
16
|
Excon::Response.new(datum[:response])
|
|
11
17
|
)
|
|
12
18
|
)
|
|
@@ -1,7 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
|
+
require 'set'
|
|
3
|
+
|
|
2
4
|
module Excon
|
|
3
5
|
module Middleware
|
|
4
6
|
class Idempotent < Excon::Middleware::Base
|
|
7
|
+
def self.valid_parameter_keys
|
|
8
|
+
[
|
|
9
|
+
:idempotent,
|
|
10
|
+
:retries_remaining,
|
|
11
|
+
:retry_errors,
|
|
12
|
+
:retry_interval,
|
|
13
|
+
:retry_limit
|
|
14
|
+
]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def request_call(datum)
|
|
18
|
+
datum[:retries_remaining] ||= datum[:retry_limit]
|
|
19
|
+
@stack.request_call(datum)
|
|
20
|
+
end
|
|
21
|
+
|
|
5
22
|
def error_call(datum)
|
|
6
23
|
if datum[:idempotent]
|
|
7
24
|
if datum.has_key?(:request_block)
|
|
@@ -21,15 +38,15 @@ module Excon
|
|
|
21
38
|
end
|
|
22
39
|
end
|
|
23
40
|
|
|
24
|
-
if datum[:idempotent] && [
|
|
25
|
-
Excon::Errors::HTTPStatusError].any? {|ex| datum[:error].kind_of?(ex) } && datum[:retries_remaining] > 1
|
|
41
|
+
if datum[:idempotent] && datum[:retry_errors].any? {|ex| datum[:error].kind_of?(ex) } && datum[:retries_remaining] > 1
|
|
26
42
|
|
|
27
43
|
sleep(datum[:retry_interval]) if datum[:retry_interval]
|
|
28
44
|
|
|
29
45
|
# reduces remaining retries, reset connection, and restart request_call
|
|
30
46
|
datum[:retries_remaining] -= 1
|
|
31
47
|
connection = datum.delete(:connection)
|
|
32
|
-
|
|
48
|
+
valid_keys = Set.new(connection.valid_request_keys(datum[:middlewares]))
|
|
49
|
+
datum.select! {|key, _| valid_keys.include?(key) }
|
|
33
50
|
connection.request(datum)
|
|
34
51
|
else
|
|
35
52
|
@stack.error_call(datum)
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
module Excon
|
|
3
3
|
module Middleware
|
|
4
4
|
class Instrumentor < Excon::Middleware::Base
|
|
5
|
+
def self.valid_parameter_keys
|
|
6
|
+
[
|
|
7
|
+
:logger,
|
|
8
|
+
:instrumentor,
|
|
9
|
+
:instrumentor_name
|
|
10
|
+
]
|
|
11
|
+
end
|
|
12
|
+
|
|
5
13
|
def error_call(datum)
|
|
6
14
|
if datum.has_key?(:instrumentor)
|
|
7
15
|
datum[:instrumentor].instrument("#{datum[:instrumentor_name]}.error", :error => datum[:error]) do
|
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
module Excon
|
|
3
3
|
module Middleware
|
|
4
4
|
class Mock < Excon::Middleware::Base
|
|
5
|
+
def self.valid_parameter_keys
|
|
6
|
+
[
|
|
7
|
+
:allow_unstubbed_requests,
|
|
8
|
+
:captures,
|
|
9
|
+
:mock
|
|
10
|
+
]
|
|
11
|
+
end
|
|
12
|
+
|
|
5
13
|
def request_call(datum)
|
|
6
14
|
if datum[:mock]
|
|
7
15
|
# convert File/Tempfile body to string before matching:
|
|
@@ -17,12 +25,13 @@ module Excon
|
|
|
17
25
|
raise Excon::Errors::InvalidStub.new("Request body should be a string or an IO object. #{datum[:body].class} provided")
|
|
18
26
|
end
|
|
19
27
|
|
|
20
|
-
if stub = Excon.stub_for(datum)
|
|
28
|
+
if (stub = Excon.stub_for(datum))
|
|
29
|
+
datum[:remote_ip] ||= '127.0.0.1'
|
|
21
30
|
datum[:response] = {
|
|
22
31
|
:body => '',
|
|
23
32
|
:headers => {},
|
|
24
33
|
:status => 200,
|
|
25
|
-
:remote_ip =>
|
|
34
|
+
:remote_ip => datum[:remote_ip]
|
|
26
35
|
}
|
|
27
36
|
|
|
28
37
|
stub_datum = case stub.last
|
|
@@ -32,7 +41,7 @@ module Excon
|
|
|
32
41
|
stub.last
|
|
33
42
|
end
|
|
34
43
|
|
|
35
|
-
datum[:response].merge!(stub_datum.reject {|key,
|
|
44
|
+
datum[:response].merge!(stub_datum.reject {|key,_| key == :headers})
|
|
36
45
|
if stub_datum.has_key?(:headers)
|
|
37
46
|
datum[:response][:headers].merge!(stub_datum[:headers])
|
|
38
47
|
end
|
|
@@ -2,9 +2,21 @@
|
|
|
2
2
|
module Excon
|
|
3
3
|
module Middleware
|
|
4
4
|
class RedirectFollower < Excon::Middleware::Base
|
|
5
|
+
def self.valid_parameter_keys
|
|
6
|
+
[
|
|
7
|
+
:redirects_remaining,
|
|
8
|
+
:redirect_limit
|
|
9
|
+
]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def request_call(datum)
|
|
13
|
+
datum[:redirects_remaining] ||= datum[:redirect_limit] ||
|
|
14
|
+
Excon::DEFAULT_REDIRECT_LIMIT
|
|
15
|
+
@stack.request_call(datum)
|
|
16
|
+
end
|
|
5
17
|
|
|
6
18
|
def get_header(datum, header)
|
|
7
|
-
_, header_value = datum[:response][:headers].detect do |key,
|
|
19
|
+
_, header_value = datum[:response][:headers].detect do |key, _|
|
|
8
20
|
key.casecmp(header) == 0
|
|
9
21
|
end
|
|
10
22
|
header_value
|
|
@@ -14,14 +26,21 @@ module Excon
|
|
|
14
26
|
if datum.has_key?(:response)
|
|
15
27
|
case datum[:response][:status]
|
|
16
28
|
when 301, 302, 303, 307, 308
|
|
29
|
+
if datum[:redirects_remaining] <= 0
|
|
30
|
+
raise Excon::Errors::TooManyRedirects
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
datum[:redirects_remaining] -= 1
|
|
34
|
+
|
|
17
35
|
uri_parser = datum[:uri_parser] || Excon.defaults[:uri_parser]
|
|
18
36
|
|
|
19
37
|
location = get_header(datum, 'Location')
|
|
20
38
|
base_uri = Excon::Utils.request_uri(datum)
|
|
21
39
|
uri = uri_parser.join(base_uri, location)
|
|
22
40
|
|
|
23
|
-
# delete old/redirect response
|
|
41
|
+
# delete old/redirect response and remote_ip
|
|
24
42
|
response = datum.delete(:response)
|
|
43
|
+
datum.delete(:remote_ip)
|
|
25
44
|
|
|
26
45
|
params = datum.dup
|
|
27
46
|
params.delete(:connection)
|
|
@@ -52,7 +71,10 @@ module Excon
|
|
|
52
71
|
params.merge!(:password => Utils.unescape_uri(uri.password)) if uri.password
|
|
53
72
|
|
|
54
73
|
response = Excon::Connection.new(params).request
|
|
55
|
-
datum.merge!({
|
|
74
|
+
datum.merge!({
|
|
75
|
+
:remote_ip => response.remote_ip,
|
|
76
|
+
:response => response.data
|
|
77
|
+
})
|
|
56
78
|
else
|
|
57
79
|
@stack.response_call(datum)
|
|
58
80
|
end
|
data/lib/excon/pretty_printer.rb
CHANGED
|
@@ -9,14 +9,7 @@ module Excon
|
|
|
9
9
|
datum.delete(:connection)
|
|
10
10
|
datum.delete(:stack)
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
datum[:headers] = datum[:headers].dup
|
|
14
|
-
datum[:headers]['Authorization'] = REDACTED
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
if datum.has_key?(:password)
|
|
18
|
-
datum[:password] = REDACTED
|
|
19
|
-
end
|
|
12
|
+
datum = Utils.redact(datum)
|
|
20
13
|
end
|
|
21
14
|
|
|
22
15
|
indent += 2
|
data/lib/excon/response.rb
CHANGED
|
@@ -59,10 +59,13 @@ module Excon
|
|
|
59
59
|
|
|
60
60
|
def self.parse(socket, datum)
|
|
61
61
|
# this will discard any trailing lines from the previous response if any.
|
|
62
|
-
|
|
62
|
+
line = nil
|
|
63
|
+
loop do
|
|
63
64
|
line = socket.readline
|
|
64
|
-
|
|
65
|
+
break if line[9,3].to_i != 0
|
|
66
|
+
end
|
|
65
67
|
|
|
68
|
+
status = line[9, 3].to_i
|
|
66
69
|
reason_phrase = line[13..-3] # -3 strips the trailing "\r\n"
|
|
67
70
|
|
|
68
71
|
datum[:response] = {
|
|
@@ -90,7 +93,7 @@ module Excon
|
|
|
90
93
|
|
|
91
94
|
unless (['HEAD', 'CONNECT'].include?(datum[:method].to_s.upcase)) || NO_ENTITY.include?(datum[:response][:status])
|
|
92
95
|
|
|
93
|
-
if key = datum[:response][:headers].keys.detect {|k| k.casecmp('Transfer-Encoding') == 0 }
|
|
96
|
+
if (key = datum[:response][:headers].keys.detect {|k| k.casecmp('Transfer-Encoding') == 0 })
|
|
94
97
|
encodings = Utils.split_header_value(datum[:response][:headers][key])
|
|
95
98
|
if (encoding = encodings.last) && encoding.casecmp('chunked') == 0
|
|
96
99
|
transfer_encoding_chunked = true
|
|
@@ -103,7 +106,7 @@ module Excon
|
|
|
103
106
|
end
|
|
104
107
|
|
|
105
108
|
# use :response_block unless :expects would fail
|
|
106
|
-
if response_block = datum[:response_block]
|
|
109
|
+
if (response_block = datum[:response_block])
|
|
107
110
|
if datum[:middlewares].include?(Excon::Middleware::Expects) && datum[:expects] &&
|
|
108
111
|
!Array(datum[:expects]).include?(datum[:response][:status])
|
|
109
112
|
response_block = nil
|
|
@@ -140,11 +143,11 @@ module Excon
|
|
|
140
143
|
end
|
|
141
144
|
parse_headers(socket, datum) # merge trailers into headers
|
|
142
145
|
else
|
|
143
|
-
if key = datum[:response][:headers].keys.detect {|k| k.casecmp('Content-Length') == 0 }
|
|
146
|
+
if (key = datum[:response][:headers].keys.detect {|k| k.casecmp('Content-Length') == 0 })
|
|
144
147
|
content_length = datum[:response][:headers][key].to_i
|
|
145
148
|
end
|
|
146
149
|
|
|
147
|
-
if remaining = content_length
|
|
150
|
+
if (remaining = content_length)
|
|
148
151
|
if response_block
|
|
149
152
|
while remaining > 0
|
|
150
153
|
chunk = socket.read([datum[:chunk_size], remaining].min) || raise(EOFError)
|
|
@@ -160,11 +163,11 @@ module Excon
|
|
|
160
163
|
end
|
|
161
164
|
else
|
|
162
165
|
if response_block
|
|
163
|
-
while chunk = socket.read(datum[:chunk_size])
|
|
166
|
+
while (chunk = socket.read(datum[:chunk_size]))
|
|
164
167
|
response_block.call(chunk, nil, nil)
|
|
165
168
|
end
|
|
166
169
|
else
|
|
167
|
-
while chunk = socket.read(datum[:chunk_size])
|
|
170
|
+
while (chunk = socket.read(datum[:chunk_size]))
|
|
168
171
|
datum[:response][:body] << chunk
|
|
169
172
|
end
|
|
170
173
|
end
|
|
@@ -222,7 +225,7 @@ module Excon
|
|
|
222
225
|
end
|
|
223
226
|
|
|
224
227
|
# Retrieve a specific header value. Header names are treated case-insensitively.
|
|
225
|
-
#
|
|
228
|
+
# @param [String] name Header name
|
|
226
229
|
def get_header(name)
|
|
227
230
|
headers[name]
|
|
228
231
|
end
|