excon 0.62.0 → 0.92.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|