Catching Timeout::Error from Net::HTTP::new.start()

I’ve decided to add reCAPTCHA ‘prove you’re not a bot’ to my signup
page, and plan on sprinkling it around in various places when people
want to post to forums, etc. I know I could use the thing that is
already written… but since a simple ‘gem install recaptcha’ does
not work, but instead needs a --source flag, I won’t use it.

I also want to know what my code is doing, so I am writing my own. :slight_smile:

In the verify method, I have:

Return nil if it verified, or an error if it did not. If an error

is returned, this should be passed to the next recaptcha request.

def verify_recaptcha(params)
uri = URI.parse(‘http://blackhole.isc.org/verify’)

req = Net::HTTP::Post.new(uri.path)
req['User-Agent'] = "eq2guild.flame.org-fetchbot/1.0"
req['Keep-Alive'] = 'no'
req['Connection'] = 'close'

data = {
  "privatekey" => '--secret--',
  "remoteip" => request.remote_ip,
  "challenge" => params[:recaptcha_challenge_field],
  "response" => params[:recaptcha_response_field],
}
req.set_form_data(data, '&')

http = Net::HTTP::new(uri.host, uri.port)
http.open_timeout = 10
http.read_timeout = 10

response = http.start do |http|
  http.request(req)
end

if response.code.to_s == "200"
  reply = response.body.split("\n")
  return nil if reply[0].chomp == 'true'
  return reply[1].chomp
end

end

This code works fine when I made the URI be the actual captcha
servers. However, I’ve chosen to allow users to simply bypass the
captcha check when the servers cannot be reached. I set the maximum
time the user would have to wait to 10 seconds, and expect to get a
timeout exception.

I call this with:

begin
result = verify_recaptcha(params)
rescue
# do nothing to prevent the save
end

user.save!

However… I am always getting the exception popped up the stack, and
my application controller bombs:

Timeout::Error in AccountController#signup

execution expired

/usr/pkg/lib/ruby/1.8/timeout.rb:54:in open' /usr/pkg/lib/ruby/1.8/net/http.rb:564:in connect’
/usr/pkg/lib/ruby/1.8/timeout.rb:56:in timeout' /usr/pkg/lib/ruby/1.8/timeout.rb:76:in timeout’
/usr/pkg/lib/ruby/1.8/net/http.rb:564:in connect' /usr/pkg/lib/ruby/1.8/net/http.rb:557:in do_start’
/usr/pkg/lib/ruby/1.8/net/http.rb:546:in start' /home/explorer/proj/svn/flame-svn/eq2guild/trunk/app/controllers/application.rb:83:in verify_recaptcha’
/home/explorer/proj/svn/flame-svn/eq2guild/trunk/app/controllers/account_controller.rb:31:in
signup' /home/explorer/proj/svn/flame-svn/eq2guild/trunk/app/controllers/application.rb:54:in set_timezone’

Clearly this is not what I expected! I expected to catch the
exception and let my (empty) rescue code take over. I plan on logging
the error there of course, eventually.

Any ideas why I cannot trap this exception?

–Michael

It turns out I found the ‘answer’ I wrapped my http fetch with:

begin
  response = http.start do |http|
    http.request(req)
  end
rescue Timeout::Error => e
  return nil # XXXMLG this is ok for now...
end

This traps the Timeout::Error

Now, are there two classes of exceptions in Ruby? An exception that
is trapped using the normal

begin

rescue

end

block and one that is not unless you specifically name it?

–Michael

begin

rescue

end
doesn’t actually mean rescue everything. It means rescue everything
that derives from StandardError, which apparently Timeout::Error
doesn’t. To rescue truely everything, you need rescue Exception. (see
http://faq.rubygarden.org/entry/show/105?controller_prefix=faq%2F)

Fred