class Excon::Connection

def closed?

def closed?
  sockets[socket_key] && sockets[socket_key].closed?
end

def connect

def connect
  new_socket = TCPSocket.open(@connection[:host], @connection[:port])
  if @connection[:scheme] == 'https'
    @ssl_context = OpenSSL::SSL::SSLContext.new
    @ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE
    new_socket = OpenSSL::SSL::SSLSocket.new(new_socket, @ssl_context)
    new_socket.sync_close = true
    new_socket.connect
  end
  new_socket
end

def initialize(url, params = {})

Options Hash: (**params)
  • :scheme (String) -- The protocol; 'https' causes OpenSSL to be used
  • :query (Hash) -- 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
  • :port (Fixnum) -- The port on which to connect, to the destination host
  • :path (String) -- Default path; appears after 'scheme://host:port/'. Only used if params[:path] is not supplied to Connection#request
  • :host (String) -- The destination host's reachable DNS name or IP, in the form of a String
  • :headers (Hash) -- The default headers to supply in a request. Only used if params[:headers] is not supplied to Connection#request
  • :body (String) -- Default text to be sent over a socket. Only used if :body absent in Connection#request params

Parameters:
  • params (Hash) -- One or more optional params
  • url (String) -- The destination URL
def initialize(url, params = {})
  uri = URI.parse(url)
  @connection = {
    :headers  => { 'Host' => uri.host },
    :host     => uri.host,
    :path     => uri.path,
    :port     => uri.port,
    :query    => uri.query,
    :scheme   => uri.scheme
  }.merge!(params)
  set_socket_key
  reset
end

def request(params, &block)

Options Hash: (**params)
  • :scheme (String) -- The protocol; 'https' causes OpenSSL to be used
  • :query (Hash) -- appended to the 'scheme://host:port/path/' in the form of '?key=value'
  • :port (Fixnum) -- The port on which to connect, to the destination host
  • :path (String) -- appears after 'scheme://host:port/'
  • :host (String) -- The destination host's reachable DNS name or IP, in the form of a String
  • :headers (Hash) -- The default headers to supply in a request
  • :body (String) -- text to be sent over a socket

Parameters:
  • params (Hash) -- One or more optional params, override defaults set in Connection.new

Other tags:
    Yield: - @see Response#self.parse
def request(params, &block)
  begin
    # connection has defaults, merge in new params to override
    params = @connection.merge(params)
    params[:headers] = @connection[:headers].merge(params[:headers] || {})
    # if path is empty or doesn't start with '/', insert one
    unless params[:path][0..0] == '/'
      params[:path].insert(0, '/')
    end
    # start with "METHOD /path"
    request = params[:method].to_s.upcase << ' ' << params[:path]
    # add query to path, if there is one
    case params[:query]
    when String
      request << '?' << params[:query]
    when Hash
      request << '?'
      for key, values in params[:query]
        for value in [*values]
          value_string = value && ('=' << CGI.escape(value.to_s))
          request << key.to_s << value_string.to_s << '&'
        end
      end
      request.chop! # remove trailing '&'
    end
    # finish first line with "HTTP/1.1\r\n"
    request << HTTP_1_1
    # calculate content length and set to handle non-ascii
    params[:headers]['Content-Length'] = case params[:body]
    when File
      params[:body].binmode
      File.size(params[:body].path)
    when String
      if params[:body].respond_to?(:force_encoding)
        params[:body].force_encoding('BINARY')
      end
      params[:body].length
    else
      0
    end
    # add headers to request
    for key, value in params[:headers]
      request << key.to_s << ': ' << value.to_s << CR_NL
    end
    # add additional "\r\n" to indicate end of headers
    request << CR_NL
    # write out the request, sans body
    socket.write(request)
    # write out the body
    if params[:body]
      if params[:body].is_a?(String)
        socket.write(params[:body])
      else
        while chunk = params[:body].read(CHUNK_SIZE)
          socket.write(chunk)
        end
      end
    end
    # read the response
    response = Excon::Response.parse(socket, params, &block)
    if response.headers['Connection'] == 'close'
      reset
    end
    response
  rescue => socket_error
    reset
    raise(Excon::Errors::SocketError.new(socket_error))
  end
  if params[:expects] && ![*params[:expects]].include?(response.status)
    reset
    raise(Excon::Errors.status_error(params, response))
  else
    response
  end
rescue => request_error
  if params[:idempotent] &&
      (request_error.is_a?(Excon::Errors::SocketError) ||
      (request_error.is_a?(Excon::Errors::HTTPStatusError) && response.status != 404))
    retries_remaining ||= 4
    retries_remaining -= 1
    if retries_remaining > 0
      retry
    else
      raise(request_error)
    end
  else
    raise(request_error)
  end
end

def reset

def reset
  (old_socket = sockets.delete(socket_key)) && old_socket.close
end

def set_socket_key

def set_socket_key
  @connection[:socket_key] = "#{@connection[:host]}:#{@connection[:port]}"
end

def socket

def socket
  if closed?
    reset
  end
  sockets[socket_key] ||= connect
end

def socket_key

def socket_key
  @connection[:socket_key]
end

def sockets

def sockets
  Thread.current[:_excon_sockets] ||= {}
end