class HTTP::Connection
A connection to the HTTP
server
Constants
- BUFFER_SIZE
Attempt to read this much data
- CLOSE
- HTTP_1_0
HTTP/1.0
- HTTP_1_1
HTTP/1.1
- KEEP_ALIVE
Allowed values for CONNECTION header
Attributes
Returned after HTTP
CONNECT (via proxy)
Public Class Methods
@param [HTTP::Request] req @param [HTTP::Options] options @raise [HTTP::ConnectionError] when failed to connect
# File lib/http/connection.rb, line 31 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 end
Public Instance Methods
Close the connection @return [void]
# File lib/http/connection.rb, line 128 def close @socket.close unless @socket.closed? @pending_response = false @pending_request = false end
Whether our connection has expired @return [Boolean]
# File lib/http/connection.rb, line 143 def expired? !@conn_expires_at || @conn_expires_at < Time.now end
@return [Boolean] whenever proxy connect failed
# File lib/http/connection.rb, line 61 def failed_proxy_connect? @failed_proxy_connect end
Callback for when we've reached the end of a response @return [void]
# File lib/http/connection.rb, line 116 def finish_response close unless keep_alive? @parser.reset @socket.reset_counter if @socket.respond_to?(:reset_counter) reset_timer @pending_response = false end
Whether we're keeping the conn alive @return [Boolean]
# File lib/http/connection.rb, line 137 def keep_alive? !!@keep_alive && !@socket.closed? end
Reads data from socket up until headers are loaded @return [void]
# File lib/http/connection.rb, line 105 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
Read a chunk of the body
@return [String] data chunk @return [nil] when no more data left
# File lib/http/connection.rb, line 90 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
Send a request to the server
@param [Request] req Request
to send to the server @return [nil]
# File lib/http/connection.rb, line 69 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
Private Instance Methods
Feeds some more data into parser @return [void]
# File lib/http/connection.rb, line 213 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
Resets expiration of persistent connection. @return [void]
# File lib/http/connection.rb, line 190 def reset_timer @conn_expires_at = Time.now + @keep_alive_timeout if @persistent end
Open tunnel through proxy
# File lib/http/connection.rb, line 166 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
Store whether the connection should be kept alive. Once we reset the parser, we lose all of this state. @return [void]
# File lib/http/connection.rb, line 197 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
Sets up SSL context and starts TLS if needed. @param (see initialize) @return [void]
# File lib/http/connection.rb, line 152 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