Class: Excon::Connection
- Inherits:
-
Object
- Object
- Excon::Connection
- Defined in:
- lib/excon/connection.rb
Instance Attribute Summary collapse
-
#data ⇒ Object
readonly
Returns the value of attribute data.
Instance Method Summary collapse
- #connection ⇒ Object
- #connection=(new_params) ⇒ Object
- #error_call(datum) ⇒ Object
-
#initialize(params = {}) ⇒ Connection
constructor
Initializes a new Connection instance @param [Hash<Symbol, >] params One or more optional params @option params [String] :body Default text to be sent over a socket.
- #inspect ⇒ Object
- #params ⇒ Object
- #params=(new_params) ⇒ Object
- #proxy ⇒ Object
- #proxy=(new_proxy) ⇒ Object
-
#request(params = {}, &block) ⇒ Object
Sends the supplied request to the destination host.
- #request_call(datum) ⇒ Object
-
#requests(pipeline_params) ⇒ Object
Sends the supplied requests to the destination host using pipelining.
- #reset ⇒ Object
- #response_call(datum) ⇒ Object
- #retry_limit ⇒ Object
- #retry_limit=(new_retry_limit) ⇒ Object
Constructor Details
#initialize(params = {}) ⇒ Connection
Initializes a new Connection instance
@param [Hash<Symbol, >] params One or more optional params
@option params [String] :body Default text to be sent over a socket. Only used if :body absent in Connection#request params
@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
@option params [String] :host The destination host's reachable DNS name or IP, in the form of a String
@option params [String] :path Default path; appears after 'scheme://host:port/'. Only used if params[:path] is not supplied to Connection#request
@option params [Fixnum] :port The port on which to connect, to the destination host
@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
@option params [String] :scheme The protocol; 'https' causes OpenSSL to be used
@option params [String] :socket The path to the unix socket (required for 'unix://' connections)
@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'
@option params [String] :proxy Proxy server; e.g. 'http://myproxy.com:8888'
@option params [Fixnum] :retry_limit Set how many times we'll retry a failed request. (Default 4)
@option params [Class] :instrumentor Responds to #instrument as in ActiveSupport::Notifications
@option params [String] :instrumentor_name Name prefix for #instrument events. Defaults to 'excon'
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/excon/connection.rb', line 48 def initialize(params = {}) params = validate_params(:connection, params) @data = Excon.defaults.dup # merge does not deep-dup, so make sure headers is not the original @data[:headers] = @data[:headers].dup # the same goes for :middlewares @data[:middlewares] = @data[:middlewares].dup @data.merge!(params) unless @data[:scheme] == UNIX no_proxy_env = ENV["no_proxy"] || ENV["NO_PROXY"] || "" no_proxy_list = no_proxy_env.scan(/\*?\.?([^\s,:]+)(?::(\d+))?/i).map { |s| [s[0], s[1]] } unless no_proxy_list.index { |h| /(^|\.)#{h[0]}$/.match(@data[:host]) && (h[1].nil? || h[1].to_i == @data[:port]) } if @data[:scheme] == HTTPS && (ENV.has_key?('https_proxy') || ENV.has_key?('HTTPS_PROXY')) @data[:proxy] = setup_proxy(ENV['https_proxy'] || ENV['HTTPS_PROXY']) elsif (ENV.has_key?('http_proxy') || ENV.has_key?('HTTP_PROXY')) @data[:proxy] = setup_proxy(ENV['http_proxy'] || ENV['HTTP_PROXY']) elsif @data.has_key?(:proxy) @data[:proxy] = setup_proxy(@data[:proxy]) end end end if @data[:proxy] @data[:headers]['Proxy-Connection'] ||= 'Keep-Alive' # https credentials happen in handshake if @data[:scheme] == 'http' && (@data[:proxy][:user] || @data[:proxy][:password]) user, pass = URI.decode_www_form_component(@data[:proxy][:user].to_s), URI.decode_www_form_component(@data[:proxy][:password].to_s) auth = ['' << user.to_s << ':' << pass.to_s].pack('m').delete(Excon::CR_NL) @data[:headers]['Proxy-Authorization'] = 'Basic ' << auth end end if ENV.has_key?('EXCON_DEBUG') || ENV.has_key?('EXCON_STANDARD_INSTRUMENTOR') @data[:instrumentor] = Excon::StandardInstrumentor end # Use Basic Auth if url contains a login if @data[:user] || @data[:password] user, pass = URI.decode_www_form_component(@data[:user].to_s), URI.decode_www_form_component(@data[:password].to_s) @data[:headers]['Authorization'] ||= 'Basic ' << ['' << user.to_s << ':' << pass.to_s].pack('m').delete(Excon::CR_NL) end @socket_key = '' << @data[:scheme] if @data[:scheme] == UNIX if @data[:host] raise ArgumentError, "The `:host` parameter should not be set for `unix://` connections.\n" + "When supplying a `unix://` URI, it should start with `unix:/` or `unix:///`." elsif !@data[:socket] raise ArgumentError, 'You must provide a `:socket` for `unix://` connections' else @socket_key << '://' << @data[:socket] end else @socket_key << '://' << @data[:host] << port_string(@data) end reset end |
Instance Attribute Details
#data ⇒ Object (readonly)
Returns the value of attribute data.
4 5 6 |
# File 'lib/excon/connection.rb', line 4 def data @data end |
Instance Method Details
#connection ⇒ Object
6 7 8 9 |
# File 'lib/excon/connection.rb', line 6 def connection Excon.display_warning("Excon::Connection#connection is deprecated use Excon::Connection#data instead (#{caller.first})") @data end |
#connection=(new_params) ⇒ Object
10 11 12 13 |
# File 'lib/excon/connection.rb', line 10 def connection=(new_params) Excon.display_warning("Excon::Connection#connection= is deprecated. Use of this method may cause unexpected results. (#{caller.first})") @data = new_params end |
#error_call(datum) ⇒ Object
109 110 111 112 113 |
# File 'lib/excon/connection.rb', line 109 def error_call(datum) if datum[:error] raise(datum[:error]) end end |
#inspect ⇒ Object
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 |
# File 'lib/excon/connection.rb', line 321 def inspect vars = instance_variables.inject({}) do |accum, var| accum.merge!(var.to_sym => instance_variable_get(var)) end if vars[:'@data'][:headers].has_key?('Authorization') vars[:'@data'] = vars[:'@data'].dup vars[:'@data'][:headers] = vars[:'@data'][:headers].dup vars[:'@data'][:headers]['Authorization'] = REDACTED end if vars[:'@data'][:password] vars[:'@data'] = vars[:'@data'].dup vars[:'@data'][:password] = REDACTED end inspection = '#<Excon::Connection:' inspection << (object_id << 1).to_s(16) vars.each do |key, value| inspection << ' ' << key.to_s << '=' << value.inspect end inspection << '>' inspection end |
#params ⇒ Object
15 16 17 18 |
# File 'lib/excon/connection.rb', line 15 def params Excon.display_warning("Excon::Connection#params is deprecated use Excon::Connection#data instead (#{caller.first})") @data end |
#params=(new_params) ⇒ Object
19 20 21 22 |
# File 'lib/excon/connection.rb', line 19 def params=(new_params) Excon.display_warning("Excon::Connection#params= is deprecated. Use of this method may cause unexpected results. (#{caller.first})") @data = new_params end |
#proxy ⇒ Object
24 25 26 27 |
# File 'lib/excon/connection.rb', line 24 def proxy Excon.display_warning("Excon::Connection#proxy is deprecated use Excon::Connection#data[:proxy] instead (#{caller.first})") @data[:proxy] end |
#proxy=(new_proxy) ⇒ Object
28 29 30 31 |
# File 'lib/excon/connection.rb', line 28 def proxy=(new_proxy) Excon.display_warning("Excon::Connection#proxy= is deprecated. Use of this method may cause unexpected results. (#{caller.first})") @data[:proxy] = new_proxy end |
#request(params = {}, &block) ⇒ Object
Sends the supplied request to the destination host.
@yield [chunk] @see Response#self.parse
@param [Hash<Symbol, >] params One or more optional params, override defaults set in Connection.new
@option params [String] :body text to be sent over a socket
@option params [Hash<Symbol, String>] :headers The default headers to supply in a request
@option params [String] :path appears after 'scheme://host:port/'
@option params [Hash] :query appended to the 'scheme://host:port/path/' in the form of '?key=value'
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 |
# File 'lib/excon/connection.rb', line 230 def request(params={}, &block) params = validate_params(:request, params) # @data has defaults, merge in new params to override datum = @data.merge(params) datum[:headers] = @data[:headers].merge(datum[:headers] || {}) if datum[:scheme] == UNIX datum[:headers]['Host'] ||= '' << datum[:socket] else datum[:headers]['Host'] ||= '' << datum[:host] << port_string(datum) end datum[:retries_remaining] ||= datum[:retry_limit] # if path is empty or doesn't start with '/', insert one unless datum[:path][0, 1] == '/' datum[:path] = datum[:path].dup.insert(0, '/') end if block_given? Excon.display_warning("Excon requests with a block are deprecated, pass :response_block instead (#{caller.first})") datum[:response_block] = Proc.new end if datum[:request_block] && datum[:idempotent] Excon.display_warning("Excon requests with a :request_block can not be :idempotent (#{caller.first})") datum[:idempotent] = false end datum[:connection] = self datum[:stack] = datum[:middlewares].map do |middleware| lambda {|stack| middleware.new(stack)} end.reverse.inject(self) do |middlewares, middleware| middleware.call(middlewares) end datum = datum[:stack].request_call(datum) unless datum[:pipeline] datum = response(datum) if datum[:response][:headers]['Connection'] == 'close' reset end Excon::Response.new(datum[:response]) else datum end rescue => error reset datum[:error] = error if datum[:stack] datum[:stack].error_call(datum) else raise error end end |
#request_call(datum) ⇒ Object
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/excon/connection.rb', line 115 def request_call(datum) begin if datum.has_key?(:response) # we already have data from a middleware, so bail return datum else socket.data = datum # start with "METHOD /path" request = datum[:method].to_s.upcase << ' ' if datum[:proxy] request << datum[:scheme] << '://' << datum[:host] << port_string(datum) end request << datum[:path] # add query to path, if there is one case datum[:query] when String request << '?' << datum[:query] when Hash request << '?' datum[:query].each do |key, values| if values.nil? request << key.to_s << '&' else [values].flatten.each do |value| request << key.to_s << '=' << CGI.escape(value.to_s) << '&' end end end request.chop! # remove trailing '&' end # finish first line with "HTTP/1.1\r\n" request << HTTP_1_1 if datum.has_key?(:request_block) datum[:headers]['Transfer-Encoding'] = 'chunked' else body = datum[:body].is_a?(String) ? StringIO.new(datum[:body]) : datum[:body] # The HTTP spec isn't clear on it, but specifically, GET requests don't usually send bodies; # if they don't, sending Content-Length:0 can cause issues. unless datum[:method].to_s.casecmp('GET') == 0 && body.nil? unless datum[:headers].has_key?('Content-Length') datum[:headers]['Content-Length'] = detect_content_length(body) end end end # add headers to request datum[:headers].each do |key, values| [values].flatten.each do |value| request << key.to_s << ': ' << value.to_s << CR_NL end end # add additional "\r\n" to indicate end of headers request << CR_NL socket.write(request) # write out request + headers if datum.has_key?(:request_block) while true # write out body with chunked encoding chunk = datum[:request_block].call if FORCE_ENC chunk.force_encoding('BINARY') end if chunk.length > 0 socket.write(chunk.length.to_s(16) << CR_NL << chunk << CR_NL) else socket.write('0' << CR_NL << CR_NL) break end end elsif !body.nil? # write out body if body.respond_to?(:binmode) body.binmode end if body.respond_to?(:pos=) body.pos = 0 end while chunk = body.read(datum[:chunk_size]) socket.write(chunk) end end end rescue => error case error when Excon::Errors::StubNotFound, Excon::Errors::Timeout raise(error) else raise(Excon::Errors::SocketError.new(error)) end end datum end |
#requests(pipeline_params) ⇒ Object
Sends the supplied requests to the destination host using pipelining.
@pipeline_params [Array<Hash>] pipeline_params An array of one or more optional params, override defaults set in Connection.new, see #request for details
290 291 292 293 294 295 296 |
# File 'lib/excon/connection.rb', line 290 def requests(pipeline_params) pipeline_params.map do |params| request(params.merge!(:pipeline => true)) end.map do |datum| Excon::Response.new(response(datum)[:response]) end end |
#reset ⇒ Object
298 299 300 |
# File 'lib/excon/connection.rb', line 298 def reset (old_socket = sockets.delete(@socket_key)) && old_socket.close end |
#response_call(datum) ⇒ Object
212 213 214 215 216 217 218 219 220 221 |
# File 'lib/excon/connection.rb', line 212 def response_call(datum) if datum.has_key?(:response_block) && !datum[:response][:body].empty? content_length = remaining = datum[:response][:body].bytesize while remaining > 0 datum[:response_block].call(datum[:response][:body].slice!(0, [datum[:chunk_size], remaining].min), [remaining - datum[:chunk_size], 0].max, content_length) remaining -= datum[:chunk_size] end end datum end |
#retry_limit ⇒ Object
316 317 318 319 |
# File 'lib/excon/connection.rb', line 316 def retry_limit Excon.display_warning("Excon::Connection#retry_limit is deprecated, use Excon::Connection#data[:retry_limit]. (#{caller.first})") @data[:retry_limit] ||= DEFAULT_RETRY_LIMIT end |
#retry_limit=(new_retry_limit) ⇒ Object
311 312 313 314 |
# File 'lib/excon/connection.rb', line 311 def retry_limit=(new_retry_limit) Excon.display_warning("Excon::Connection#retry_limit= is deprecated, pass :retry_limit to the initializer (#{caller.first})") @data[:retry_limit] = new_retry_limit end |