Skip to content

Timing HTTP Requests in Eventmachine

Intro

I’ve been playing with Ilya Grigorik’s slick em-http-client for Eventmachine, and I needed to record how long it was taking to connect to the remote web server and download the complete response. I had to inherit from the classes in his library, but it was pretty painless. Here’s what I did:

module EventMachine
  class TimedHttpClient  < HttpClient
    attr_reader :connect_time, :download_time

    def post_init
      @started_at = Time.now
      super
    end

    def connection_completed
      @connected_at = Time.now
      @connect_time = (@connected_at - @started_at) * 1000
      super
    end
    
    def on_request_complete
      @download_time = (Time.now - @connected_at) * 1000
      super
    end
  end

  class TimedHttpRequest < HttpRequest
    def send_request(&blk)
      begin
       EventMachine.connect(@host_to_connect, @port_to_connect,
        EventMachine::TimedHttpClient) { |c|
          c.uri = @uri
          c.method = @method
          c.options = @options
          c.comm_inactivity_timeout = @options[:timeout]
          c.pending_connect_timeout = @options[:timeout]
          blk.call(c) unless blk.nil?
        }
      rescue EventMachine::ConnectionError => e
        conn = EventMachine::HttpClient.new("")
        conn.on_error(e.message, true)
        conn
      end
    end
  end
end

Hooking in

The basic functionality of the modification is dead simple, we just override the HttpClient callbacks to record the time deltas before passing control back to the parent methods. connection_completed gets triggered when the TCP connection to the web server is finished and we’re ready to start downloading the content. on_request_complete is a method defined by em-http-client itself, that gets triggered when it’s finished downloading the HTTP response.

Patching up

Unfortunately, HttpRequest is hard-coded to use HttpClient as the Eventmachine handler. Since we want to use our inherited TimedHttpClient as the handler, we have to overwrite the code in HttpRequest that calls Eventmachine.connect and use our TimedHttpClient instead. That’s the only difference between the code here and the original HttpRequest#send_request method.

Trying it out

EventMachine.run do
  http = EventMachine::TimedHttpRequest.new('http://wordpress.com/').get
  http.callback do |r|
    puts "Downloaded: #{r.uri}"
    puts "Connection time: #{'%.1f' % http.connect_time} ms"
    puts "Download time:   #{'%.1f' % http.download_time} ms"
    
    EventMachine.stop
  end
end

And the result:

Downloaded: http://wordpress.com:80/
Connection time: 346.6 ms
Download time:   2140.1 ms
Advertisements