class HTTP::Connection

A connection to the HTTP server

def close

Returns:
  • (void) -
def close
  @socket.close unless @socket&.closed?
  @pending_response = false
  @pending_request  = false
end

def expired?

Returns:
  • (Boolean) -
def expired?
  !@conn_expires_at || @conn_expires_at < Time.now
end

def failed_proxy_connect?

Returns:
  • (Boolean) - whenever proxy connect failed
def failed_proxy_connect?
  @failed_proxy_connect
end

def finish_response

Returns:
  • (void) -
def finish_response
  close unless keep_alive?
  @parser.reset
  @socket.reset_counter if @socket.respond_to?(:reset_counter)
  reset_timer
  @pending_response = false
end

def finished_request?

def finished_request?
  !@pending_request && !@pending_response
end

def initialize(req, options)

Raises:
  • (HTTP::ConnectionError) - when failed to connect

Parameters:
  • options (HTTP::Options) --
  • req (HTTP::Request) --
def initialize(req, options)
  @persistent           = options.persistent?
  @keep_alive_timeout   = options.keep_alive_timeout.to_f
  @pending_request      = false
  @pending_response     = false
  @failed_proxy_connect = false
  @buffer               = "".b
  @parser = Response::Parser.new
  @socket = options.timeout_class.new(options.timeout_options)
  @socket.connect(options.socket_class, req.socket_host, req.socket_port, options.nodelay)
  send_proxy_connect_request(req)
  start_tls(req, options)
  reset_timer
rescue IOError, SocketError, SystemCallError => e
  raise ConnectionError, "failed to connect: #{e}", e.backtrace
rescue TimeoutError
  close
  raise
end

def keep_alive?

Returns:
  • (Boolean) -
def keep_alive?
  !!@keep_alive && !@socket.closed?
end

def read_headers!

Returns:
  • (void) -
def read_headers!
  until @parser.headers?
    result = read_more(BUFFER_SIZE)
    raise ConnectionError, "couldn't read response headers" if result == :eof
  end
  set_keep_alive
end

def read_more(size)

Returns:
  • (void) -
def read_more(size)
  return if @parser.finished?
  value = @socket.readpartial(size, @buffer)
  if value == :eof
    @parser << ""
    :eof
  elsif value
    @parser << value
  end
rescue IOError, SocketError, SystemCallError => e
  raise ConnectionError, "error reading from socket: #{e}", e.backtrace
end

def readpartial(size = BUFFER_SIZE)

Returns:
  • (nil) - when no more data left
  • (String) - data chunk
def readpartial(size = BUFFER_SIZE)
  return unless @pending_response
  chunk = @parser.read(size)
  return chunk if chunk
  finished = (read_more(size) == :eof) || @parser.finished?
  chunk    = @parser.read(size)
  finish_response if finished
  chunk || "".b
end

def reset_timer

Returns:
  • (void) -
def reset_timer
  @conn_expires_at = Time.now + @keep_alive_timeout if @persistent
end

def send_proxy_connect_request(req)

Open tunnel through proxy
def send_proxy_connect_request(req)
  return unless req.uri.https? && req.using_proxy?
  @pending_request = true
  req.connect_using_proxy @socket
  @pending_request  = false
  @pending_response = true
  read_headers!
  @proxy_response_headers = @parser.headers
  if @parser.status_code != 200
    @failed_proxy_connect = true
    return
  end
  @parser.reset
  @pending_response = false
end

def send_request(req)

Returns:
  • (nil) -

Parameters:
  • req (Request) -- Request to send to the server
def send_request(req)
  if @pending_response
    raise StateError, "Tried to send a request while one is pending already. Make sure you read off the body."
  end
  if @pending_request
    raise StateError, "Tried to send a request while a response is pending. Make sure you read off the body."
  end
  @pending_request = true
  req.stream @socket
  @pending_response = true
  @pending_request  = false
end

def set_keep_alive

Returns:
  • (void) -
def set_keep_alive
  return @keep_alive = false unless @persistent
  @keep_alive =
    case @parser.http_version
    when HTTP_1_0 # HTTP/1.0 requires opt in for Keep Alive
      @parser.headers[Headers::CONNECTION] == KEEP_ALIVE
    when HTTP_1_1 # HTTP/1.1 is opt-out
      @parser.headers[Headers::CONNECTION] != CLOSE
    else # Anything else we assume doesn't supportit
      false
    end
end

def start_tls(req, options)

Returns:
  • (void) -

Parameters:
  • () --
def start_tls(req, options)
  return unless req.uri.https? && !failed_proxy_connect?
  ssl_context = options.ssl_context
  unless ssl_context
    ssl_context = OpenSSL::SSL::SSLContext.new
    ssl_context.set_params(options.ssl || {})
  end
  @socket.start_tls(req.uri.host, options.ssl_socket_class, ssl_context)
end