概要
原著者の許諾を得て翻訳・公開いたします。
- 英語記事: Ruby 2.6 adds write_timeout to Net::HTTP | BigBinary Blog
 - 原文公開日: 2018/08/14
 - 著者: Taha Husain
 - サイト: BigBinary
 
Ruby 2.6先行チェック: Net::HTTPにwrite_timeoutが追加される(翻訳)
Ruby 2.6シリーズの記事です。Ruby 2.6.0-preview2は最近リリースされました。
Ruby 2.6より前は、Net::HTTPで巨大なリクエストを作成するとリクエストを中断するまでハングし続けることがありました。この問題を修正するため、Ruby 2.6のNet::HTTPクラスにwrite_timeout属性とwrite_timeout=メソッドが追加されました。write_timeoutのデフォルト値は60秒で、integerまたはfloatで指定できます。
同様に、Net::BufferedIOクラスにもwrite_timeout属性とwrite_timeout=メソッドが追加されました。
write_timeoutで指定した秒数以内にレスポンスのチャンクが書き込まれなかった場合は、Net::WriteTimeout例外が発生します。なお、WindowsシステムではNet::WriteTimeout例外が発生しません。
コード例
# server.rb
require 'socket'
server = TCPServer.new('localhost', 2345)
loop do
  socket = server.accept
end
Ruby 2.5.1の場合
# client.rb
require 'net/http'
connection = Net::HTTP.new('localhost', 2345)
connection.open_timeout = 1
connection.read_timeout = 3
connection.start
post = Net::HTTP::Post.new('/')
body = (('a' * 1023) + "\n") * 5_000
post.body = body
puts "Sending #{body.bytesize} bytes"
connection.request(post)
上の出力
$ RBENV_VERSION=2.5.1 ruby client.rb
Sending 5120000 bytes
Ruby 2.5.1では、プログラムが中断されるまでリクエストの処理が止まりません。
Ruby 2.6.0-devの場合
client.rbのNet::HTTPインスタンスにwrite_timeoutを追加します。
# client.rb
require 'net/http'
connection = Net::HTTP.new('localhost', 2345)
connection.open_timeout = 1
connection.read_timeout = 3
# write_timeoutのタイムアウトは10秒
connection.write_timeout = 10
connection.start
post = Net::HTTP::Post.new('/')
body = (('a' * 1023) + "\n") * 5_000
post.body = body
puts "Sending #{body.bytesize} bytes"
connection.request(post)
上の出力
$ RBENV_VERSION=2.6.0-dev ruby client.rb
Sending 5120000 bytes
Traceback (most recent call last):
    13: `from client.rb:17:in `<main>``
    12: `from /net/http.rb:1479:in `request``
    11: `from /net/http.rb:1506:in `transport_request``
    10: `from /net/http.rb:1506:in `catch``
     9: `from /net/http.rb:1507:in `block in transport_request``
     8: `from /net/http/generic_request.rb:123:in `exec``
     7: `from /net/http/generic_request.rb:189:in `send_request_with_body``
     6: `from /net/protocol.rb:221:in `write``
     5: `from /net/protocol.rb:239:in `writing``
     4: `from /net/protocol.rb:222:in `block in write``
     3: `from /net/protocol.rb:249:in `write0``
     2: `from /net/protocol.rb:249:in `each_with_index``
     1: `from /net/protocol.rb:249:in `each``
`/net/protocol.rb:270:in `block in write0`: Net::WriteTimeout (Net::WriteTimeout)`
Ruby 2.6.0では10秒後に上のプログラムがNet::WriteTimeout例外で終了します(値はwrite_timeout属性に設定)。