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`がブロックを取れる(翻訳)

デザインも頼めるシステム開発会社をお探しならBPS株式会社までどうぞ 開発エンジニア積極採用中です! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833、GitHub: @hachi8833 コボラー、ITコンサル、ローカライズ業界、Rails開発を経てTechRachoの編集・記事作成を担当。 これまでにRuby on Rails チュートリアル第2版の半分ほど、Railsガイドの初期翻訳ではほぼすべてを翻訳。その後も折に触れてそれぞれ一部を翻訳。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 実は最近Go言語が好き。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

BPSアドベントカレンダー

週刊Railsウォッチ

インフラ

ActiveSupport探訪シリーズ