excon 0.62.0 → 0.85.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.
Files changed (121) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE.md +1 -1
  3. data/README.md +6 -5
  4. data/data/cacert.pem +939 -1720
  5. data/excon.gemspec +17 -2
  6. data/lib/excon.rb +25 -17
  7. data/lib/excon/connection.rb +206 -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 +4 -15
  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 +2 -2
  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 +51 -33
  25. data/lib/excon/ssl_socket.rb +33 -13
  26. data/lib/excon/test/plugin/server/exec.rb +2 -2
  27. data/lib/excon/test/server.rb +1 -1
  28. data/lib/excon/unix_socket.rb +1 -0
  29. data/lib/excon/utils.rb +58 -5
  30. data/lib/excon/version.rb +1 -1
  31. metadata +27 -98
  32. data/.document +0 -5
  33. data/.gitignore +0 -13
  34. data/.rspec +0 -3
  35. data/.travis.yml +0 -29
  36. data/Gemfile +0 -19
  37. data/Rakefile +0 -41
  38. data/benchmarks/class_vs_lambda.rb +0 -50
  39. data/benchmarks/concat_vs_insert.rb +0 -21
  40. data/benchmarks/concat_vs_interpolate.rb +0 -22
  41. data/benchmarks/cr_lf.rb +0 -21
  42. data/benchmarks/downcase-eq-eq_vs_casecmp.rb +0 -169
  43. data/benchmarks/excon.rb +0 -69
  44. data/benchmarks/excon_vs.rb +0 -165
  45. data/benchmarks/for_vs_array_each.rb +0 -27
  46. data/benchmarks/for_vs_hash_each.rb +0 -27
  47. data/benchmarks/has_key-vs-lookup.rb +0 -177
  48. data/benchmarks/headers_case_sensitivity.rb +0 -83
  49. data/benchmarks/headers_split_vs_match.rb +0 -34
  50. data/benchmarks/implicit_block-vs-explicit_block.rb +0 -98
  51. data/benchmarks/merging.rb +0 -21
  52. data/benchmarks/single_vs_double_quotes.rb +0 -21
  53. data/benchmarks/string_ranged_index.rb +0 -87
  54. data/benchmarks/strip_newline.rb +0 -115
  55. data/benchmarks/vs_stdlib.rb +0 -82
  56. data/changelog.txt +0 -1083
  57. data/spec/excon/error_spec.rb +0 -139
  58. data/spec/excon/test/server_spec.rb +0 -28
  59. data/spec/excon_spec.rb +0 -7
  60. data/spec/helpers/file_path_helpers.rb +0 -22
  61. data/spec/requests/basic_spec.rb +0 -40
  62. data/spec/requests/eof_requests_spec.rb +0 -36
  63. data/spec/requests/unix_socket_spec.rb +0 -46
  64. data/spec/spec_helper.rb +0 -24
  65. data/spec/support/shared_contexts/test_server_context.rb +0 -83
  66. data/spec/support/shared_examples/shared_example_for_clients.rb +0 -218
  67. data/spec/support/shared_examples/shared_example_for_streaming_clients.rb +0 -20
  68. data/spec/support/shared_examples/shared_example_for_test_servers.rb +0 -16
  69. data/tests/authorization_header_tests.rb +0 -29
  70. data/tests/bad_tests.rb +0 -47
  71. data/tests/basic_tests.rb +0 -351
  72. data/tests/batch_requests.rb +0 -133
  73. data/tests/complete_responses.rb +0 -31
  74. data/tests/data/127.0.0.1.cert.crt +0 -20
  75. data/tests/data/127.0.0.1.cert.key +0 -27
  76. data/tests/data/excon.cert.crt +0 -20
  77. data/tests/data/excon.cert.key +0 -27
  78. data/tests/data/xs +0 -1
  79. data/tests/error_tests.rb +0 -145
  80. data/tests/header_tests.rb +0 -119
  81. data/tests/middlewares/canned_response_tests.rb +0 -34
  82. data/tests/middlewares/capture_cookies_tests.rb +0 -34
  83. data/tests/middlewares/decompress_tests.rb +0 -157
  84. data/tests/middlewares/escape_path_tests.rb +0 -36
  85. data/tests/middlewares/idempotent_tests.rb +0 -206
  86. data/tests/middlewares/instrumentation_tests.rb +0 -315
  87. data/tests/middlewares/mock_tests.rb +0 -304
  88. data/tests/middlewares/redirect_follower_tests.rb +0 -112
  89. data/tests/pipeline_tests.rb +0 -40
  90. data/tests/proxy_tests.rb +0 -306
  91. data/tests/query_string_tests.rb +0 -87
  92. data/tests/rackups/basic.rb +0 -41
  93. data/tests/rackups/basic.ru +0 -3
  94. data/tests/rackups/basic_auth.ru +0 -14
  95. data/tests/rackups/deflater.ru +0 -4
  96. data/tests/rackups/proxy.ru +0 -18
  97. data/tests/rackups/query_string.ru +0 -13
  98. data/tests/rackups/redirecting.ru +0 -23
  99. data/tests/rackups/redirecting_with_cookie.ru +0 -40
  100. data/tests/rackups/request_headers.ru +0 -15
  101. data/tests/rackups/request_methods.ru +0 -21
  102. data/tests/rackups/response_header.ru +0 -18
  103. data/tests/rackups/ssl.ru +0 -16
  104. data/tests/rackups/ssl_mismatched_cn.ru +0 -15
  105. data/tests/rackups/ssl_verify_peer.ru +0 -16
  106. data/tests/rackups/streaming.ru +0 -30
  107. data/tests/rackups/thread_safety.ru +0 -17
  108. data/tests/rackups/timeout.ru +0 -14
  109. data/tests/rackups/webrick_patch.rb +0 -34
  110. data/tests/request_headers_tests.rb +0 -21
  111. data/tests/request_method_tests.rb +0 -47
  112. data/tests/request_tests.rb +0 -59
  113. data/tests/response_tests.rb +0 -197
  114. data/tests/servers/bad.rb +0 -20
  115. data/tests/servers/eof.rb +0 -17
  116. data/tests/servers/error.rb +0 -20
  117. data/tests/servers/good.rb +0 -350
  118. data/tests/test_helper.rb +0 -306
  119. data/tests/thread_safety_tests.rb +0 -39
  120. data/tests/timeout_tests.rb +0 -12
  121. 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)
@@ -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
@@ -15,9 +15,9 @@ module Excon
15
15
  def response_call(datum)
16
16
  body = datum[:response][:body]
17
17
  unless datum.has_key?(:response_block) || body.nil? || body.empty?
18
- if key = datum[:response][:headers].keys.detect {|k| k.casecmp('Content-Encoding') == 0 }
18
+ if (key = datum[:response][:headers].keys.detect {|k| k.casecmp('Content-Encoding') == 0 })
19
19
  encodings = Utils.split_header_value(datum[:response][:headers][key])
20
- if encoding = encodings.last
20
+ if (encoding = encodings.last)
21
21
  if encoding.casecmp('deflate') == 0
22
22
  # assume inflate omits header
23
23
  datum[:response][:body] = Zlib::Inflate.new(-Zlib::MAX_WBITS).inflate(body)
@@ -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