excon 0.16.10 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of excon might be problematic. Click here for more details.

@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- excon (0.16.10)
4
+ excon (0.17.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/README.md CHANGED
@@ -5,6 +5,7 @@ Http(s) EXtended CONnections
5
5
 
6
6
  [![Build Status](https://secure.travis-ci.org/geemus/excon.png)](http://travis-ci.org/geemus/excon)
7
7
  [![Dependency Status](https://gemnasium.com/geemus/excon.png)](https://gemnasium.com/geemus/excon)
8
+ [![Gem Version](https://fury-badge.herokuapp.com/rb/excon.png)](http://badge.fury.io/rb/excon)
8
9
 
9
10
  Getting Started
10
11
  ---------------
@@ -57,9 +58,21 @@ Both one-off and persistent connections support many other options. Here are a f
57
58
  # this request can be repeated safely, so retry on errors up to 3 times
58
59
  connection.request(:idempotent => true)
59
60
 
61
+ # this request can be repeated safely, retry up to 6 times
62
+ connection.request(:idempotent => true, :retry_limit => 6)
63
+
60
64
  # opt out of nonblocking operations for performance and/or as a workaround
61
65
  connection.request(:nonblock => false)
62
66
 
67
+ # set longer connect_timeout (default is 60 seconds)
68
+ connection.request(:connect_timeout => 360)
69
+
70
+ # set longer read_timeout (default is 60 seconds)
71
+ connection.request(:read_timeout => 360)
72
+
73
+ # set longer write_timeout (default is 60 seconds)
74
+ connection.request(:write_timeout => 360)
75
+
63
76
  These options can be combined to make pretty much any request you might need.
64
77
 
65
78
  Excon can also expect one or more HTTP status code in response, raising an exception if the response does not meet the criteria.
@@ -13,8 +13,8 @@ Gem::Specification.new do |s|
13
13
  ## If your rubyforge_project name is different, then edit it and comment out
14
14
  ## the sub! line in the Rakefile
15
15
  s.name = 'excon'
16
- s.version = '0.16.10'
17
- s.date = '2012-11-16'
16
+ s.version = '0.17.0'
17
+ s.date = '2013-02-01'
18
18
  s.rubyforge_project = 'excon'
19
19
 
20
20
  ## Make sure your summary is short. The description may be as long
@@ -96,6 +96,8 @@ Gem::Specification.new do |s|
96
96
  lib/excon/connection.rb
97
97
  lib/excon/constants.rb
98
98
  lib/excon/errors.rb
99
+ lib/excon/middlewares/expects.rb
100
+ lib/excon/middlewares/instrumentor.rb
99
101
  lib/excon/response.rb
100
102
  lib/excon/socket.rb
101
103
  lib/excon/ssl_socket.rb
@@ -106,7 +108,7 @@ Gem::Specification.new do |s|
106
108
  tests/data/xs
107
109
  tests/header_tests.rb
108
110
  tests/idempotent_tests.rb
109
- tests/instrumentation_tests.rb
111
+ tests/middlewares/instrumentation_tests.rb
110
112
  tests/proxy_tests.rb
111
113
  tests/query_string_tests.rb
112
114
  tests/rackups/basic.rb
@@ -114,11 +116,13 @@ Gem::Specification.new do |s|
114
116
  tests/rackups/basic_auth.ru
115
117
  tests/rackups/proxy.ru
116
118
  tests/rackups/query_string.ru
119
+ tests/rackups/request_headers.ru
117
120
  tests/rackups/request_methods.ru
118
121
  tests/rackups/response_header.ru
119
122
  tests/rackups/ssl.ru
120
123
  tests/rackups/thread_safety.ru
121
124
  tests/rackups/timeout.ru
125
+ tests/request_headers_tests.rb
122
126
  tests/request_method_tests.rb
123
127
  tests/servers/bad.rb
124
128
  tests/servers/eof.rb
@@ -19,6 +19,7 @@ module Excon
19
19
  :chunk_size => CHUNK_SIZE || DEFAULT_CHUNK_SIZE,
20
20
  :connect_timeout => 60,
21
21
  :headers => {},
22
+ :idempotent => false,
22
23
  :instrumentor_name => 'excon',
23
24
  :mock => false,
24
25
  :nonblock => DEFAULT_NONBLOCK,
@@ -42,6 +43,8 @@ end
42
43
  require 'excon/constants'
43
44
  require 'excon/connection'
44
45
  require 'excon/errors'
46
+ require 'excon/middlewares/expects'
47
+ require 'excon/middlewares/instrumentor'
45
48
  require 'excon/response'
46
49
  require 'excon/socket'
47
50
  require 'excon/ssl_socket'
@@ -108,7 +111,7 @@ module Excon
108
111
  request_params.update(
109
112
  :host => uri.host,
110
113
  :path => uri.path,
111
- :port => uri.port.to_s,
114
+ :port => uri.port,
112
115
  :query => uri.query,
113
116
  :scheme => uri.scheme
114
117
  )
@@ -1,6 +1,25 @@
1
1
  module Excon
2
2
  class Connection
3
- attr_reader :connection, :proxy
3
+
4
+ attr_reader :data
5
+
6
+ def params
7
+ $stderr.puts("Excon::Connection#params is deprecated use Excon::Connection#data instead (#{caller.first})")
8
+ @data
9
+ end
10
+ def params=(new_params)
11
+ $stderr.puts("Excon::Connection#params= is deprecated use Excon::Connection#data= instead (#{caller.first})")
12
+ @data = new_params
13
+ end
14
+
15
+ def proxy
16
+ $stderr.puts("Excon::Connection#proxy is deprecated use Excon::Connection#data[:proxy] instead (#{caller.first})")
17
+ @data[:proxy]
18
+ end
19
+ def proxy=(new_proxy)
20
+ $stderr.puts("Excon::Connection#proxy= is deprecated use Excon::Connection#data[:proxy]= instead (#{caller.first})")
21
+ @data[:proxy] = new_proxy
22
+ end
4
23
 
5
24
  # Initializes a new Connection instance
6
25
  # @param [String] url The destination URL
@@ -18,49 +37,153 @@ module Excon
18
37
  # @option params [String] :instrumentor_name Name prefix for #instrument events. Defaults to 'excon'
19
38
  def initialize(url, params = {})
20
39
  uri = URI.parse(url)
21
- @connection = Excon.defaults.merge({
40
+ @data = Excon.defaults.merge({
22
41
  :host => uri.host,
23
42
  :host_port => '' << uri.host << ':' << uri.port.to_s,
24
43
  :path => uri.path,
25
- :port => uri.port.to_s,
44
+ :port => uri.port,
26
45
  :query => uri.query,
27
46
  :scheme => uri.scheme,
28
47
  }).merge!(params)
29
48
  # merge does not deep-dup, so make sure headers is not the original
30
- @connection[:headers] = @connection[:headers].dup
31
-
32
- @proxy = nil
49
+ @data[:headers] = @data[:headers].dup
33
50
 
34
- if @connection[:scheme] == HTTPS && (ENV.has_key?('https_proxy') || ENV.has_key?('HTTPS_PROXY'))
35
- @proxy = setup_proxy(ENV['https_proxy'] || ENV['HTTPS_PROXY'])
51
+ if @data[:scheme] == HTTPS && (ENV.has_key?('https_proxy') || ENV.has_key?('HTTPS_PROXY'))
52
+ @data[:proxy] = setup_proxy(ENV['https_proxy'] || ENV['HTTPS_PROXY'])
36
53
  elsif (ENV.has_key?('http_proxy') || ENV.has_key?('HTTP_PROXY'))
37
- @proxy = setup_proxy(ENV['http_proxy'] || ENV['HTTP_PROXY'])
38
- elsif @connection.has_key?(:proxy)
39
- @proxy = setup_proxy(@connection[:proxy])
54
+ @data[:proxy] = setup_proxy(ENV['http_proxy'] || ENV['HTTP_PROXY'])
55
+ elsif @data.has_key?(:proxy)
56
+ @data[:proxy] = setup_proxy(@data[:proxy])
40
57
  end
41
58
 
42
- if @proxy
43
- @connection[:headers]['Proxy-Connection'] ||= 'Keep-Alive'
59
+ if @data[:proxy]
60
+ @data[:headers]['Proxy-Connection'] ||= 'Keep-Alive'
44
61
  # https credentials happen in handshake
45
- if @connection[:scheme] == 'http' && (@proxy[:user] || @proxy[:password])
46
- auth = ['' << @proxy[:user].to_s << ':' << @proxy[:password].to_s].pack('m').delete(Excon::CR_NL)
47
- @connection[:headers]['Proxy-Authorization'] = 'Basic ' << auth
62
+ if @data[:scheme] == 'http' && (@data[:proxy][:user] || @data[:proxy][:password])
63
+ auth = ['' << @data[:proxy][:user].to_s << ':' << @data[:proxy][:password].to_s].pack('m').delete(Excon::CR_NL)
64
+ @data[:headers]['Proxy-Authorization'] = 'Basic ' << auth
48
65
  end
49
66
  end
50
67
 
51
68
  if ENV.has_key?('EXCON_DEBUG') || ENV.has_key?('EXCON_STANDARD_INSTRUMENTOR')
52
- @connection[:instrumentor] = Excon::StandardInstrumentor
69
+ @data[:instrumentor] = Excon::StandardInstrumentor
53
70
  end
54
71
 
55
72
  # Use Basic Auth if url contains a login
56
73
  if uri.user || uri.password
57
- @connection[:headers]['Authorization'] ||= 'Basic ' << ['' << uri.user.to_s << ':' << uri.password.to_s].pack('m').delete(Excon::CR_NL)
74
+ @data[:headers]['Authorization'] ||= 'Basic ' << ['' << uri.user.to_s << ':' << uri.password.to_s].pack('m').delete(Excon::CR_NL)
58
75
  end
59
76
 
60
- @socket_key = '' << @connection[:host_port]
77
+ @socket_key = '' << @data[:host_port]
61
78
  reset
62
79
  end
63
80
 
81
+ def call(datum)
82
+ begin
83
+ response_datum = if datum[:mock]
84
+ invoke_stub(datum)
85
+ else
86
+ socket.data = datum
87
+ # start with "METHOD /path"
88
+ request = datum[:method].to_s.upcase << ' '
89
+ if @data[:proxy]
90
+ request << datum[:scheme] << '://' << datum[:host_port]
91
+ end
92
+ request << datum[:path]
93
+
94
+ # add query to path, if there is one
95
+ case datum[:query]
96
+ when String
97
+ request << '?' << datum[:query]
98
+ when Hash
99
+ request << '?'
100
+ datum[:query].each do |key, values|
101
+ if values.nil?
102
+ request << key.to_s << '&'
103
+ else
104
+ [values].flatten.each do |value|
105
+ request << key.to_s << '=' << CGI.escape(value.to_s) << '&'
106
+ end
107
+ end
108
+ end
109
+ request.chop! # remove trailing '&'
110
+ end
111
+
112
+ # finish first line with "HTTP/1.1\r\n"
113
+ request << HTTP_1_1
114
+
115
+ if datum.has_key?(:request_block)
116
+ datum[:headers]['Transfer-Encoding'] = 'chunked'
117
+ elsif ! (datum[:method].to_s.casecmp('GET') == 0 && datum[:body].nil?)
118
+ # The HTTP spec isn't clear on it, but specifically, GET requests don't usually send bodies;
119
+ # if they don't, sending Content-Length:0 can cause issues.
120
+ datum[:headers]['Content-Length'] = detect_content_length(datum[:body])
121
+ end
122
+
123
+ # add headers to request
124
+ datum[:headers].each do |key, values|
125
+ [values].flatten.each do |value|
126
+ request << key.to_s << ': ' << value.to_s << CR_NL
127
+ end
128
+ end
129
+
130
+ # add additional "\r\n" to indicate end of headers
131
+ request << CR_NL
132
+
133
+ # write out the request, sans body
134
+ socket.write(request)
135
+
136
+ # write out the body
137
+ if datum.has_key?(:request_block)
138
+ while true
139
+ chunk = datum[:request_block].call
140
+ if FORCE_ENC
141
+ chunk.force_encoding('BINARY')
142
+ end
143
+ if chunk.length > 0
144
+ socket.write(chunk.length.to_s(16) << CR_NL << chunk << CR_NL)
145
+ else
146
+ socket.write('0' << CR_NL << CR_NL)
147
+ break
148
+ end
149
+ end
150
+ elsif !datum[:body].nil?
151
+ if datum[:body].is_a?(String)
152
+ unless datum[:body].empty?
153
+ socket.write(datum[:body])
154
+ end
155
+ else
156
+ if datum[:body].respond_to?(:binmode)
157
+ datum[:body].binmode
158
+ end
159
+ if datum[:body].respond_to?(:pos=)
160
+ datum[:body].pos = 0
161
+ end
162
+ while chunk = datum[:body].read(datum[:chunk_size])
163
+ socket.write(chunk)
164
+ end
165
+ end
166
+ end
167
+
168
+ # read the response
169
+ response_datum = Excon::Response.parse(socket, datum)
170
+
171
+ if response_datum[:headers]['Connection'] == 'close'
172
+ reset
173
+ end
174
+
175
+ response_datum
176
+ end
177
+ rescue Excon::Errors::StubNotFound, Excon::Errors::Timeout => error
178
+ raise(error)
179
+ rescue => socket_error
180
+ reset
181
+ raise(Excon::Errors::SocketError.new(socket_error))
182
+ end
183
+
184
+ response_datum
185
+ end
186
+
64
187
  # Sends the supplied request to the destination host.
65
188
  # @yield [chunk] @see Response#self.parse
66
189
  # @param [Hash<Symbol, >] params One or more optional params, override defaults set in Connection.new
@@ -72,53 +195,50 @@ module Excon
72
195
  # @option params [Hash] :query appended to the 'scheme://host:port/path/' in the form of '?key=value'
73
196
  # @option params [String] :scheme The protocol; 'https' causes OpenSSL to be used
74
197
  def request(params, &block)
75
- # connection has defaults, merge in new params to override
76
- params = @connection.merge(params)
77
- params[:host_port] = '' << params[:host] << ':' << params[:port].to_s
78
- params[:headers] = @connection[:headers].merge(params[:headers] || {})
79
- params[:headers]['Host'] ||= '' << params[:host_port]
198
+ # @data has defaults, merge in new params to override
199
+ datum = @data.merge(params)
200
+ datum[:host_port] = '' << datum[:host] << ':' << datum[:port].to_s
201
+ datum[:headers] = @data[:headers].merge(datum[:headers] || {})
202
+ datum[:headers]['Host'] = '' << datum[:host_port]
203
+ datum[:retries_remaining] ||= datum[:retry_limit]
80
204
 
81
205
  # if path is empty or doesn't start with '/', insert one
82
- unless params[:path][0, 1] == '/'
83
- params[:path].insert(0, '/')
206
+ unless datum[:path][0, 1] == '/'
207
+ datum[:path].insert(0, '/')
84
208
  end
85
209
 
86
210
  if block_given?
87
211
  $stderr.puts("Excon requests with a block are deprecated, pass :response_block instead (#{caller.first})")
88
- params[:response_block] = Proc.new
212
+ datum[:response_block] = Proc.new
89
213
  end
90
214
 
91
- if params.has_key?(:instrumentor)
92
- if (retries_remaining ||= params[:retry_limit]) < params[:retry_limit]
93
- event_name = "#{params[:instrumentor_name]}.retry"
94
- else
95
- event_name = "#{params[:instrumentor_name]}.request"
96
- end
97
- response = params[:instrumentor].instrument(event_name, params) do
98
- request_kernel(params)
99
- end
100
- params[:instrumentor].instrument("#{params[:instrumentor_name]}.response", response.attributes)
101
- response
102
- else
103
- request_kernel(params)
215
+ datum[:middlewares] = [
216
+ lambda {|app| Excon::Middleware::Instrumentor.new(app) },
217
+ lambda {|app| Excon::Middleware::Expects.new(app) }
218
+ ]
219
+ stack = datum[:middlewares].reverse.inject(self) do |middlewares, middleware|
220
+ middleware.call(middlewares)
104
221
  end
222
+ response_datum = stack.call(datum)
223
+
224
+ Excon::Response.new(response_datum)
105
225
  rescue => request_error
106
- if params[:idempotent] && [Excon::Errors::Timeout, Excon::Errors::SocketError,
226
+ if datum[:idempotent] && [Excon::Errors::Timeout, Excon::Errors::SocketError,
107
227
  Excon::Errors::HTTPStatusError].any? {|ex| request_error.kind_of? ex }
108
- retries_remaining ||= params[:retry_limit]
109
- retries_remaining -= 1
110
- if retries_remaining > 0
111
- retry
228
+ datum[:retries_remaining] -= 1
229
+ if datum[:retries_remaining] > 0
230
+ request(datum, &block)
112
231
  else
113
- if params.has_key?(:instrumentor)
114
- params[:instrumentor].instrument("#{params[:instrumentor_name]}.error", :error => request_error)
232
+ if datum.has_key?(:instrumentor)
233
+ datum[:instrumentor].instrument("#{datum[:instrumentor_name]}.error", :error => request_error)
115
234
  end
116
235
  raise(request_error)
117
236
  end
118
237
  else
119
- if params.has_key?(:instrumentor)
120
- params[:instrumentor].instrument("#{params[:instrumentor_name]}.error", :error => request_error)
238
+ if datum.has_key?(:instrumentor)
239
+ datum[:instrumentor].instrument("#{datum[:instrumentor_name]}.error", :error => request_error)
121
240
  end
241
+ reset
122
242
  raise(request_error)
123
243
  end
124
244
  end
@@ -138,22 +258,22 @@ module Excon
138
258
 
139
259
  def retry_limit=(new_retry_limit)
140
260
  $stderr.puts("Excon::Connection#retry_limit= is deprecated, pass :retry_limit to the initializer (#{caller.first})")
141
- @connection[:retry_limit] = new_retry_limit
261
+ @data[:retry_limit] = new_retry_limit
142
262
  end
143
263
 
144
264
  def retry_limit
145
265
  $stderr.puts("Excon::Connection#retry_limit is deprecated, pass :retry_limit to the initializer (#{caller.first})")
146
- @connection[:retry_limit] ||= DEFAULT_RETRY_LIMIT
266
+ @data[:retry_limit] ||= DEFAULT_RETRY_LIMIT
147
267
  end
148
268
 
149
269
  def inspect
150
270
  vars = instance_variables.inject({}) do |accum, var|
151
271
  accum.merge!(var.to_sym => instance_variable_get(var))
152
272
  end
153
- if vars[:'@connection'][:headers].has_key?('Authorization')
154
- vars[:'@connection'] = vars[:'@connection'].dup
155
- vars[:'@connection'][:headers] = vars[:'@connection'][:headers].dup
156
- vars[:'@connection'][:headers]['Authorization'] = REDACTED
273
+ if vars[:'@data'][:headers].has_key?('Authorization')
274
+ vars[:'@data'] = vars[:'@data'].dup
275
+ vars[:'@data'][:headers] = vars[:'@data'][:headers].dup
276
+ vars[:'@data'][:headers]['Authorization'] = REDACTED
157
277
  end
158
278
  inspection = '#<Excon::Connection:'
159
279
  inspection << (object_id << 1).to_s(16)
@@ -184,186 +304,74 @@ module Excon
184
304
  end
185
305
  end
186
306
 
187
- def request_kernel(params)
188
- begin
189
- response = if params[:mock]
190
- invoke_stub(params)
191
- else
192
- socket.params = params
193
- # start with "METHOD /path"
194
- request = params[:method].to_s.upcase << ' '
195
- if @proxy
196
- request << params[:scheme] << '://' << params[:host_port]
197
- end
198
- request << params[:path]
199
-
200
- # add query to path, if there is one
201
- case params[:query]
202
- when String
203
- request << '?' << params[:query]
204
- when Hash
205
- request << '?'
206
- params[:query].each do |key, values|
207
- if values.nil?
208
- request << key.to_s << '&'
209
- else
210
- [*values].each do |value|
211
- request << key.to_s << '=' << CGI.escape(value.to_s) << '&'
212
- end
213
- end
214
- end
215
- request.chop! # remove trailing '&'
216
- end
217
-
218
- # finish first line with "HTTP/1.1\r\n"
219
- request << HTTP_1_1
220
-
221
- if params.has_key?(:request_block)
222
- params[:headers]['Transfer-Encoding'] = 'chunked'
223
- elsif ! (params[:method].to_s.casecmp('GET') == 0 && params[:body].nil?)
224
- # The HTTP spec isn't clear on it, but specifically, GET requests don't usually send bodies;
225
- # if they don't, sending Content-Length:0 can cause issues.
226
- params[:headers]['Content-Length'] = detect_content_length(params[:body])
227
- end
228
-
229
- # add headers to request
230
- params[:headers].each do |key, values|
231
- [*values].each do |value|
232
- request << key.to_s << ': ' << value.to_s << CR_NL
233
- end
234
- end
235
-
236
- # add additional "\r\n" to indicate end of headers
237
- request << CR_NL
238
-
239
- # write out the request, sans body
240
- socket.write(request)
241
-
242
- # write out the body
243
- if params.has_key?(:request_block)
244
- while true
245
- chunk = params[:request_block].call
246
- if FORCE_ENC
247
- chunk.force_encoding('BINARY')
248
- end
249
- if chunk.length > 0
250
- socket.write(chunk.length.to_s(16) << CR_NL << chunk << CR_NL)
251
- else
252
- socket.write('0' << CR_NL << CR_NL)
253
- break
254
- end
255
- end
256
- elsif !params[:body].nil?
257
- if params[:body].is_a?(String)
258
- unless params[:body].empty?
259
- socket.write(params[:body])
260
- end
261
- else
262
- if params[:body].respond_to?(:binmode)
263
- params[:body].binmode
264
- end
265
- if params[:body].respond_to?(:pos=)
266
- params[:body].pos = 0
267
- end
268
- while chunk = params[:body].read(params[:chunk_size])
269
- socket.write(chunk)
270
- end
271
- end
272
- end
273
-
274
- # read the response
275
- response = Excon::Response.parse(socket, params)
276
-
277
- if response.headers['Connection'] == 'close'
278
- reset
279
- end
280
-
281
- response
282
- end
283
- rescue Excon::Errors::StubNotFound, Excon::Errors::Timeout => error
284
- raise(error)
285
- rescue => socket_error
286
- reset
287
- raise(Excon::Errors::SocketError.new(socket_error))
288
- end
289
-
290
- if params.has_key?(:expects) && ![*params[:expects]].include?(response.status)
291
- reset
292
- raise(Excon::Errors.status_error(params, response))
293
- else
294
- response
295
- end
296
- end
297
-
298
- def invoke_stub(params)
299
-
307
+ def invoke_stub(datum)
300
308
  # convert File/Tempfile body to string before matching:
301
- unless params[:body].nil? || params[:body].is_a?(String)
302
- if params[:body].respond_to?(:binmode)
303
- params[:body].binmode
309
+ unless datum[:body].nil? || datum[:body].is_a?(String)
310
+ if datum[:body].respond_to?(:binmode)
311
+ datum[:body].binmode
304
312
  end
305
- if params[:body].respond_to?(:rewind)
306
- params[:body].rewind
313
+ if datum[:body].respond_to?(:rewind)
314
+ datum[:body].rewind
307
315
  end
308
- params[:body] = params[:body].read
316
+ datum[:body] = datum[:body].read
309
317
  end
310
318
 
311
- params[:captures] = {:headers => {}} # setup data to hold captures
319
+ datum[:captures] = {:headers => {}} # setup data to hold captures
312
320
  Excon.stubs.each do |stub, response|
313
321
  headers_match = !stub.has_key?(:headers) || stub[:headers].keys.all? do |key|
314
322
  case value = stub[:headers][key]
315
323
  when Regexp
316
- if match = value.match(params[:headers][key])
317
- params[:captures][:headers][key] = match.captures
324
+ if match = value.match(datum[:headers][key])
325
+ datum[:captures][:headers][key] = match.captures
318
326
  end
319
327
  match
320
328
  else
321
- value == params[:headers][key]
329
+ value == datum[:headers][key]
322
330
  end
323
331
  end
324
332
  non_headers_match = (stub.keys - [:headers]).all? do |key|
325
333
  case value = stub[key]
326
334
  when Regexp
327
- if match = value.match(params[key])
328
- params[:captures][key] = match.captures
335
+ if match = value.match(datum[key])
336
+ datum[:captures][key] = match.captures
329
337
  end
330
338
  match
331
339
  else
332
- value == params[key]
340
+ value == datum[key]
333
341
  end
334
342
  end
335
343
  if headers_match && non_headers_match
336
- response_attributes = case response
344
+ response_datum = case response
337
345
  when Proc
338
- response.call(params)
346
+ response.call(datum)
339
347
  else
340
348
  response
341
349
  end
342
350
 
343
- if params[:expects] && ![*params[:expects]].include?(response_attributes[:status])
351
+ if datum[:expects] && ![*datum[:expects]].include?(response_datum[:status])
344
352
  # don't pass stuff into a block if there was an error
345
- elsif params.has_key?(:response_block) && response_attributes.has_key?(:body)
346
- body = response_attributes.delete(:body)
353
+ elsif datum.has_key?(:response_block) && response_datum.has_key?(:body)
354
+ body = response_datum.delete(:body)
347
355
  content_length = remaining = body.bytesize
348
356
  i = 0
349
357
  while i < body.length
350
- params[:response_block].call(body[i, params[:chunk_size]], [remaining - params[:chunk_size], 0].max, content_length)
351
- remaining -= params[:chunk_size]
352
- i += params[:chunk_size]
358
+ datum[:response_block].call(body[i, datum[:chunk_size]], [remaining - datum[:chunk_size], 0].max, content_length)
359
+ remaining -= datum[:chunk_size]
360
+ i += datum[:chunk_size]
353
361
  end
354
362
  end
355
- return Excon::Response.new(response_attributes)
363
+ return response_datum
356
364
  end
357
365
  end
358
366
  # if we reach here no stubs matched
359
- raise(Excon::Errors::StubNotFound.new('no stubs matched ' << params.inspect))
367
+ raise(Excon::Errors::StubNotFound.new('no stubs matched ' << datum.inspect))
360
368
  end
361
369
 
362
370
  def socket
363
- sockets[@socket_key] ||= if @connection[:scheme] == HTTPS
364
- Excon::SSLSocket.new(@connection, @proxy)
371
+ sockets[@socket_key] ||= if @data[:scheme] == HTTPS
372
+ Excon::SSLSocket.new(@data)
365
373
  else
366
- Excon::Socket.new(@connection, @proxy)
374
+ Excon::Socket.new(@data)
367
375
  end
368
376
  end
369
377
 
@@ -372,18 +380,23 @@ module Excon
372
380
  end
373
381
 
374
382
  def setup_proxy(proxy)
375
- uri = URI.parse(proxy)
376
- unless uri.host and uri.port and uri.scheme
377
- raise Excon::Errors::ProxyParseError, "Proxy is invalid"
383
+ case proxy
384
+ when String
385
+ uri = URI.parse(proxy)
386
+ unless uri.host and uri.port and uri.scheme
387
+ raise Excon::Errors::ProxyParseError, "Proxy is invalid"
388
+ end
389
+ {
390
+ :host => uri.host,
391
+ :host_port => '' << uri.host << ':' << uri.port.to_s,
392
+ :password => uri.password,
393
+ :port => uri.port,
394
+ :scheme => uri.scheme,
395
+ :user => uri.user
396
+ }
397
+ else
398
+ proxy
378
399
  end
379
- {
380
- :host => uri.host,
381
- :host_port => '' << uri.host << ':' << uri.port.to_s,
382
- :password => uri.password,
383
- :port => uri.port,
384
- :scheme => uri.scheme,
385
- :user => uri.user
386
- }
387
400
  end
388
401
 
389
402
  end