lib/ftw/agent.rb in ftw-0.0.6 vs lib/ftw/agent.rb in ftw-0.0.7

- old
+ new

@@ -1,8 +1,9 @@ require "ftw/namespace" require "ftw/request" require "ftw/connection" +require "ftw/protocol" require "ftw/pool" require "ftw/websocket" require "addressable/uri" require "cabin" require "logger" @@ -33,10 +34,13 @@ # response = agent.get!("http://www.google.com/") # puts response.body.head # # TODO(sissel): TBD: implement cookies... delicious chocolate chip cookies. class FTW::Agent + include FTW::Protocol + + # List of standard HTTP methods described in RFC2616 STANDARD_METHODS = %w(options get head post put delete trace connect) # Everything is private by default. # At the bottom of this class, public methods will be declared. private @@ -93,29 +97,20 @@ # Make a new websocket connection. # # This will send the http request. If the websocket handshake # is successful, a FTW::WebSocket instance will be returned. # Otherwise, a FTW::Response will be returned. + # + # See {#request} for what the 'uri' and 'options' parameters should be. def websocket!(uri, options={}) # TODO(sissel): Use FTW::Agent#upgrade! ? req = request("GET", uri, options) ws = FTW::WebSocket.new(req) response = execute(req) if ws.handshake_ok?(response) # response.body is a FTW::Connection ws.connection = response.body - - # TODO(sissel): Investigate this bug - # There seems to be a bug in http_parser.rb (or in this library) where - # websocket responses lead with a newline for some reason. Work around - # it. - data = response.body.read - if data[0] == "\n" - response.body.pushback(data[1..-1]) - else - response.body.pushback(data) - end return ws else return response end end # def websocket! @@ -156,16 +151,20 @@ # of this Request, it will be reused. Otherwise, a new connection # is opened. # # Redirects are always followed. # - # @params + # @param [FTW::Request] # @return [FTW::Response] the response for this request. def execute(request) # TODO(sissel): Make redirection-following optional, but default. - connection = connect(request.headers["Host"], request.port) + connection, error = connect(request.headers["Host"], request.port) + if !error.nil? + p :error => error + raise error + end connection.secure if request.protocol == "https" response = request.execute(connection) redirects = 0 while response.redirect? and response.headers.include?("Location") @@ -190,11 +189,16 @@ # agent's cookie store @logger.debug("Redirecting", :location => response.headers["Location"]) redirects += 1 request.use_uri(response.headers["Location"]) - connection = connect(request.headers["Host"], request.port) + connection, error = connect(request.headers["Host"], request.port) + # TODO(sissel): Do better error handling than raising. + if !error.nil? + p :error => error + raise error + end connection.secure if request.protocol == "https" response = request.execute(connection) end # RFC 2616 section 9.4, HEAD requests MUST NOT have a message body. @@ -211,18 +215,31 @@ # Returns a FTW::Connection connected to this host:port. def connect(host, port) address = "#{host}:#{port}" @logger.debug("Fetching from pool", :address => address) + error = nil connection = @pool.fetch(address) do @logger.info("New connection to #{address}") connection = FTW::Connection.new(address) - connection.connect - connection + error = connection.connect + if !error.nil? + # Return nil to the pool, so like, we failed.. + nil + else + # Otherwise return our new connection + connection + end end + + if !error.nil? + @logger.error("Connection failed", :destination => address, :error => error) + return nil, error + end + @logger.debug("Pool fetched a connection", :connection => connection) connection.mark - return connection + return connection, nil end # def connect public(:initialize, :execute, :websocket!, :upgrade!) end # class FTW::Agent