Tech Racho エンジニアの「?」を「!」に。
  • 開発

Ruby 2.6先行チェック: Net::HTTPにwrite_timeoutが追加される(翻訳)

概要

原著者の許諾を得て翻訳・公開いたします。

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属性に設定)。

関連記事

Ruby 2.6先行チェック: begin-end内でrescueのないelseがあると例外を発生(翻訳)

Ruby 2.6先行チェック: `String#split`がブロックを取れる(翻訳)


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。