@kyanny's blog

My thoughts, my life. Views/opinions are my own.

遅い HTTP サーバで HTTP クライアントのタイムアウトを調べる

HTTP クライアントのなかにはいくつかタイムアウトのオプションを設定できるものがある(curl(1) の --connect-timeout--max-time など)

Ruby の Net::HTTP も open_timeoutread_timeout というオプションを指定できるが、 HTTP サーバのどの処理に時間がかかったときこれらのタイムアウトが効くのか気になったので、ソケット処理の各段階に wait を入れられるような簡易 HTTP サーバを書いて試してみた。

https://github.com/kyanny/playground/tree/gh-pages/slow-http-server

  • curl(1) の場合
    • bind の前までは HTTP サーバに接続できないのでタイムアウトオプションは無意味
    • --connect-timeout は bind 後 listen 前で待たされるときに効き、 listen 後に待たされても関知しない
    • --max-time は bind 後のどの段階で待たされるときにも効く(サーバがレスポンスを返し終わっていてもソケットをし終わるまではタイムアウトが効く)
  • Net::HTTP の場合
    • bind の前までは HTTP サーバに接続できないのでタイムアウトオプションは無意味
    • open_timeout は bind 後 listen 前で待たされるときに効き、 listen 後に待たされても関知しない
      • curl(1) の --connect-timeout と同じ挙動
      • タイムアウトすると Net::OpenTimeout 例外を送出する
    • read_timeout は listen 後のどの段階で待たされるときにも効く
      • これも curl(1) の --max-time と同じ挙動にみえる
      • 例外は全て同じではなく、 listen 後 accept 前で待たされたときは Errno::ECONNRESET を、 accept 後 write の途中で待たされたときは Net::ReadTimeout を、 write 完了後 close 前で待たされたときは Errno::ECONNRESET を送出する

余談。 Ruby の Net::HTTP で簡易 HTTP サーバにリクエストを送ると、 Content-Length ヘッダを正しく返さないときに Errno::ECONNREFUSED 例外を送出する、という不思議な挙動に出くわした。 HTTP の仕様的にそういうものなのかと思ったら、 "HTTP/1.0 200 OK\r\n\r\nHello world" | nc -l 8080 で立てた簡易 HTTP サーバに対してリクエストを送るとふつうにレスポンスを受け取れる。ソケットのオプションか何かに違いがあるのでは、という気がするが、調べていないのでわからない。

https://github.com/kyanny/playground/blob/gh-pages/slow-http-server/s.sh

https://github.com/kyanny/playground/blob/gh-pages/slow-http-server/nc.sh