Skip to content

Commit

Permalink
split connection and request options
Browse files Browse the repository at this point in the history
  • Loading branch information
igrigorik committed Apr 21, 2011
1 parent 5e799ee commit 28b985f
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 105 deletions.
3 changes: 2 additions & 1 deletion lib/em-http.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
require 'em-http/http_connection'
require 'em-http/http_header'
require 'em-http/http_encoding'
require 'em-http/http_options'
require 'em-http/http_client_options'
require 'em-http/http_connection_options'
require 'em-http/client'
require 'em-http/multi'
require 'em-http/request'
Expand Down
52 changes: 19 additions & 33 deletions lib/em-http/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,11 @@ class HttpClient
CRLF="\r\n"

attr_accessor :state, :response
attr_reader :response_header, :error, :content_charset, :req, :options
attr_reader :response_header, :error, :content_charset, :req

def initialize(conn, req, options)
def initialize(conn, options)
@conn = conn

@req = req
@method = req.method
@options = options
@req = options

@stream = nil
@headers = nil
Expand All @@ -49,7 +46,7 @@ def peer; @conn.peer; end
def connection_completed
@state = :response_header

head, body = build_request, @options[:body]
head, body = build_request, @req.body
@conn.middleware.each do |m|
head, body = m.request(self, head, body) if m.respond_to?(:request)
end
Expand Down Expand Up @@ -78,7 +75,7 @@ def redirect?
def unbind
if finished?
if redirect?
@req.options[:followed] += 1
@req.followed += 1
@conn.redirect(self, @response_header.location)
else
succeed(self)
Expand All @@ -102,31 +99,20 @@ def normalize_body(body)
body.is_a?(Hash) ? form_encode_body(body) : body
end

def proxy?; !@options[:proxy].nil?; end
def http_proxy?; proxy? && [nil, :http].include?(@options[:proxy][:type]); end

def ssl?; @req.uri.scheme == "https" || @req.uri.port == 443; end

def continue?
@response_header.status == 100 && (@method == 'POST' || @method == 'PUT')
end

def pass_cookies?
@options[:pass_cookies].nil? || @options[:pass_cookies]
@response_header.status == 100 && (@req.method == 'POST' || @req.method == 'PUT')
end

def cookies
@cookies ||= []
end

def build_request
head = @options[:head] ? munge_header_keys(@options[:head]) : {}
proxy = @options[:proxy]
head = @req.headers ? munge_header_keys(@req.headers) : {}
proxy = @req.proxy

if http_proxy?
# initialize headers for the http proxy
head = proxy[:head] ? munge_header_keys(proxy[:head]) : {}
head['proxy-authorization'] = proxy[:authorization] if proxy[:authorization]
if @req.http_proxy?
head['proxy-authorization'] = @req.proxy[:authorization] if @req.proxy[:authorization]
end

# Set the cookie header if provided
Expand All @@ -136,7 +122,7 @@ def build_request
head['cookie'] = cookies.compact.uniq.join("; ").squeeze(";") unless cookies.empty?

# Set connection close unless keepalive
unless @options[:keepalive]
if !@req.keepalive
head['connection'] = 'close'
end

Expand All @@ -151,8 +137,8 @@ def build_request

def send_request(head, body)
body = normalize_body(body)
file = @options[:file]
query = @options[:query]
file = @req.file
query = @req.query

# Set the Content-Length if file is given
head['content-length'] = File.size(file) if file
Expand All @@ -161,19 +147,19 @@ def send_request(head, body)
head['content-length'] = body.bytesize if body

# Set content-type header if missing and body is a Ruby hash
if not head['content-type'] and @options[:body].is_a? Hash
if not head['content-type'] and @req.body.is_a? Hash
head['content-type'] = 'application/x-www-form-urlencoded'
end

request_header ||= encode_request(@method, @req.uri, query, @conn.opts.proxy)
request_header ||= encode_request(@req.method, @req.uri, query, @conn.connopts.proxy)
request_header << encode_headers(head)
request_header << CRLF
@conn.send_data request_header

if body
@conn.send_data body
elsif @options[:file]
@conn.stream_file_data @options[:file], :http_chunks => false
elsif @req.file
@conn.stream_file_data @req.file, :http_chunks => false
end
end

Expand Down Expand Up @@ -218,7 +204,7 @@ def parse_response_header(header, version, status)
end

# add set-cookie's to cookie list
cookies << @response_header.cookie if @response_header.cookie && pass_cookies?
cookies << @response_header.cookie if @response_header.cookie && @req.pass_cookies

# correct location header - some servers will incorrectly give a relative URI
if @response_header.location
Expand All @@ -242,7 +228,7 @@ def parse_response_header(header, version, status)
# Fire callbacks immediately after recieving header requests
# if the request method is HEAD. In case of a redirect, terminate
# current connection and reinitialize the process.
if @method == "HEAD"
if @req.method == "HEAD"
@state = :finished
return
end
Expand Down
57 changes: 57 additions & 0 deletions lib/em-http/http_client_options.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
class HttpClientOptions
attr_reader :uri, :method, :host, :port, :proxy
attr_reader :headers, :file, :body, :query
attr_reader :keepalive, :redirects, :pass_cookies

attr_accessor :followed

def initialize(uri, options, method)
set_uri(uri, options)

@keepalive = options[:keepalive] || false # default to single request per connection
@redirects = options[:redirects] ||= 0 # default number of redirects to follow
@followed = options[:followed] ||= 0 # keep track of number of followed requests

@method = method.to_s.upcase
@headers = options[:head] || {}
@proxy = options[:proxy] || {}
@query = options[:query]

@file = options[:file]
@body = options[:body]

@pass_cookies = options[:pass_cookies] || false
end

def follow_redirect?; @followed < @redirects; end
def http_proxy?; @proxy && [nil, :http].include?(@proxy[:type]); end
def ssl?; @uri.scheme == "https" || @uri.port == 443; end

def set_uri(uri, options)
uri = uri.kind_of?(Addressable::URI) ? uri : Addressable::URI::parse(uri.to_s)

uri.path = '/' if uri.path.empty?
if path = options.delete(:path)
uri.path = path
end

@uri = uri

# Make sure the ports are set as Addressable::URI doesn't
# set the port if it isn't there
if @uri.scheme == "https"
@uri.port ||= 443
else
@uri.port ||= 80
end

if proxy = options[:proxy]
@host = proxy[:host]
@port = proxy[:port]
else
@host = @uri.host
@port = @uri.port
end

end
end
22 changes: 10 additions & 12 deletions lib/em-http/http_connection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,15 @@ class FailedConnection
include HTTPMethods
include Deferrable

attr_accessor :error, :opts
attr_accessor :error

def initialize(req)
@opts = req
def initialize(uri, opts)
@connopts = opts
@uri = uri
end

def setup_request(method, options)
c = HttpClient.new(self, HttpOptions.new(@opts.uri, options, method), options)
c = HttpClient.new(self, HttpClientOptions.new(@uri, options, method))
c.close(@error)
c
end
Expand All @@ -30,10 +31,10 @@ class HttpConnection < Connection
include Deferrable
include Socksify

attr_accessor :error, :opts
attr_accessor :error, :connopts, :uri

def setup_request(method, options = {})
c = HttpClient.new(self, HttpOptions.new(@opts.uri, options, method), options)
c = HttpClient.new(self, HttpClientOptions.new(@uri, options, method))
callback { c.connection_completed }

middleware.each do |m|
Expand Down Expand Up @@ -90,18 +91,15 @@ def receive_data(data)
end

def connection_completed
if @opts.proxy && @opts.proxy[:type] == :socks5
socksify(client.req.uri.host, client.req.uri.port, *@opts.proxy[:authorization]) { start }

if @connopts.proxy && @connopts.proxy[:type] == :socks5
socksify(client.req.uri.host, client.req.uri.port, *@connopts.proxy[:authorization]) { start }
else
start
end
end

def start
ssl = @opts.options[:tls] || @opts.options[:ssl] || {}
start_tls(ssl) if client && client.ssl?

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

Expand Down
23 changes: 23 additions & 0 deletions lib/em-http/http_connection_options.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
class HttpConnectionOptions
attr_reader :host, :port, :tls, :proxy
attr_reader :connect_timeout, :inactivity_timeout

def initialize(uri, options)
@connect_timeout = options[:connect_timeout] || 5 # default connection setup timeout
@inactivity_timeout = options[:inactivity_timeout] ||= 10 # default connection inactivity (post-setup) timeout

@tls = options[:tls] || options[:ssl] || {}
@proxy = options[:proxy]

uri = uri.kind_of?(Addressable::URI) ? uri : Addressable::URI::parse(uri.to_s)
uri.port = (uri.scheme == "https" ? (uri.port || 443) : (uri.port || 80))

if proxy = options[:proxy]
@host = proxy[:host]
@port = proxy[:port]
else
@host = uri.host
@port = uri.port
end
end
end
53 changes: 0 additions & 53 deletions lib/em-http/http_options.rb

This file was deleted.

13 changes: 7 additions & 6 deletions lib/em-http/request.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ class HttpRequest

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

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

c.pending_connect_timeout = req.options[:connect_timeout]
c.comm_inactivity_timeout = req.options[:inactivity_timeout]
c.pending_connect_timeout = connopt.connect_timeout
c.comm_inactivity_timeout = connopt.inactivity_timeout
end

rescue EventMachine::ConnectionError => e
Expand All @@ -26,7 +27,7 @@ def self.new(uri, options={})
# Net outcome: failed connection will invoke the same ConnectionError
# message on the connection deferred, and on the client deferred.
#
conn = EventMachine::FailedConnection.new(req)
conn = EventMachine::FailedConnection.new(uri, connopt)
conn.error = e.message
conn.fail
conn
Expand Down

0 comments on commit 28b985f

Please sign in to comment.