diff --git a/lib/em-http.rb b/lib/em-http.rb index 5ac364ea..bc8d0d45 100644 --- a/lib/em-http.rb +++ b/lib/em-http.rb @@ -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' diff --git a/lib/em-http/client.rb b/lib/em-http/client.rb index 63bc9772..edede6d6 100644 --- a/lib/em-http/client.rb +++ b/lib/em-http/client.rb @@ -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 @@ -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 @@ -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) @@ -102,17 +99,8 @@ 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 @@ -120,13 +108,11 @@ def 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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/lib/em-http/http_client_options.rb b/lib/em-http/http_client_options.rb new file mode 100644 index 00000000..07e66495 --- /dev/null +++ b/lib/em-http/http_client_options.rb @@ -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 diff --git a/lib/em-http/http_connection.rb b/lib/em-http/http_connection.rb index 61414612..f157488c 100644 --- a/lib/em-http/http_connection.rb +++ b/lib/em-http/http_connection.rb @@ -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 @@ -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| @@ -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 diff --git a/lib/em-http/http_connection_options.rb b/lib/em-http/http_connection_options.rb new file mode 100644 index 00000000..7db81635 --- /dev/null +++ b/lib/em-http/http_connection_options.rb @@ -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 diff --git a/lib/em-http/http_options.rb b/lib/em-http/http_options.rb deleted file mode 100644 index c8c100ea..00000000 --- a/lib/em-http/http_options.rb +++ /dev/null @@ -1,53 +0,0 @@ -class HttpOptions - attr_reader :uri, :method, :host, :port, :options - - def initialize(uri, options, method = :none) - @options = options - @method = method.to_s.upcase - - set_uri(uri) - - @options[:keepalive] ||= false # default to single request per connection - @options[:redirects] ||= 0 # default number of redirects to follow - @options[:followed] ||= 0 # keep track of number of followed requests - - @options[:connect_timeout] ||= 5 # default connection setup timeout - @options[:inactivity_timeout] ||= 10 # default connection inactivity (post-setup) timeout - end - - def proxy - @options[:proxy] - end - - def follow_redirect? - @options[:followed] < @options[:redirects] - end - - def set_uri(uri) - 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 diff --git a/lib/em-http/request.rb b/lib/em-http/request.rb index 2bbcb4c9..7cad3e82 100644 --- a/lib/em-http/request.rb +++ b/lib/em-http/request.rb @@ -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 @@ -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