2009年11月2日月曜日

Ruby, HTTP, timeout, rescue のメモ

Ruby で http 経由で画像をダウンロードしようとして色々嵌まった。
嵌まった足跡満載のコード

begin
  uri = URI.parse(uri_str)
  http = Net::HTTP.new(uri.host, uri.port)
  http.open_timeout = 5
  http.read_timeout = 20
  http.start do |http_local|
    begin
      response = http_local.get(uri.path)
      if response["content-type"] =~ /image\/([^\s]+)/
        ext = $1
        fname = IMAGEDIR+"/#{wid}/#{id}.#{ext}"
        open(fname, "w") do |w|
          w.write response.body
        end
      end
    rescue Errno::ETIMEDOUT, TimeoutError, Timeout::Error, Exception
      log.puts $!.inspect
    end
  end
rescue Errno::ETIMEDOUT, TimeoutError, Timeout::Error, Exception
  log.puts $!.inspect
end

  • http.open_timeout は HTTP.start(uri) do ... の中で指定しても意味がない。startでopenされてしまうから。
  • timeout の例外は素の rescue (StandardExceptionを受ける)では拾えない。で、何を投げるかnet/http のドキュメントに書かれてない。
    • ドキュメントにない以上、 おすすめは "rescue Object" だ。がっかり。
  • http.start はスレッドですか?
    • スレッドだとして、終了までブロッキングしますか?。とか迷って http_local とかできてしまった。
    • 例外が捕まらないから Thread からの例外なのかと思って迷走してしまった。

 ドキュメントに載せるサンプルがいまいちなのだと思う。こんなサンプルだったら迷走せずに済んだ。
begin
  uri = URI.parse(uri_str)
  http = Net::HTTP.new(uri.host, uri.port)
  http.open_timeout = 5
  http.read_timeout = 20
  http.start do
    response = http.get(uri.path)
    do_something(response)
  end
rescue Object # orz
  # handle_exception
end
 うーん、いや、サンプルはもっと練られていた方がうれしいな。
open_timeout とかは、HTTP.start の optional 引数(:open_timeout => 5) とかにできた方がいいな。
http.start do はもうブロックにする意味がない。すると start って名前もなんだか。

以下疑似コード。動かないけど、こんな感じであってほしかった。

begin
  uri = URI.parse(uri_str)
  response = Net::HTTP.get(uri.host, uri.port, :open_timeout => 5, :read_timeout =>20)
  do_something(response)
rescue HTTP::TimeoutException
  # handle_timeout
rescue
  # handle_exception
end

0 件のコメント:

コメントを投稿