framed_rails 0.1.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.
- data/.gitignore +11 -0
- data/.ruby-version +1 -0
- data/CHANGELOG +1 -0
- data/Gemfile +3 -0
- data/LICENSE +1 -0
- data/README.md +107 -0
- data/framed_rails.gemspec +37 -0
- data/lib/framed/client.rb +34 -0
- data/lib/framed/emitters.rb +113 -0
- data/lib/framed/example.rb +17 -0
- data/lib/framed/exceptions.rb +13 -0
- data/lib/framed/okjson.rb +602 -0
- data/lib/framed/rails.rb +43 -0
- data/lib/framed/railtie.rb +9 -0
- data/lib/framed/utils.rb +54 -0
- data/lib/framed/version.rb +4 -0
- data/lib/framed_rails.rb +71 -0
- data/vendor/gems/excon-0.45.3/data/cacert.pem +3860 -0
- data/vendor/gems/excon-0.45.3/lib/excon/connection.rb +469 -0
- data/vendor/gems/excon-0.45.3/lib/excon/constants.rb +142 -0
- data/vendor/gems/excon-0.45.3/lib/excon/errors.rb +155 -0
- data/vendor/gems/excon-0.45.3/lib/excon/extensions/uri.rb +33 -0
- data/vendor/gems/excon-0.45.3/lib/excon/headers.rb +83 -0
- data/vendor/gems/excon-0.45.3/lib/excon/middlewares/base.rb +24 -0
- data/vendor/gems/excon-0.45.3/lib/excon/middlewares/decompress.rb +35 -0
- data/vendor/gems/excon-0.45.3/lib/excon/middlewares/escape_path.rb +11 -0
- data/vendor/gems/excon-0.45.3/lib/excon/middlewares/expects.rb +18 -0
- data/vendor/gems/excon-0.45.3/lib/excon/middlewares/idempotent.rb +33 -0
- data/vendor/gems/excon-0.45.3/lib/excon/middlewares/instrumentor.rb +34 -0
- data/vendor/gems/excon-0.45.3/lib/excon/middlewares/mock.rb +51 -0
- data/vendor/gems/excon-0.45.3/lib/excon/middlewares/redirect_follower.rb +56 -0
- data/vendor/gems/excon-0.45.3/lib/excon/middlewares/response_parser.rb +12 -0
- data/vendor/gems/excon-0.45.3/lib/excon/pretty_printer.rb +45 -0
- data/vendor/gems/excon-0.45.3/lib/excon/response.rb +212 -0
- data/vendor/gems/excon-0.45.3/lib/excon/socket.rb +310 -0
- data/vendor/gems/excon-0.45.3/lib/excon/ssl_socket.rb +151 -0
- data/vendor/gems/excon-0.45.3/lib/excon/standard_instrumentor.rb +27 -0
- data/vendor/gems/excon-0.45.3/lib/excon/unix_socket.rb +40 -0
- data/vendor/gems/excon-0.45.3/lib/excon/utils.rb +87 -0
- data/vendor/gems/excon-0.45.3/lib/excon.rb +234 -0
- metadata +91 -0
@@ -0,0 +1,469 @@
|
|
1
|
+
module Excon
|
2
|
+
class Connection
|
3
|
+
include Utils
|
4
|
+
|
5
|
+
attr_reader :data
|
6
|
+
|
7
|
+
def connection
|
8
|
+
Excon.display_warning('Excon::Connection#connection is deprecated use Excon::Connection#data instead.')
|
9
|
+
@data
|
10
|
+
end
|
11
|
+
def connection=(new_params)
|
12
|
+
Excon.display_warning('Excon::Connection#connection= is deprecated. Use of this method may cause unexpected results.')
|
13
|
+
@data = new_params
|
14
|
+
end
|
15
|
+
|
16
|
+
def params
|
17
|
+
Excon.display_warning('Excon::Connection#params is deprecated use Excon::Connection#data instead.')
|
18
|
+
@data
|
19
|
+
end
|
20
|
+
def params=(new_params)
|
21
|
+
Excon.display_warning('Excon::Connection#params= is deprecated. Use of this method may cause unexpected results.')
|
22
|
+
@data = new_params
|
23
|
+
end
|
24
|
+
|
25
|
+
def proxy
|
26
|
+
Excon.display_warning('Excon::Connection#proxy is deprecated use Excon::Connection#data[:proxy] instead.')
|
27
|
+
@data[:proxy]
|
28
|
+
end
|
29
|
+
def proxy=(new_proxy)
|
30
|
+
Excon.display_warning('Excon::Connection#proxy= is deprecated. Use of this method may cause unexpected results.')
|
31
|
+
@data[:proxy] = new_proxy
|
32
|
+
end
|
33
|
+
|
34
|
+
# Initializes a new Connection instance
|
35
|
+
# @param [Hash<Symbol, >] params One or more optional params
|
36
|
+
# @option params [String] :body Default text to be sent over a socket. Only used if :body absent in Connection#request params
|
37
|
+
# @option params [Hash<Symbol, String>] :headers The default headers to supply in a request. Only used if params[:headers] is not supplied to Connection#request
|
38
|
+
# @option params [String] :host The destination host's reachable DNS name or IP, in the form of a String. IPv6 addresses must be wrapped (e.g. [::1]). See URI#host.
|
39
|
+
# @option params [String] :hostname Same as host, but usable for socket connections. IPv6 addresses must not be wrapped (e.g. ::1). See URI#hostname.
|
40
|
+
# @option params [String] :path Default path; appears after 'scheme://host:port/'. Only used if params[:path] is not supplied to Connection#request
|
41
|
+
# @option params [Fixnum] :port The port on which to connect, to the destination host
|
42
|
+
# @option params [Hash] :query Default query; appended to the 'scheme://host:port/path/' in the form of '?key=value'. Will only be used if params[:query] is not supplied to Connection#request
|
43
|
+
# @option params [String] :scheme The protocol; 'https' causes OpenSSL to be used
|
44
|
+
# @option params [String] :socket The path to the unix socket (required for 'unix://' connections)
|
45
|
+
# @option params [String] :ciphers Only use the specified SSL/TLS cipher suites; use OpenSSL cipher spec format e.g. 'HIGH:!aNULL:!3DES' or 'AES256-SHA:DES-CBC3-SHA'
|
46
|
+
# @option params [String] :proxy Proxy server; e.g. 'http://myproxy.com:8888'
|
47
|
+
# @option params [Fixnum] :retry_limit Set how many times we'll retry a failed request. (Default 4)
|
48
|
+
# @option params [Class] :instrumentor Responds to #instrument as in ActiveSupport::Notifications
|
49
|
+
# @option params [String] :instrumentor_name Name prefix for #instrument events. Defaults to 'excon'
|
50
|
+
def initialize(params = {})
|
51
|
+
@data = Excon.defaults.dup
|
52
|
+
# merge does not deep-dup, so make sure headers is not the original
|
53
|
+
@data[:headers] = @data[:headers].dup
|
54
|
+
|
55
|
+
# the same goes for :middlewares
|
56
|
+
@data[:middlewares] = @data[:middlewares].dup
|
57
|
+
|
58
|
+
params = validate_params(:connection, params)
|
59
|
+
@data.merge!(params)
|
60
|
+
|
61
|
+
setup_proxy
|
62
|
+
|
63
|
+
if ENV.has_key?('EXCON_STANDARD_INSTRUMENTOR')
|
64
|
+
@data[:instrumentor] = Excon::StandardInstrumentor
|
65
|
+
end
|
66
|
+
|
67
|
+
if @data[:debug] || ENV.has_key?('EXCON_DEBUG')
|
68
|
+
@data[:debug_request] = @data[:debug_response] = true
|
69
|
+
@data[:instrumentor] = Excon::StandardInstrumentor
|
70
|
+
end
|
71
|
+
|
72
|
+
# Use Basic Auth if url contains a login
|
73
|
+
if @data[:user] || @data[:password]
|
74
|
+
user, pass = Utils.unescape_form(@data[:user].to_s), Utils.unescape_form(@data[:password].to_s)
|
75
|
+
@data[:headers]['Authorization'] ||= 'Basic ' << ['' << user.to_s << ':' << pass.to_s].pack('m').delete(Excon::CR_NL)
|
76
|
+
end
|
77
|
+
|
78
|
+
@socket_key = '' << @data[:scheme]
|
79
|
+
if @data[:scheme] == UNIX
|
80
|
+
if @data[:host]
|
81
|
+
raise ArgumentError, "The `:host` parameter should not be set for `unix://` connections.\n" +
|
82
|
+
"When supplying a `unix://` URI, it should start with `unix:/` or `unix:///`."
|
83
|
+
elsif !@data[:socket]
|
84
|
+
raise ArgumentError, 'You must provide a `:socket` for `unix://` connections'
|
85
|
+
else
|
86
|
+
@socket_key << '://' << @data[:socket]
|
87
|
+
end
|
88
|
+
else
|
89
|
+
@socket_key << '://' << @data[:host] << port_string(@data)
|
90
|
+
end
|
91
|
+
reset
|
92
|
+
end
|
93
|
+
|
94
|
+
def error_call(datum)
|
95
|
+
if datum[:error]
|
96
|
+
raise(datum[:error])
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def request_call(datum)
|
101
|
+
begin
|
102
|
+
if datum.has_key?(:response)
|
103
|
+
# we already have data from a middleware, so bail
|
104
|
+
return datum
|
105
|
+
else
|
106
|
+
socket.data = datum
|
107
|
+
# start with "METHOD /path"
|
108
|
+
request = datum[:method].to_s.upcase << ' '
|
109
|
+
if datum[:proxy] && datum[:scheme] != HTTPS
|
110
|
+
request << datum[:scheme] << '://' << datum[:host] << port_string(datum)
|
111
|
+
end
|
112
|
+
request << datum[:path]
|
113
|
+
|
114
|
+
# add query to path, if there is one
|
115
|
+
request << query_string(datum)
|
116
|
+
|
117
|
+
# finish first line with "HTTP/1.1\r\n"
|
118
|
+
request << HTTP_1_1
|
119
|
+
|
120
|
+
if datum.has_key?(:request_block)
|
121
|
+
datum[:headers]['Transfer-Encoding'] = 'chunked'
|
122
|
+
else
|
123
|
+
body = datum[:body].is_a?(String) ? StringIO.new(datum[:body]) : datum[:body]
|
124
|
+
|
125
|
+
# The HTTP spec isn't clear on it, but specifically, GET requests don't usually send bodies;
|
126
|
+
# if they don't, sending Content-Length:0 can cause issues.
|
127
|
+
unless datum[:method].to_s.casecmp('GET') == 0 && body.nil?
|
128
|
+
unless datum[:headers].has_key?('Content-Length')
|
129
|
+
datum[:headers]['Content-Length'] = detect_content_length(body)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
# add headers to request
|
135
|
+
datum[:headers].each do |key, values|
|
136
|
+
[values].flatten.each do |value|
|
137
|
+
request << key.to_s << ': ' << value.to_s << CR_NL
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# add additional "\r\n" to indicate end of headers
|
142
|
+
request << CR_NL
|
143
|
+
socket.write(request) # write out request + headers
|
144
|
+
|
145
|
+
if datum.has_key?(:request_block)
|
146
|
+
while true # write out body with chunked encoding
|
147
|
+
chunk = datum[:request_block].call
|
148
|
+
if FORCE_ENC
|
149
|
+
chunk.force_encoding('BINARY')
|
150
|
+
end
|
151
|
+
if chunk.length > 0
|
152
|
+
socket.write(chunk.length.to_s(16) << CR_NL << chunk << CR_NL)
|
153
|
+
else
|
154
|
+
socket.write('0' << CR_NL << CR_NL)
|
155
|
+
break
|
156
|
+
end
|
157
|
+
end
|
158
|
+
elsif !body.nil? # write out body
|
159
|
+
if body.respond_to?(:binmode)
|
160
|
+
body.binmode
|
161
|
+
end
|
162
|
+
if body.respond_to?(:rewind)
|
163
|
+
body.rewind rescue nil
|
164
|
+
end
|
165
|
+
while chunk = body.read(datum[:chunk_size])
|
166
|
+
socket.write(chunk)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
rescue => error
|
171
|
+
case error
|
172
|
+
when Excon::Errors::StubNotFound, Excon::Errors::Timeout
|
173
|
+
raise(error)
|
174
|
+
else
|
175
|
+
raise(Excon::Errors::SocketError.new(error))
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
datum
|
180
|
+
end
|
181
|
+
|
182
|
+
def response_call(datum)
|
183
|
+
# ensure response_block is yielded to and body is empty from middlewares
|
184
|
+
if datum.has_key?(:response_block) && !datum[:response][:body].empty?
|
185
|
+
response_body = datum[:response][:body].dup
|
186
|
+
datum[:response][:body] = ''
|
187
|
+
content_length = remaining = response_body.bytesize
|
188
|
+
while remaining > 0
|
189
|
+
datum[:response_block].call(response_body.slice!(0, [datum[:chunk_size], remaining].min), [remaining - datum[:chunk_size], 0].max, content_length)
|
190
|
+
remaining -= datum[:chunk_size]
|
191
|
+
end
|
192
|
+
end
|
193
|
+
datum
|
194
|
+
end
|
195
|
+
|
196
|
+
# Sends the supplied request to the destination host.
|
197
|
+
# @yield [chunk] @see Response#self.parse
|
198
|
+
# @param [Hash<Symbol, >] params One or more optional params, override defaults set in Connection.new
|
199
|
+
# @option params [String] :body text to be sent over a socket
|
200
|
+
# @option params [Hash<Symbol, String>] :headers The default headers to supply in a request
|
201
|
+
# @option params [String] :path appears after 'scheme://host:port/'
|
202
|
+
# @option params [Hash] :query appended to the 'scheme://host:port/path/' in the form of '?key=value'
|
203
|
+
def request(params={}, &block)
|
204
|
+
params = validate_params(:request, params)
|
205
|
+
# @data has defaults, merge in new params to override
|
206
|
+
datum = @data.merge(params)
|
207
|
+
datum[:headers] = @data[:headers].merge(datum[:headers] || {})
|
208
|
+
|
209
|
+
if datum[:scheme] == UNIX
|
210
|
+
datum[:headers]['Host'] ||= '' << datum[:socket]
|
211
|
+
else
|
212
|
+
datum[:headers]['Host'] ||= '' << datum[:host] << port_string(datum)
|
213
|
+
end
|
214
|
+
datum[:retries_remaining] ||= datum[:retry_limit]
|
215
|
+
|
216
|
+
# if path is empty or doesn't start with '/', insert one
|
217
|
+
unless datum[:path][0, 1] == '/'
|
218
|
+
datum[:path] = datum[:path].dup.insert(0, '/')
|
219
|
+
end
|
220
|
+
|
221
|
+
if block_given?
|
222
|
+
Excon.display_warning('Excon requests with a block are deprecated, pass :response_block instead.')
|
223
|
+
datum[:response_block] = Proc.new
|
224
|
+
end
|
225
|
+
|
226
|
+
datum[:connection] = self
|
227
|
+
|
228
|
+
datum[:stack] = datum[:middlewares].map do |middleware|
|
229
|
+
lambda {|stack| middleware.new(stack)}
|
230
|
+
end.reverse.inject(self) do |middlewares, middleware|
|
231
|
+
middleware.call(middlewares)
|
232
|
+
end
|
233
|
+
datum = datum[:stack].request_call(datum)
|
234
|
+
|
235
|
+
unless datum[:pipeline]
|
236
|
+
datum = response(datum)
|
237
|
+
|
238
|
+
if datum[:persistent]
|
239
|
+
if key = datum[:response][:headers].keys.detect {|k| k.casecmp('Connection') == 0 }
|
240
|
+
if datum[:response][:headers][key].casecmp('close') == 0
|
241
|
+
reset
|
242
|
+
end
|
243
|
+
end
|
244
|
+
else
|
245
|
+
reset
|
246
|
+
end
|
247
|
+
|
248
|
+
Excon::Response.new(datum[:response])
|
249
|
+
else
|
250
|
+
datum
|
251
|
+
end
|
252
|
+
rescue => error
|
253
|
+
reset
|
254
|
+
datum[:error] = error
|
255
|
+
if datum[:stack]
|
256
|
+
datum[:stack].error_call(datum)
|
257
|
+
else
|
258
|
+
raise error
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
# Sends the supplied requests to the destination host using pipelining.
|
263
|
+
# @pipeline_params [Array<Hash>] pipeline_params An array of one or more optional params, override defaults set in Connection.new, see #request for details
|
264
|
+
def requests(pipeline_params)
|
265
|
+
pipeline_params.each {|params| params.merge!(:pipeline => true, :persistent => true) }
|
266
|
+
pipeline_params.last.merge!(:persistent => @data[:persistent])
|
267
|
+
|
268
|
+
responses = pipeline_params.map do |params|
|
269
|
+
request(params)
|
270
|
+
end.map do |datum|
|
271
|
+
Excon::Response.new(response(datum)[:response])
|
272
|
+
end
|
273
|
+
|
274
|
+
if @data[:persistent]
|
275
|
+
if key = responses.last[:headers].keys.detect {|k| k.casecmp('Connection') == 0 }
|
276
|
+
if responses.last[:headers][key].casecmp('close') == 0
|
277
|
+
reset
|
278
|
+
end
|
279
|
+
end
|
280
|
+
else
|
281
|
+
reset
|
282
|
+
end
|
283
|
+
|
284
|
+
responses
|
285
|
+
end
|
286
|
+
|
287
|
+
def reset
|
288
|
+
if old_socket = sockets.delete(@socket_key)
|
289
|
+
old_socket.close rescue nil
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# Generate HTTP request verb methods
|
294
|
+
Excon::HTTP_VERBS.each do |method|
|
295
|
+
class_eval <<-DEF, __FILE__, __LINE__ + 1
|
296
|
+
def #{method}(params={}, &block)
|
297
|
+
request(params.merge!(:method => :#{method}), &block)
|
298
|
+
end
|
299
|
+
DEF
|
300
|
+
end
|
301
|
+
|
302
|
+
def retry_limit=(new_retry_limit)
|
303
|
+
Excon.display_warning('Excon::Connection#retry_limit= is deprecated, pass :retry_limit to the initializer.')
|
304
|
+
@data[:retry_limit] = new_retry_limit
|
305
|
+
end
|
306
|
+
|
307
|
+
def retry_limit
|
308
|
+
Excon.display_warning('Excon::Connection#retry_limit is deprecated, use Excon::Connection#data[:retry_limit].')
|
309
|
+
@data[:retry_limit] ||= DEFAULT_RETRY_LIMIT
|
310
|
+
end
|
311
|
+
|
312
|
+
def inspect
|
313
|
+
vars = instance_variables.inject({}) do |accum, var|
|
314
|
+
accum.merge!(var.to_sym => instance_variable_get(var))
|
315
|
+
end
|
316
|
+
if vars[:'@data'][:headers].has_key?('Authorization')
|
317
|
+
vars[:'@data'] = vars[:'@data'].dup
|
318
|
+
vars[:'@data'][:headers] = vars[:'@data'][:headers].dup
|
319
|
+
vars[:'@data'][:headers]['Authorization'] = REDACTED
|
320
|
+
end
|
321
|
+
if vars[:'@data'][:password]
|
322
|
+
vars[:'@data'] = vars[:'@data'].dup
|
323
|
+
vars[:'@data'][:password] = REDACTED
|
324
|
+
end
|
325
|
+
inspection = '#<Excon::Connection:'
|
326
|
+
inspection << (object_id << 1).to_s(16)
|
327
|
+
vars.each do |key, value|
|
328
|
+
inspection << ' ' << key.to_s << '=' << value.inspect
|
329
|
+
end
|
330
|
+
inspection << '>'
|
331
|
+
inspection
|
332
|
+
end
|
333
|
+
|
334
|
+
private
|
335
|
+
|
336
|
+
def detect_content_length(body)
|
337
|
+
if body.respond_to?(:size)
|
338
|
+
# IO object: File, Tempfile, StringIO, etc.
|
339
|
+
body.size
|
340
|
+
elsif body.respond_to?(:stat)
|
341
|
+
# for 1.8.7 where file does not have size
|
342
|
+
body.stat.size
|
343
|
+
else
|
344
|
+
0
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
def validate_params(validation, params)
|
349
|
+
valid_keys = case validation
|
350
|
+
when :connection
|
351
|
+
Excon::VALID_CONNECTION_KEYS
|
352
|
+
when :request
|
353
|
+
Excon::VALID_REQUEST_KEYS
|
354
|
+
end
|
355
|
+
invalid_keys = params.keys - valid_keys
|
356
|
+
unless invalid_keys.empty?
|
357
|
+
Excon.display_warning("Invalid Excon #{validation} keys: #{invalid_keys.map(&:inspect).join(', ')}")
|
358
|
+
# FIXME: for now, just warn, don't mutate, give things (ie fog) a chance to catch up
|
359
|
+
#params = params.dup
|
360
|
+
#invalid_keys.each {|key| params.delete(key) }
|
361
|
+
end
|
362
|
+
|
363
|
+
if validation == :connection && params.key?(:host) && !params.key?(:hostname)
|
364
|
+
Excon.display_warning('hostname is missing! For IPv6 support, provide both host and hostname: Excon::Connection#new(:host => uri.host, :hostname => uri.hostname, ...).')
|
365
|
+
params[:hostname] = params[:host]
|
366
|
+
end
|
367
|
+
|
368
|
+
params
|
369
|
+
end
|
370
|
+
|
371
|
+
def response(datum={})
|
372
|
+
datum[:stack].response_call(datum)
|
373
|
+
rescue => error
|
374
|
+
case error
|
375
|
+
when Excon::Errors::HTTPStatusError, Excon::Errors::Timeout
|
376
|
+
raise(error)
|
377
|
+
else
|
378
|
+
raise(Excon::Errors::SocketError.new(error))
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
def socket
|
383
|
+
unix_proxy = @data[:proxy] ? @data[:proxy][:scheme] == UNIX : false
|
384
|
+
sockets[@socket_key] ||= if @data[:scheme] == UNIX || unix_proxy
|
385
|
+
Excon::UnixSocket.new(@data)
|
386
|
+
elsif @data[:scheme] == HTTPS
|
387
|
+
Excon::SSLSocket.new(@data)
|
388
|
+
else
|
389
|
+
Excon::Socket.new(@data)
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
def sockets
|
394
|
+
if @data[:thread_safe_sockets]
|
395
|
+
Thread.current[:_excon_sockets] ||= {}
|
396
|
+
else
|
397
|
+
@_excon_sockets ||= {}
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
def setup_proxy
|
402
|
+
if @data[:disable_proxy]
|
403
|
+
if @data[:proxy]
|
404
|
+
raise ArgumentError, "`:disable_proxy` parameter and `:proxy` parameter cannot both be set at the same time."
|
405
|
+
end
|
406
|
+
return
|
407
|
+
end
|
408
|
+
|
409
|
+
unless @data[:scheme] == UNIX
|
410
|
+
if no_proxy_env = ENV["no_proxy"] || ENV["NO_PROXY"]
|
411
|
+
no_proxy_list = no_proxy_env.scan(/\*?\.?([^\s,:]+)(?::(\d+))?/i).map { |s| [s[0], s[1]] }
|
412
|
+
end
|
413
|
+
|
414
|
+
unless no_proxy_env && no_proxy_list.index { |h| /(^|\.)#{h[0]}$/.match(@data[:host]) && (h[1].nil? || h[1].to_i == @data[:port]) }
|
415
|
+
if @data[:scheme] == HTTPS && (ENV.has_key?('https_proxy') || ENV.has_key?('HTTPS_PROXY'))
|
416
|
+
@data[:proxy] = ENV['https_proxy'] || ENV['HTTPS_PROXY']
|
417
|
+
elsif (ENV.has_key?('http_proxy') || ENV.has_key?('HTTP_PROXY'))
|
418
|
+
@data[:proxy] = ENV['http_proxy'] || ENV['HTTP_PROXY']
|
419
|
+
end
|
420
|
+
end
|
421
|
+
|
422
|
+
case @data[:proxy]
|
423
|
+
when nil
|
424
|
+
@data.delete(:proxy)
|
425
|
+
when Hash
|
426
|
+
# no processing needed
|
427
|
+
when String, URI
|
428
|
+
uri = @data[:proxy].is_a?(String) ? URI.parse(@data[:proxy]) : @data[:proxy]
|
429
|
+
@data[:proxy] = {
|
430
|
+
:host => uri.host,
|
431
|
+
:hostname => uri.hostname,
|
432
|
+
# path is only sensible for a Unix socket proxy
|
433
|
+
:path => uri.scheme == UNIX ? uri.path : nil,
|
434
|
+
:port => uri.port,
|
435
|
+
:scheme => uri.scheme,
|
436
|
+
}
|
437
|
+
if uri.password
|
438
|
+
@data[:proxy][:password] = uri.password
|
439
|
+
end
|
440
|
+
if uri.user
|
441
|
+
@data[:proxy][:user] = uri.user
|
442
|
+
end
|
443
|
+
if @data[:proxy][:scheme] == UNIX
|
444
|
+
if @data[:proxy][:host]
|
445
|
+
raise ArgumentError, "The `:host` parameter should not be set for `unix://` proxies.\n" +
|
446
|
+
"When supplying a `unix://` URI, it should start with `unix:/` or `unix:///`."
|
447
|
+
end
|
448
|
+
else
|
449
|
+
unless uri.host && uri.port && uri.scheme
|
450
|
+
raise Excon::Errors::ProxyParseError, "Proxy is invalid"
|
451
|
+
end
|
452
|
+
end
|
453
|
+
else
|
454
|
+
raise Excon::Errors::ProxyParseError, "Proxy is invalid"
|
455
|
+
end
|
456
|
+
|
457
|
+
if @data.has_key?(:proxy) && @data[:scheme] == 'http'
|
458
|
+
@data[:headers]['Proxy-Connection'] ||= 'Keep-Alive'
|
459
|
+
# https credentials happen in handshake
|
460
|
+
if @data[:proxy].has_key?(:user) || @data[:proxy].has_key?(:password)
|
461
|
+
user, pass = Utils.unescape_form(@data[:proxy][:user].to_s), Utils.unescape_form(@data[:proxy][:password].to_s)
|
462
|
+
auth = ['' << user << ':' << pass].pack('m').delete(Excon::CR_NL)
|
463
|
+
@data[:headers]['Proxy-Authorization'] = 'Basic ' << auth
|
464
|
+
end
|
465
|
+
end
|
466
|
+
end
|
467
|
+
end
|
468
|
+
end
|
469
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module Excon
|
2
|
+
|
3
|
+
VERSION = '0.45.3'
|
4
|
+
|
5
|
+
CR_NL = "\r\n"
|
6
|
+
|
7
|
+
DEFAULT_CA_FILE = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "data", "cacert.pem"))
|
8
|
+
|
9
|
+
DEFAULT_CHUNK_SIZE = 1048576 # 1 megabyte
|
10
|
+
|
11
|
+
# avoid overwrite if somebody has redefined
|
12
|
+
unless const_defined?(:CHUNK_SIZE)
|
13
|
+
CHUNK_SIZE = DEFAULT_CHUNK_SIZE
|
14
|
+
end
|
15
|
+
|
16
|
+
DEFAULT_RETRY_LIMIT = 4
|
17
|
+
|
18
|
+
FORCE_ENC = CR_NL.respond_to?(:force_encoding)
|
19
|
+
|
20
|
+
HTTP_1_1 = " HTTP/1.1\r\n"
|
21
|
+
|
22
|
+
HTTP_VERBS = %w{connect delete get head options patch post put trace}
|
23
|
+
|
24
|
+
HTTPS = 'https'
|
25
|
+
|
26
|
+
NO_ENTITY = [204, 205, 304].freeze
|
27
|
+
|
28
|
+
REDACTED = 'REDACTED'
|
29
|
+
|
30
|
+
UNIX = 'unix'
|
31
|
+
|
32
|
+
USER_AGENT = 'excon/' << VERSION
|
33
|
+
|
34
|
+
VERSIONS = USER_AGENT + ' (' << RUBY_PLATFORM << ') ruby/' << RUBY_VERSION
|
35
|
+
|
36
|
+
VALID_REQUEST_KEYS = [
|
37
|
+
:body,
|
38
|
+
:captures,
|
39
|
+
:chunk_size,
|
40
|
+
:debug_request,
|
41
|
+
:debug_response,
|
42
|
+
:expects,
|
43
|
+
:headers,
|
44
|
+
:idempotent,
|
45
|
+
:instrumentor,
|
46
|
+
:instrumentor_name,
|
47
|
+
:method,
|
48
|
+
:middlewares,
|
49
|
+
:mock,
|
50
|
+
:path,
|
51
|
+
:persistent,
|
52
|
+
:pipeline,
|
53
|
+
:query,
|
54
|
+
:read_timeout,
|
55
|
+
:request_block,
|
56
|
+
:response_block,
|
57
|
+
:retries_remaining, # used internally
|
58
|
+
:retry_limit,
|
59
|
+
:versions,
|
60
|
+
:write_timeout
|
61
|
+
]
|
62
|
+
|
63
|
+
VALID_CONNECTION_KEYS = VALID_REQUEST_KEYS + [
|
64
|
+
:ciphers,
|
65
|
+
:client_key,
|
66
|
+
:client_key_pass,
|
67
|
+
:client_cert,
|
68
|
+
:certificate,
|
69
|
+
:certificate_path,
|
70
|
+
:disable_proxy,
|
71
|
+
:private_key,
|
72
|
+
:private_key_path,
|
73
|
+
:connect_timeout,
|
74
|
+
:family,
|
75
|
+
:host,
|
76
|
+
:hostname,
|
77
|
+
:omit_default_port,
|
78
|
+
:nonblock,
|
79
|
+
:reuseaddr,
|
80
|
+
:password,
|
81
|
+
:port,
|
82
|
+
:proxy,
|
83
|
+
:scheme,
|
84
|
+
:socket,
|
85
|
+
:ssl_ca_file,
|
86
|
+
:ssl_ca_path,
|
87
|
+
:ssl_cert_store,
|
88
|
+
:ssl_verify_callback,
|
89
|
+
:ssl_verify_peer,
|
90
|
+
:ssl_verify_peer_host,
|
91
|
+
:ssl_version,
|
92
|
+
:tcp_nodelay,
|
93
|
+
:thread_safe_sockets,
|
94
|
+
:uri_parser,
|
95
|
+
:user
|
96
|
+
]
|
97
|
+
|
98
|
+
unless ::IO.const_defined?(:WaitReadable)
|
99
|
+
class ::IO
|
100
|
+
module WaitReadable; end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
unless ::IO.const_defined?(:WaitWritable)
|
105
|
+
class ::IO
|
106
|
+
module WaitWritable; end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
# these come last as they rely on the above
|
110
|
+
DEFAULTS = {
|
111
|
+
:chunk_size => CHUNK_SIZE || DEFAULT_CHUNK_SIZE,
|
112
|
+
:ciphers => 'HIGH:!SSLv2:!aNULL:!eNULL:!3DES',
|
113
|
+
:connect_timeout => 60,
|
114
|
+
:debug_request => false,
|
115
|
+
:debug_response => false,
|
116
|
+
:headers => {
|
117
|
+
'User-Agent' => USER_AGENT
|
118
|
+
},
|
119
|
+
:idempotent => false,
|
120
|
+
:instrumentor_name => 'excon',
|
121
|
+
:middlewares => [
|
122
|
+
Excon::Middleware::ResponseParser,
|
123
|
+
Excon::Middleware::Expects,
|
124
|
+
Excon::Middleware::Idempotent,
|
125
|
+
Excon::Middleware::Instrumentor,
|
126
|
+
Excon::Middleware::Mock
|
127
|
+
],
|
128
|
+
:mock => false,
|
129
|
+
:nonblock => true,
|
130
|
+
:omit_default_port => false,
|
131
|
+
:persistent => false,
|
132
|
+
:read_timeout => 60,
|
133
|
+
:retry_limit => DEFAULT_RETRY_LIMIT,
|
134
|
+
:ssl_verify_peer => true,
|
135
|
+
:tcp_nodelay => false,
|
136
|
+
:thread_safe_sockets => true,
|
137
|
+
:uri_parser => URI,
|
138
|
+
:versions => VERSIONS,
|
139
|
+
:write_timeout => 60
|
140
|
+
}
|
141
|
+
|
142
|
+
end
|