Skip to content

Commit

Permalink
Defer connection until needed
Browse files Browse the repository at this point in the history
  • Loading branch information
wishdev committed May 12, 2011
1 parent 2712fbe commit 17b264c
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 57 deletions.
113 changes: 84 additions & 29 deletions lib/em-http/http_connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,91 @@ def put options = {}, &blk; setup_request(:put, options, &blk); end
def post options = {}, &blk; setup_request(:post, options, &blk); end
end

class FailedConnection
include HTTPMethods
class HttpStubConnection < Connection
include Deferrable
attr_reader :parent

attr_accessor :error
def parent=(p)
@parent = p
@parent.conn = self
end

def initialize(uri, opts)
@connopts = opts
@uri = uri
@peer = nil
def receive_data(data)
@parent.receive_data data
end

def setup_request(method, options)
c = HttpClient.new(self, HttpClientOptions.new(@uri, options, method))
c.close(@error)
c
def connection_completed
@parent.connection_completed
end

def unbind
@parent.unbind
end
end

class HttpConnection < Connection
class HttpConnection
include HTTPMethods
include Deferrable
include Socksify

attr_accessor :error, :connopts, :uri
attr_reader :deferred
attr_accessor :error, :connopts, :uri, :conn

def setup_request(method, options = {})
c = HttpClient.new(self, HttpClientOptions.new(@uri, options, method))
callback { c.connection_completed }
def initialize
@deferred = true
@middleware = []
end

def conn=(c)
@conn = c
@deferred = false
end

def activate_connection(client)
begin
EventMachine.connect(@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 will immediately
# fail any client request.
#
# 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.
#
client.close(e.message)
end
end

def setup_request(method, options = {}, c = nil)
c ||= HttpClient.new(self, HttpClientOptions.new(@uri, options, method))
if @deferred
activate_connection(c)
else
finalize_request(c)
end
c
end

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
c
end

def middleware
Expand All @@ -54,8 +103,6 @@ def post_init
@clients = []
@pending = []

@middleware = []

@p = Http::Parser.new
@p.on_headers_complete = proc do |h|
client.parse_response_header(h, @p.http_version, @p.status_code)
Expand Down Expand Up @@ -92,7 +139,7 @@ def receive_data(data)
end

def connection_completed
@peer = get_peername
@peer = @conn.get_peername

if @connopts.proxy && @connopts.proxy[:type] == :socks5
socksify(client.req.uri.host, client.req.uri.port, *@connopts.proxy[:authorization]) { start }
Expand All @@ -103,15 +150,15 @@ def connection_completed

def start
start_tls(@connopts.tls) if client && client.req.ssl?
succeed
@conn.succeed
end

def redirect(client)
@pending.push client
end

def unbind
@clients.map {|c| c.unbind }
@clients.map { |c| c.unbind }

if r = @pending.shift
@clients.push r
Expand All @@ -120,19 +167,27 @@ def unbind
@p.reset!

begin
set_deferred_status :unknown
reconnect(r.req.host, r.req.port)
callback { r.connection_completed }
@conn.set_deferred_status :unknown
@conn.reconnect(r.req.host, r.req.port)
@conn.callback { r.connection_completed }
rescue EventMachine::ConnectionError => e
@clients.pop.close(e.message)
end
end
end

def send_data(data)
@conn.send_data data
end

def stream_file_data(filename, args = {})
@conn.stream_file_data filename, args
end

private

def client
@clients.first
end
def client
@clients.first
end
end
end
33 changes: 5 additions & 28 deletions lib/em-http/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,12 @@ class HttpRequest
@middleware = []

def self.new(uri, options={})
begin
connopt = HttpConnectionOptions.new(uri, options)
connopt = HttpConnectionOptions.new(uri, options)

EventMachine.connect(connopt.host, connopt.port, HttpConnection) do |c|
c.connopts = connopt
c.uri = uri

c.pending_connect_timeout = connopt.connect_timeout
c.comm_inactivity_timeout = connopt.inactivity_timeout
end

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 will immediately
# fail any client request.
#
# 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.
#
conn = EventMachine::FailedConnection.new(uri, connopt)
conn.error = e.message
conn.fail
conn
end
c = HttpConnection.new
c.connopts = connopt
c.uri = uri
c
end

def self.use(klass, *args, &block)
Expand Down

0 comments on commit 17b264c

Please sign in to comment.