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