[#44925] [Backport93 - Backport #5702][Open] backport r33935 — Yusuke Endoh <mame@...>

19 messages 2011/12/03
[#44929] [Backport93 - Backport #5702] backport r33935 — Tomoyuki Chikanaga <nagachika00@...> 2011/12/05

[#44931] Re: [Backport93 - Backport #5702] backport r33935 — Yusuke Endoh <mame@...> 2011/12/05

遠藤です。

[#44940] Re: [ruby-cvs:41134] naruse:r33956 (trunk): Comment out tests which fails with GDBM-DBM compat mode. — Tanaka Akira <akr@...>

2011/12/6 <naruse@ruby-lang.org>:

9 messages 2011/12/05
[#44941] Re: [ruby-cvs:41134] naruse:r33956 (trunk): Comment out tests which fails with GDBM-DBM compat mode. — KOSAKI Motohiro <kosaki.motohiro@...> 2011/12/05

2011年12月5日16:56 Tanaka Akira <akr@fsij.org>:

[#44942] Re: [ruby-cvs:41134] naruse:r33956 (trunk): Comment out tests which fails with GDBM-DBM compat mode. — KOSAKI Motohiro <kosaki.motohiro@...> 2011/12/05

> おかしいな。gdbmは勝手にcreateフラグを立ててしまうので当該2つの

[#44985] [ruby-trunk - Bug #5757][Open] main threadがreadやselectで待っていると、^C でなかなか死なない — Yui NARUSE <naruse@...>

12 messages 2011/12/13

[#45021] [ruby-trunk - Bug #5786][Open] LoadError: cannot load such file -- openssl — Kazuhiro NISHIYAMA <redmine@...>

11 messages 2011/12/21

[#45057] [ruby-trunk - Feature #5820][Assigned] Merge Onigmo to Ruby 2.0 — Yui NARUSE <naruse@...>

21 messages 2011/12/28

[ruby-dev:45030] [ruby-trunk - Bug #5790][Open] net/http の EOFError と Keep-Alive

From: Yui NARUSE <naruse@...>
Date: 2011-12-22 09:49:17 UTC
List: ruby-dev #45030
Issue #5790 has been reported by Yui NARUSE.

----------------------------------------
Bug #5790: net/http の EOFError と Keep-Alive
https://bugs.ruby-lang.org/issues/5790

Author: Yui NARUSE
Status: Open
Priority: Normal
Assignee: 
Category: 
Target version: 2.0.0
ruby -v: ruby 2.0.0dev (2011-12-21 trunk 34086) [x86_64-freebsd9.0]


[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,


-- 
http://redmine.ruby-lang.org

In This Thread

Prev Next