excon 0.62.0 → 0.92.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. checksums.yaml +5 -5
  2. data/CONTRIBUTING.md +0 -1
  3. data/LICENSE.md +1 -1
  4. data/README.md +7 -6
  5. data/data/cacert.pem +1220 -1828
  6. data/excon.gemspec +19 -3
  7. data/lib/excon/connection.rb +216 -139
  8. data/lib/excon/constants.rb +38 -13
  9. data/lib/excon/error.rb +15 -0
  10. data/lib/excon/headers.rb +4 -3
  11. data/lib/excon/instrumentors/logging_instrumentor.rb +5 -16
  12. data/lib/excon/instrumentors/standard_instrumentor.rb +2 -9
  13. data/lib/excon/middlewares/base.rb +6 -0
  14. data/lib/excon/middlewares/capture_cookies.rb +1 -1
  15. data/lib/excon/middlewares/decompress.rb +11 -4
  16. data/lib/excon/middlewares/expects.rb +7 -1
  17. data/lib/excon/middlewares/idempotent.rb +20 -3
  18. data/lib/excon/middlewares/instrumentor.rb +8 -0
  19. data/lib/excon/middlewares/mock.rb +12 -3
  20. data/lib/excon/middlewares/redirect_follower.rb +25 -3
  21. data/lib/excon/middlewares/response_parser.rb +3 -0
  22. data/lib/excon/pretty_printer.rb +1 -8
  23. data/lib/excon/response.rb +12 -9
  24. data/lib/excon/socket.rb +59 -42
  25. data/lib/excon/ssl_socket.rb +37 -15
  26. data/lib/excon/test/plugin/server/exec.rb +5 -2
  27. data/lib/excon/test/plugin/server/puma.rb +4 -1
  28. data/lib/excon/test/plugin/server/unicorn.rb +5 -0
  29. data/lib/excon/test/plugin/server/webrick.rb +4 -1
  30. data/lib/excon/test/server.rb +1 -1
  31. data/lib/excon/unix_socket.rb +1 -0
  32. data/lib/excon/utils.rb +59 -5
  33. data/lib/excon/version.rb +1 -1
  34. data/lib/excon.rb +25 -17
  35. metadata +27 -98
  36. data/.document +0 -5
  37. data/.gitignore +0 -13
  38. data/.rspec +0 -3
  39. data/.travis.yml +0 -29
  40. data/Gemfile +0 -19
  41. data/Rakefile +0 -41
  42. data/benchmarks/class_vs_lambda.rb +0 -50
  43. data/benchmarks/concat_vs_insert.rb +0 -21
  44. data/benchmarks/concat_vs_interpolate.rb +0 -22
  45. data/benchmarks/cr_lf.rb +0 -21
  46. data/benchmarks/downcase-eq-eq_vs_casecmp.rb +0 -169
  47. data/benchmarks/excon.rb +0 -69
  48. data/benchmarks/excon_vs.rb +0 -165
  49. data/benchmarks/for_vs_array_each.rb +0 -27
  50. data/benchmarks/for_vs_hash_each.rb +0 -27
  51. data/benchmarks/has_key-vs-lookup.rb +0 -177
  52. data/benchmarks/headers_case_sensitivity.rb +0 -83
  53. data/benchmarks/headers_split_vs_match.rb +0 -34
  54. data/benchmarks/implicit_block-vs-explicit_block.rb +0 -98
  55. data/benchmarks/merging.rb +0 -21
  56. data/benchmarks/single_vs_double_quotes.rb +0 -21
  57. data/benchmarks/string_ranged_index.rb +0 -87
  58. data/benchmarks/strip_newline.rb +0 -115
  59. data/benchmarks/vs_stdlib.rb +0 -82
  60. data/changelog.txt +0 -1083
  61. data/spec/excon/error_spec.rb +0 -139
  62. data/spec/excon/test/server_spec.rb +0 -28
  63. data/spec/excon_spec.rb +0 -7
  64. data/spec/helpers/file_path_helpers.rb +0 -22
  65. data/spec/requests/basic_spec.rb +0 -40
  66. data/spec/requests/eof_requests_spec.rb +0 -36
  67. data/spec/requests/unix_socket_spec.rb +0 -46
  68. data/spec/spec_helper.rb +0 -24
  69. data/spec/support/shared_contexts/test_server_context.rb +0 -83
  70. data/spec/support/shared_examples/shared_example_for_clients.rb +0 -218
  71. data/spec/support/shared_examples/shared_example_for_streaming_clients.rb +0 -20
  72. data/spec/support/shared_examples/shared_example_for_test_servers.rb +0 -16
  73. data/tests/authorization_header_tests.rb +0 -29
  74. data/tests/bad_tests.rb +0 -47
  75. data/tests/basic_tests.rb +0 -351
  76. data/tests/batch_requests.rb +0 -133
  77. data/tests/complete_responses.rb +0 -31
  78. data/tests/data/127.0.0.1.cert.crt +0 -20
  79. data/tests/data/127.0.0.1.cert.key +0 -27
  80. data/tests/data/excon.cert.crt +0 -20
  81. data/tests/data/excon.cert.key +0 -27
  82. data/tests/data/xs +0 -1
  83. data/tests/error_tests.rb +0 -145
  84. data/tests/header_tests.rb +0 -119
  85. data/tests/middlewares/canned_response_tests.rb +0 -34
  86. data/tests/middlewares/capture_cookies_tests.rb +0 -34
  87. data/tests/middlewares/decompress_tests.rb +0 -157
  88. data/tests/middlewares/escape_path_tests.rb +0 -36
  89. data/tests/middlewares/idempotent_tests.rb +0 -206
  90. data/tests/middlewares/instrumentation_tests.rb +0 -315
  91. data/tests/middlewares/mock_tests.rb +0 -304
  92. data/tests/middlewares/redirect_follower_tests.rb +0 -112
  93. data/tests/pipeline_tests.rb +0 -40
  94. data/tests/proxy_tests.rb +0 -306
  95. data/tests/query_string_tests.rb +0 -87
  96. data/tests/rackups/basic.rb +0 -41
  97. data/tests/rackups/basic.ru +0 -3
  98. data/tests/rackups/basic_auth.ru +0 -14
  99. data/tests/rackups/deflater.ru +0 -4
  100. data/tests/rackups/proxy.ru +0 -18
  101. data/tests/rackups/query_string.ru +0 -13
  102. data/tests/rackups/redirecting.ru +0 -23
  103. data/tests/rackups/redirecting_with_cookie.ru +0 -40
  104. data/tests/rackups/request_headers.ru +0 -15
  105. data/tests/rackups/request_methods.ru +0 -21
  106. data/tests/rackups/response_header.ru +0 -18
  107. data/tests/rackups/ssl.ru +0 -16
  108. data/tests/rackups/ssl_mismatched_cn.ru +0 -15
  109. data/tests/rackups/ssl_verify_peer.ru +0 -16
  110. data/tests/rackups/streaming.ru +0 -30
  111. data/tests/rackups/thread_safety.ru +0 -17
  112. data/tests/rackups/timeout.ru +0 -14
  113. data/tests/rackups/webrick_patch.rb +0 -34
  114. data/tests/request_headers_tests.rb +0 -21
  115. data/tests/request_method_tests.rb +0 -47
  116. data/tests/request_tests.rb +0 -59
  117. data/tests/response_tests.rb +0 -197
  118. data/tests/servers/bad.rb +0 -20
  119. data/tests/servers/eof.rb +0 -17
  120. data/tests/servers/error.rb +0 -20
  121. data/tests/servers/good.rb +0 -350
  122. data/tests/test_helper.rb +0 -306
  123. data/tests/thread_safety_tests.rb +0 -39
  124. data/tests/timeout_tests.rb +0 -12
  125. data/tests/utils_tests.rb +0 -81
@@ -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
- :idempotent,
44
- :instrumentor,
45
- :instrumentor_name,
50
+ :instrumentor, # Used for setting logging within Connection
51
+ :logger,
46
52
  :method,
47
53
  :middlewares,
48
- :mock,
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
- :retries_remaining, # used internally
57
- :retry_limit,
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
- :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:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS',
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 = {}, &block)
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
- self.logger.log(logger.level, info) if info
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 = {}, &block)
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
- if params.has_key?(:headers) && params[:headers].has_key?('Authorization')
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
@@ -8,7 +8,7 @@ module Excon
8
8
  end
9
9
 
10
10
  def get_header(datum, header)
11
- _, header_value = datum[:response][:headers].detect do |key, value|
11
+ _, header_value = datum[:response][:headers].detect do |key, _|
12
12
  key.casecmp(header) == 0
13
13
  end
14
14
  header_value
@@ -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
- # assume inflate omits header
23
- datum[:response][:body] = Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(body)
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,value| key == :response},
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] && [Excon::Errors::Timeout, Excon::Errors::SocketError,
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
- datum.reject! {|key, _| !Excon::VALID_REQUEST_KEYS.include?(key) }
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 => '127.0.0.1'
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,value| key == :headers})
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, value|
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!({:response => response.data})
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
@@ -6,6 +6,9 @@ module Excon
6
6
  unless datum.has_key?(:response)
7
7
  datum = Excon::Response.parse(datum[:connection].send(:socket), datum)
8
8
  end
9
+ if datum.has_key?(:logger)
10
+ datum[:response][:logger] = datum[:logger]
11
+ end
9
12
  @stack.response_call(datum)
10
13
  end
11
14
  end
@@ -9,14 +9,7 @@ module Excon
9
9
  datum.delete(:connection)
10
10
  datum.delete(:stack)
11
11
 
12
- if datum.has_key?(:headers) && datum[:headers].has_key?('Authorization')
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
@@ -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
- begin
62
+ line = nil
63
+ loop do
63
64
  line = socket.readline
64
- end until status = line[9, 3].to_i
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
- # @param [String] name Header name
228
+ # @param [String] name Header name
226
229
  def get_header(name)
227
230
  headers[name]
228
231
  end