遠藤です。

open-uri を使って定期的に Web ページを取得するプログラムを走らせて
いると、実にさまざまな例外が投げられうることに気がつきました。

今のところ経験したことがあるのは以下の 7 つです (経験した順) 。

- Errno::ETIMEDOUT
- OpenURI::HTTPError
- Errno::ECONNRESET
- Timeout::Error
- EOFError
- Errno::EHOSTUNREACH
- SocketError


そこで質問です。

1) open-uri が投げうる例外は他にあるでしょうか (Errno::* はいくらでも
ありそうですが) 。

2) この中で本来投げられるべきでない例外はないでしょうか。EOFError は
少し怪しいと思っています。バグだとしても Net::HTTP か Timeout のバグ
だと思いますが。ちなみに再現させる方法はもちろんわかりません。

3) この状況に対して、open-uri のユーザはどのように対処することが要求
されているのでしょうか。4 つ思いつきました。

  A. StandardError を rescue してまとめて対処する

  B. open-uri が投げうる例外をすべて把握し、個々に対処するコードを書く

  C. そもそも robust 性が要求されるプログラムで open-uri や Net::HTTP
     を使うことが想定されていないので、使ってはいけない

  D. そもそも robust 性が要求されるプログラムで Ruby を (略)


ruby 1.8.7 (2008-08-11 patchlevel 72) [i486-linux] で申し訳ないのです
が、それぞれの例外が投げられたときのトレースを以下に貼り付けます。


/usr/lib/ruby/1.8/net/http.rb:560:in `initialize': Connection timed
out - connect(2) (Errno::ETIMEDOUT)
        from /usr/lib/ruby/1.8/net/http.rb:560:in `open'
        from /usr/lib/ruby/1.8/net/http.rb:560:in `connect'
        from /usr/lib/ruby/1.8/timeout.rb:53:in `timeout'
        from /usr/lib/ruby/1.8/timeout.rb:93:in `timeout'
        from /usr/lib/ruby/1.8/net/http.rb:560:in `connect'
        from /usr/lib/ruby/1.8/net/http.rb:553:in `do_start'
        from /usr/lib/ruby/1.8/net/http.rb:542:in `start'
        from /usr/lib/ruby/1.8/open-uri.rb:242:in `open_http'
        from /usr/lib/ruby/1.8/open-uri.rb:616:in `buffer_open'
        from /usr/lib/ruby/1.8/open-uri.rb:164:in `open_loop'
        from /usr/lib/ruby/1.8/open-uri.rb:162:in `catch'
        from /usr/lib/ruby/1.8/open-uri.rb:162:in `open_loop'
        from /usr/lib/ruby/1.8/open-uri.rb:132:in `open_uri'
        from /usr/lib/ruby/1.8/open-uri.rb:518:in `open'
        from /usr/lib/ruby/1.8/open-uri.rb:30:in `open'


/usr/lib/ruby/1.8/open-uri.rb:277:in `open_http': 400 Bad Request
(OpenURI::HTTPError)
        from /usr/lib/ruby/1.8/open-uri.rb:616:in `buffer_open'
        from /usr/lib/ruby/1.8/open-uri.rb:164:in `open_loop'
        from /usr/lib/ruby/1.8/open-uri.rb:162:in `catch'
        from /usr/lib/ruby/1.8/open-uri.rb:162:in `open_loop'
        from /usr/lib/ruby/1.8/open-uri.rb:132:in `open_uri'
        from /usr/lib/ruby/1.8/open-uri.rb:518:in `open'
        from /usr/lib/ruby/1.8/open-uri.rb:30:in `open'


/usr/lib/ruby/1.8/net/protocol.rb:135:in `sysread': Connection reset
by peer (Errno::ECONNRESET)
        from /usr/lib/ruby/1.8/net/protocol.rb:135:in `rbuf_fill'
        from /usr/lib/ruby/1.8/timeout.rb:62:in `timeout'
        from /usr/lib/ruby/1.8/timeout.rb:93:in `timeout'
        from /usr/lib/ruby/1.8/net/protocol.rb:134:in `rbuf_fill'
        from /usr/lib/ruby/1.8/net/protocol.rb:116:in `readuntil'
        from /usr/lib/ruby/1.8/net/protocol.rb:126:in `readline'
        from /usr/lib/ruby/1.8/net/http.rb:2020:in `read_status_line'
        from /usr/lib/ruby/1.8/net/http.rb:2009:in `read_new'
        from /usr/lib/ruby/1.8/net/http.rb:1050:in `request'
        from /usr/lib/ruby/1.8/open-uri.rb:248:in `open_http'
        from /usr/lib/ruby/1.8/net/http.rb:543:in `start'
        from /usr/lib/ruby/1.8/open-uri.rb:242:in `open_http'
        from /usr/lib/ruby/1.8/open-uri.rb:616:in `buffer_open'
        from /usr/lib/ruby/1.8/open-uri.rb:164:in `open_loop'
        from /usr/lib/ruby/1.8/open-uri.rb:162:in `catch'
        from /usr/lib/ruby/1.8/open-uri.rb:162:in `open_loop'
        from /usr/lib/ruby/1.8/open-uri.rb:132:in `open_uri'
        from /usr/lib/ruby/1.8/open-uri.rb:518:in `open'
        from /usr/lib/ruby/1.8/open-uri.rb:30:in `open'


/usr/lib/ruby/1.8/timeout.rb:60:in `rbuf_fill': execution expired
(Timeout::Error)
        from /usr/lib/ruby/1.8/net/protocol.rb:134:in `rbuf_fill'
        from /usr/lib/ruby/1.8/net/protocol.rb:116:in `readuntil'
        from /usr/lib/ruby/1.8/net/protocol.rb:126:in `readline'
        from /usr/lib/ruby/1.8/net/http.rb:2020:in `read_status_line'
        from /usr/lib/ruby/1.8/net/http.rb:2009:in `read_new'
        from /usr/lib/ruby/1.8/net/http.rb:1050:in `request'
        from /usr/lib/ruby/1.8/open-uri.rb:248:in `open_http'
        from /usr/lib/ruby/1.8/net/http.rb:543:in `start'
        from /usr/lib/ruby/1.8/open-uri.rb:242:in `open_http'
        from /usr/lib/ruby/1.8/open-uri.rb:616:in `buffer_open'
        from /usr/lib/ruby/1.8/open-uri.rb:164:in `open_loop'
        from /usr/lib/ruby/1.8/open-uri.rb:162:in `catch'
        from /usr/lib/ruby/1.8/open-uri.rb:162:in `open_loop'
        from /usr/lib/ruby/1.8/open-uri.rb:132:in `open_uri'
        from /usr/lib/ruby/1.8/open-uri.rb:518:in `open'
        from /usr/lib/ruby/1.8/open-uri.rb:30:in `open'


/usr/lib/ruby/1.8/net/protocol.rb:135:in `sysread': end of file
reached (EOFError)
        from /usr/lib/ruby/1.8/net/protocol.rb:135:in `rbuf_fill'
        from /usr/lib/ruby/1.8/timeout.rb:62:in `timeout'
        from /usr/lib/ruby/1.8/timeout.rb:93:in `timeout'
        from /usr/lib/ruby/1.8/net/protocol.rb:134:in `rbuf_fill'
        from /usr/lib/ruby/1.8/net/protocol.rb:116:in `readuntil'
        from /usr/lib/ruby/1.8/net/protocol.rb:126:in `readline'
        from /usr/lib/ruby/1.8/net/http.rb:2020:in `read_status_line'
        from /usr/lib/ruby/1.8/net/http.rb:2009:in `read_new'
        from /usr/lib/ruby/1.8/net/http.rb:1050:in `request'
        from /usr/lib/ruby/1.8/open-uri.rb:248:in `open_http'
        from /usr/lib/ruby/1.8/net/http.rb:543:in `start'
        from /usr/lib/ruby/1.8/open-uri.rb:242:in `open_http'
        from /usr/lib/ruby/1.8/open-uri.rb:616:in `buffer_open'
        from /usr/lib/ruby/1.8/open-uri.rb:164:in `open_loop'
        from /usr/lib/ruby/1.8/open-uri.rb:162:in `catch'
        from /usr/lib/ruby/1.8/open-uri.rb:162:in `open_loop'
        from /usr/lib/ruby/1.8/open-uri.rb:132:in `open_uri'
        from /usr/lib/ruby/1.8/open-uri.rb:518:in `open'
        from /usr/lib/ruby/1.8/open-uri.rb:30:in `open'


/usr/lib/ruby/1.8/net/http.rb:560:in `initialize': No route to host -
connect(2) (Errno::EHOSTUNREACH)
        from /usr/lib/ruby/1.8/net/http.rb:560:in `open'
        from /usr/lib/ruby/1.8/net/http.rb:560:in `connect'
        from /usr/lib/ruby/1.8/timeout.rb:53:in `timeout'
        from /usr/lib/ruby/1.8/timeout.rb:93:in `timeout'
        from /usr/lib/ruby/1.8/net/http.rb:560:in `connect'
        from /usr/lib/ruby/1.8/net/http.rb:553:in `do_start'
        from /usr/lib/ruby/1.8/net/http.rb:542:in `start'
        from /usr/lib/ruby/1.8/open-uri.rb:242:in `open_http'
        from /usr/lib/ruby/1.8/open-uri.rb:616:in `buffer_open'
        from /usr/lib/ruby/1.8/open-uri.rb:164:in `open_loop'
        from /usr/lib/ruby/1.8/open-uri.rb:162:in `catch'
        from /usr/lib/ruby/1.8/open-uri.rb:162:in `open_loop'
        from /usr/lib/ruby/1.8/open-uri.rb:132:in `open_uri'
        from /usr/lib/ruby/1.8/open-uri.rb:518:in `open'
        from /usr/lib/ruby/1.8/open-uri.rb:30:in `open'


/usr/lib/ruby/1.8/net/http.rb:560:in `initialize': getaddrinfo:
Temporary failure in name resolution (SocketError)
        from /usr/lib/ruby/1.8/net/http.rb:560:in `open'
        from /usr/lib/ruby/1.8/net/http.rb:560:in `connect'
        from /usr/lib/ruby/1.8/timeout.rb:53:in `timeout'
        from /usr/lib/ruby/1.8/timeout.rb:93:in `timeout'
        from /usr/lib/ruby/1.8/net/http.rb:560:in `connect'
        from /usr/lib/ruby/1.8/net/http.rb:553:in `do_start'
        from /usr/lib/ruby/1.8/net/http.rb:542:in `start'
        from /usr/lib/ruby/1.8/open-uri.rb:242:in `open_http'
        from /usr/lib/ruby/1.8/open-uri.rb:616:in `buffer_open'
        from /usr/lib/ruby/1.8/open-uri.rb:164:in `open_loop'
        from /usr/lib/ruby/1.8/open-uri.rb:162:in `catch'
        from /usr/lib/ruby/1.8/open-uri.rb:162:in `open_loop'
        from /usr/lib/ruby/1.8/open-uri.rb:132:in `open_uri'
        from /usr/lib/ruby/1.8/open-uri.rb:518:in `open'
        from /usr/lib/ruby/1.8/open-uri.rb:30:in `open'

-- 
Yusuke ENDOH <mame / tsg.ne.jp>