class EventMachine::HttpConnection

Attributes

conn[R]
connopts[RW]
deferred[R]
error[RW]
uri[RW]

Public Class Methods

new() click to toggle source
# File lib/em-http/http_connection.rb, line 43
def initialize
  @deferred = true
  @middleware = []
end

Public Instance Methods

activate_connection(client) click to toggle source
# File lib/em-http/http_connection.rb, line 53
def activate_connection(client)
  begin
    EventMachine.bind_connect(@connopts.bind, @connopts.bind_port,
                              @connopts.host, @connopts.port,
                              HttpStubConnection) do |conn|
      post_init

      @deferred = false
      @conn = conn

      conn.parent = self
      conn.pending_connect_timeout = @connopts.connect_timeout
      conn.comm_inactivity_timeout = @connopts.inactivity_timeout
    end

    finalize_request(client)
  rescue EventMachine::ConnectionError => e
    #
    # Currently, this can only fire on initial connection setup
    # since #connect is a synchronous method. Hence, rescue the exception,
    # and return a failed deferred which fail any client request at next
    # tick.  We fail at next tick to keep a consistent API when the newly
    # created HttpClient is failed. This approach has the advantage to
    # remove a state check of @deferred_status after creating a new
    # HttpRequest. The drawback is that users may setup a callback which we
    # know won't be used.
    #
    # Once there is async-DNS, then we'll iterate over the outstanding
    # client requests and fail them in order.
    #
    # Net outcome: failed connection will invoke the same ConnectionError
    # message on the connection deferred, and on the client deferred.
    #
    EM.next_tick{client.close(e.message)}
  end
end
close(reason = nil)
Alias for: unbind
conn=(c) click to toggle source
# File lib/em-http/http_connection.rb, line 48
def conn=(c)
  @conn = c
  @deferred = false
end
connection_completed() click to toggle source
# File lib/em-http/http_connection.rb, line 159
def connection_completed
  @peer = @conn.get_peername

  if @connopts.socks_proxy?
    socksify(client.req.uri.host, client.req.uri.inferred_port, *@connopts.proxy[:authorization]) { start }
  elsif @connopts.connect_proxy?
    connectify(client.req.uri.host, client.req.uri.inferred_port, *@connopts.proxy[:authorization]) { start }
  else
    start
  end
end
finalize_request(c) click to toggle source
# File lib/em-http/http_connection.rb, line 96
def finalize_request(c)
  @conn.callback { c.connection_completed }

  middleware.each do |m|
    c.callback(&m.method(:response)) if m.respond_to?(:response)
  end

  @clients.push c
end
middleware() click to toggle source
# File lib/em-http/http_connection.rb, line 106
def middleware
  [HttpRequest.middleware, @middleware].flatten
end
peer() click to toggle source
# File lib/em-http/http_connection.rb, line 146
def peer
  Socket.unpack_sockaddr_in(@peer)[1] rescue nil
end
post_init() click to toggle source
# File lib/em-http/http_connection.rb, line 110
def post_init
  @clients = []
  @pending = []

  @p = Http::Parser.new
  @p.header_value_type = :mixed
  @p.on_headers_complete = proc do |h|
    if client
      client.parse_response_header(h, @p.http_version, @p.status_code)
      :reset if client.req.no_body?
    else
      # if we receive unexpected data without a pending client request
      # reset the parser to avoid firing any further callbacks and close
      # the connection because we're processing invalid HTTP
      @p.reset!
      unbind
    end
  end

  @p.on_body = proc do |b|
    client.on_body_data(b)
  end

  @p.on_message_complete = proc do
    if !client.continue?
      c = @clients.shift
      c.state = :finished
      c.on_request_complete
    end
  end
end
receive_data(data) click to toggle source
# File lib/em-http/http_connection.rb, line 150
def receive_data(data)
  begin
    @p << data
  rescue HTTP::Parser::Error => e
    c = @clients.shift
    c.nil? ? unbind(e.message) : c.on_error(e.message)
  end
end
redirect(client, new_location) click to toggle source
# File lib/em-http/http_connection.rb, line 176
def redirect(client, new_location)
  old_location = client.req.uri
  new_location = client.req.set_uri(new_location)

  if client.req.keepalive
    # Application requested a keep-alive connection but one of the requests
    # hits a cross-origin redirect. We need to open a new connection and
    # let both connections proceed simultaneously.
    if old_location.origin != new_location.origin
      conn = HttpConnection.new
      client.conn = conn
      conn.connopts = @connopts
      conn.uri = client.req.uri
      conn.activate_connection(client)

    # If the redirect is a same-origin redirect on a keep-alive request
    # then immidiately dispatch the request over existing connection.
    else
      @clients.push client
      client.connection_completed
    end
  else
    # If connection is not keep-alive the unbind will fire and we'll
    # reconnect using the same connection object.
    @pending.push client
  end
end
send_data(data) click to toggle source
# File lib/em-http/http_connection.rb, line 235
def send_data(data)
  @conn.send_data data
end
setup_request(method, options = {}, c = nil) click to toggle source
# File lib/em-http/http_connection.rb, line 90
def setup_request(method, options = {}, c = nil)
  c ||= HttpClient.new(self, HttpClientOptions.new(@uri, options, method))
  @deferred ? activate_connection(c) : finalize_request(c)
  c
end
start() click to toggle source
# File lib/em-http/http_connection.rb, line 171
def start
  @conn.start_tls(@connopts.tls) if client && client.req.ssl?
  @conn.succeed
end
stream_file_data(filename, args = {}) click to toggle source
# File lib/em-http/http_connection.rb, line 239
def stream_file_data(filename, args = {})
  @conn.stream_file_data filename, args
end
unbind(reason = nil) click to toggle source
# File lib/em-http/http_connection.rb, line 204
def unbind(reason = nil)
  @clients.map { |c| c.unbind(reason) }

  if r = @pending.shift
    @clients.push r

    r.reset!
    @p.reset!

    begin
      @conn.set_deferred_status :unknown

      if @connopts.proxy
        @conn.reconnect(@connopts.host, @connopts.port)
      else
        @conn.reconnect(r.req.host, r.req.port)
      end

      @conn.pending_connect_timeout = @connopts.connect_timeout
      @conn.comm_inactivity_timeout = @connopts.inactivity_timeout
      @conn.callback { r.connection_completed }
    rescue EventMachine::ConnectionError => e
      @clients.pop.close(e.message)
    end
  else
    @deferred = true
    @conn.close_connection
  end
end
Also aliased as: close
use(klass, *args, &block) click to toggle source
# File lib/em-http/http_connection.rb, line 142
def use(klass, *args, &block)
  @middleware << klass.new(*args, &block)
end

Private Instance Methods

client() click to toggle source
# File lib/em-http/http_connection.rb, line 245
def client
  @clients.first
end