# File lib/em-http/client.rb, line 305 def connect_proxy?; proxy? && (@options[:proxy][:use_connect] == true); end
start HTTP request once we establish connection to host
# File lib/em-http/client.rb, line 230 def connection_completed # if we need to negotiate the proxy connection first, then # issue a CONNECT query and wait for 200 response if connect_proxy? and @state == :response_header @state = :connect_proxy send_request_header # if connecting via proxy, then state will be :proxy_connected, # indicating successful tunnel. from here, initiate normal http # exchange else @state = :response_header ssl = @options[:tls] || @options[:ssl] || {} start_tls(ssl) if @uri.scheme == "https" or @uri.port == 443 send_request_header send_request_body end end
assign disconnect callback for websocket
# File lib/em-http/client.rb, line 275 def disconnect(&blk) @disconnect = blk end
Response processing
# File lib/em-http/client.rb, line 434 def dispatch while case @state when :connect_proxy parse_response_header 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 :websocket process_websocket when :finished, :invalid break else raise RuntimeError, "invalid state: #{@state}" end end
# File lib/em-http/client.rb, line 293 def normalize_body @normalized_body ||= begin if @options[:body].is_a? Hash @options[:body].to_params else @options[:body] end end end
Called when part of the body has been read
# File lib/em-http/client.rb, line 384 def on_body_data(data) if @content_decoder begin @content_decoder << data rescue HttpDecoders::DecoderError on_error "Content-decoder error" end else on_decoded_body_data(data) end end
# File lib/em-http/client.rb, line 396 def on_decoded_body_data(data) if @stream @stream.call(data) else @response << data end end
request failed, invoke errback
# File lib/em-http/client.rb, line 261 def on_error(msg, dns_error = false) @error = msg # no connection signature on DNS failures # fail the connection directly dns_error == true ? fail(self) : unbind end
request is done, invoke the callback
# File lib/em-http/client.rb, line 250 def on_request_complete begin @content_decoder.finalize! if @content_decoder rescue HttpDecoders::DecoderError on_error "Content-decoder error" end close_connection end
# File lib/em-http/client.rb, line 213 def post_init @parser = HttpClientParser.new @data = EventMachine::Buffer.new @chunk_header = HttpChunkHeader.new @response_header = HttpResponseHeader.new @parser_nbytes = 0 @redirects = 0 @response = '' @error = '' @last_effective_url = nil @content_decoder = nil @stream = nil @disconnect = nil @state = :response_header end
# File lib/em-http/client.rb, line 304 def proxy?; !@options[:proxy].nil?; end
# File lib/em-http/client.rb, line 378 def receive_data(data) @data << data dispatch end
raw data push from the client (WebSocket) should only be invoked after handshake, otherwise it will inject data into the header exchange
frames need to start with 0x00-0x7f byte and end with an 0xFF byte. Per spec, we can also set the first byte to a value betweent 0x80 and 0xFF, followed by a leading length indicator
# File lib/em-http/client.rb, line 287 def send(data) if @state == :websocket send_data("\x00#{data}\xff") end end
# File lib/em-http/client.rb, line 368 def send_request_body if @options[:body] body = normalize_body send_data body return elsif @options[:file] stream_file_data @options[:file], :http_chunks => false end end
# File lib/em-http/client.rb, line 307 def send_request_header query = @options[:query] head = @options[:head] ? munge_header_keys(@options[:head]) : {} file = @options[:file] proxy = @options[:proxy] body = normalize_body request_header = nil if proxy # initialize headers for the http proxy head = proxy[:head] ? munge_header_keys(proxy[:head]) : {} head['proxy-authorization'] = proxy[:authorization] if proxy[:authorization] # if we need to negotiate the tunnel connection first, then # issue a CONNECT query to the proxy first. This is an optional # flag, by default we will provide full URIs to the proxy if @state == :connect_proxy request_header = HTTP_REQUEST_HEADER % ['CONNECT', "#{@uri.host}:#{@uri.port}"] end end if websocket? head['upgrade'] = 'WebSocket' head['connection'] = 'Upgrade' head['origin'] = @options[:origin] || @uri.host else # Set the Content-Length if file is given head['content-length'] = File.size(file) if file # Set the Content-Length if body is given head['content-length'] = body.bytesize if body # Set the cookie header if provided if cookie = head.delete('cookie') head['cookie'] = encode_cookie(cookie) end # Set content-type header if missing and body is a Ruby hash if not head['content-type'] and options[:body].is_a? Hash head['content-type'] = "application/x-www-form-urlencoded" end end # Set the Host header if it hasn't been specified already head['host'] ||= encode_host # Set the User-Agent if it hasn't been specified head['user-agent'] ||= "EventMachine HttpClient" # Record last seen URL @last_effective_url = @uri # Build the request headers request_header ||= encode_request(@method, @uri, query, proxy) request_header << encode_headers(head) request_header << CRLF send_data request_header end
assign a stream processing block
# File lib/em-http/client.rb, line 270 def stream(&blk) @stream = blk end
# File lib/em-http/client.rb, line 404 def unbind if (@state == :finished) && (@last_effective_url != @uri) && (@redirects < @options[:redirects]) # update uri to redirect location if we're allowed to traverse deeper @uri = @last_effective_url # keep track of the depth of requests we made in this session @redirects += 1 # swap current connection and reassign current handler req = HttpOptions.new(@method, @uri, @options) reconnect(req.host, req.port) @response_header = HttpResponseHeader.new @state = :response_header @response = '' @data.clear else if @state == :finished || (@state == :body && @bytes_remaining.nil?) succeed(self) else @disconnect.call(self) if @state == :websocket and @disconnect fail(self) end end end
Generated with the Darkfish Rdoc Generator 2.