Project

General

Profile

Actions

Bug #5790

closed

net/http の EOFError と Keep-Alive

Added by naruse (Yui NARUSE) over 10 years ago. Updated over 10 years ago.

Status:
Closed
Priority:
Normal
Assignee:
-
Target version:
ruby -v:
ruby 2.0.0dev (2011-12-21 trunk 34086) [x86_64-freebsd9.0]
Backport:
[ruby-dev:45030]

Description

[ruby-dev:39421] がずっと心に残っていたので、思い立って調べてみたので、
(正確には自分が高頻度で踏むようになったので調べてみた)
その調査結果と対策案を提案します。

まず、投げられる原因ですが、根本的な原因は Keep-Alive のタイムアウトです。
HTTP/1.1 ではデフォルトで持続的接続を行うので、複数回のリクエストに渡って
一つの socket が使い回されます。

しかし、リクエスト同士で時間が開いていると、サーバー側でタイムアウトする
可能性があります。この時にクライアント側の read(2) が 0 を返す、
つまり EOFError となることがあります。

HTTP/1.1 は、冪等なメソッドの場合には確認なしにリトライすべきと言っているので、
そのようにするパッチを添付します。
冪等でないメソッドの場合にどうするべきかは悩ましいところです。
http://tools.ietf.org/html/rfc2616#section-8.1.4
http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-17#section-6.1.5
http://www.studyinghttp.net/connections

なお、この Keep-Alive における Timeout は、
Apache の場合、FreeBSD ports や pkgsrc では 5 秒、
Debian Packages や RPM では 15 秒でした。

diff --git a/lib/net/http.rb b/lib/net/http.rb
index 879cfe0..13bd1a7 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -1332,7 +1332,10 @@ module Net #:nodoc:
res
end

  • IDEMPOTENT_METHODS_ = %w/GET HEAD PUT DELETE OPTIONS TRACE/ # :nodoc:
  • def transport_request(req)
  •  count = 0
     begin_transport req
     res = catch(:response) {
       req.exec @socket, @curr_http_version, edit_path(req.path)
    

@@ -1346,6 +1349,16 @@ module Net #:nodoc:
}
end_transport req, res
res

  • rescue EOFError, Errno::ECONNRESET => exception
  •  if count == 0 && IDEMPOTENT_METHODS_.include?(req.method)
    
  •    count += 1
    
  •    @socket.close if @socket and not @socket.closed?
    
  •    D "Conn close because of error #{exception}, and retry"
    
  •    retry
    
  •  end
    
  •  D "Conn close because of error #{exception}"
    
  •  @socket.close if @socket and not @socket.closed?
    
  •  raise
    
    rescue => exception
    D "Conn close because of error #{exception}"
    @socket.close if @socket and not @socket.closed?
    diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb
    index 1515854..2e7ab4e 100644
    --- a/test/net/http/test_http.rb
    +++ b/test/net/http/test_http.rb
    @@ -564,3 +564,29 @@ class TestNetHTTPContinue < Test::Unit::TestCase
    assert_not_match(/HTTP/1.1 100 continue/, @debug.string)
    end
    end

+class TestNetHTTPKeepAlive < Test::Unit::TestCase

  • CONFIG = {
  • 'host' => '127.0.0.1',
  • 'port' => 10081,
  • 'proxy_host' => nil,
  • 'proxy_port' => nil,
  • 'RequestTimeout' => 0.1,
  • }
  • include TestNetHTTPUtils
  • def test_keep_alive_get
  • start {|http|
  •  res = http.get('/')
    
  •  assert_kind_of Net::HTTPResponse, res
    
  •  assert_kind_of String, res.body
    
  •  sleep 1
    
  •  assert_nothing_raised {
    
  •    res = http.get('/')
    
  •  }
    
  •  assert_kind_of Net::HTTPResponse, res
    
  •  assert_kind_of String, res.body
    
  • }
  • end
    +end
    diff --git a/test/net/http/utils.rb b/test/net/http/utils.rb
    index 50f616f..07e0b9f 100644
    --- a/test/net/http/utils.rb
    +++ b/test/net/http/utils.rb
    @@ -51,6 +51,7 @@ module TestNetHTTPUtils
    :ServerType => Thread,
    }
    server_config[:OutputBufferSize] = 4 if config('chunked')
  • server_config[:RequestTimeout] = config('RequestTimeout') if config('RequestTimeout')
    if defined?(OpenSSL) and config('ssl_enable')
    server_config.update({
    :SSLEnable => true,

Related issues 2 (0 open2 closed)

Related to Ruby master - Bug #6001: Retry idempotent HTTP requests for more errorsClosed02/11/2012Actions
Has duplicate Ruby master - Bug #5813: net/http's EOFError and Keep-AliveClosednaruse (Yui NARUSE)12/27/2011Actions

Updated by Anonymous over 10 years ago

(2011/12/22 18:49), Yui NARUSE wrote:

HTTP/1.1 は、冪等なメソッドの場合には確認なしにリトライすべきと言っているので、
そのようにするパッチを添付します。
冪等でないメソッドの場合にどうするべきかは悩ましいところです。
http://tools.ietf.org/html/rfc2616#section-8.1.4
http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-17#section-6.1.5
http://www.studyinghttp.net/connections

細かいところは見ていないのですが、基本的にその方向でよいと思います。が、
この件については、ruby-coreにも投げて、Eric Hodelのコメントをもらったほ
うがよいと思います。その理由は、つい最近Mechanize 2.1(というより、本質
的にはその下層にあるnet-http-persistence)に同じ問題への対策が入ったため
です。net/http側で面倒を見るべき、と彼に言ったのですが、彼は違う意見だっ
たようなので、念のため。

Actions #2

Updated by naruse (Yui NARUSE) over 10 years ago

  • File deleted (signature.asc)
Actions #3

Updated by naruse (Yui NARUSE) over 10 years ago

  • Status changed from Open to Closed
  • % Done changed from 0 to 100

This issue was solved with changeset r34341.
Yui, thank you for reporting this issue.
Your contribution to Ruby is greatly appreciated.
May Ruby be with you.


Actions

Also available in: Atom PDF