HTTP client class implemented as a subclass of Coolio::TCPSocket. Encodes requests and allows streaming consumption of the response. Response is parsed with a Ragel-generated whitelist parser which supports chunked HTTP encoding.
loop = Coolio::Loop.default client = Coolio::HttpClient.connect("www.google.com").attach(loop) client.request('GET', '/search', query: {q: 'foobar'}) loop.run
Connect to the given server, with port 80 as the default
# File lib/cool.io/http_client.rb, line 141 def self.connect(addr, port = 80, *args) super end
# File lib/cool.io/http_client.rb, line 145 def initialize(socket) super @parser = HttpClientParser.new @parser_nbytes = 0 @state = :response_header @data = ::IO::Buffer.new @response_header = HttpResponseHeader.new @chunk_header = HttpChunkHeader.new end
Enable the HttpClient if it has been disabled
# File lib/cool.io/http_client.rb, line 185 def enable super dispatch unless @data.empty? end
Called when part of the body has been read
# File lib/cool.io/http_client.rb, line 195 def on_body_data(data) STDOUT.write data STDOUT.flush end
called by close
# File lib/cool.io/http_client.rb, line 206 def on_close if @state != :finished and @state == :body on_request_complete end end
Called when an error occurs dispatching the request
# File lib/cool.io/http_client.rb, line 213 def on_error(reason) close raise RuntimeError, reason end
Called when the request has completed
# File lib/cool.io/http_client.rb, line 201 def on_request_complete @state == :finished ? close : @state = :finished end
Called when response header has been received
# File lib/cool.io/http_client.rb, line 191 def on_response_header(response_header) end
Send an HTTP request and consume the response. Supports the following options:
head: {Key: Value} Specify an HTTP header, e.g. {'Connection': 'close'} query: {Key: Value} Specify query string parameters (auto-escaped) cookies: {Key: Value} Specify hash of cookies (auto-escaped) body: String Specify the request body (you must encode it for now)
# File lib/cool.io/http_client.rb, line 173 def request(method, path, options = {}) raise ArgumentError, "invalid request path" unless /^\// === path raise RuntimeError, "request already sent" if @requested @method, @path, @options = method, path, options @requested = true return unless @connected send_request end
Response processing
# File lib/cool.io/http_client.rb, line 280 def dispatch while enabled? and case @state when :response_header parse_response_header when :chunk_header parse_chunk_header when :chunk_body process_chunk_body when :chunk_footer process_chunk_footer when :response_footer process_response_footer when :body process_body when :finished, :invalid break else raise RuntimeError, "invalid state: #{@state}" end end
Coolio callbacks
# File lib/cool.io/http_client.rb, line 226 def on_connect @connected = true send_request if @method and @path end
# File lib/cool.io/http_client.rb, line 231 def on_read(data) @data << data dispatch end
Request sending
# File lib/cool.io/http_client.rb, line 240 def send_request send_request_header send_request_body end
# File lib/cool.io/http_client.rb, line 272 def send_request_body write @options[:body] if @options[:body] end
# File lib/cool.io/http_client.rb, line 245 def send_request_header query = @options[:query] head = @options[:head] ? munge_header_keys(@options[:head]) : {} cookies = @options[:cookies] body = @options[:body] # Set the Host header if it hasn't been specified already head['host'] ||= encode_host # Set the Content-Length if it hasn't been specified already and a body was given head['content-length'] ||= body ? body.length : 0 # Set the User-Agent if it hasn't been specified head['user-agent'] ||= "Coolio #{Coolio::VERSION}" # Default to Connection: close head['connection'] ||= 'close' # Build the request request_header = encode_request(@method, @path, query) request_header << encode_headers(head) request_header << encode_cookies(cookies) if cookies request_header << CRLF write request_header end