Bug #5813
closednet/http's EOFError and Keep-Alive
Description
[ruby-dev:39421] describes exceptions thrown by open-uri, and raise a question why net/http raises EOFError.
net/http sometimes raises EOFError.
I recently find it is because of Keep-Alive.
On HTTP/1.1, connections are Keep-Alive and a Keep-Alive connection has a timeout.
If a client of such connection doesn't send anything after some communication,
server closes the connection because of Keep-Alive timeout,
and the client's connection shall raise EOFError (sometimes it may be ECONNRESET).
HTTP/1.1 says a client should retry a request if the request is idempotent.
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
So I attached a patch to such retry to net/http.
FYI, this timeout of Keep-Alive, KeepAliveTimeout, is:
Apache in FreeBSD ports or pkgsrc is 5 seconds,
the on in Debian Packages or RPM is 15 seconds.
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?
-
rescue => exceptionraise
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,
Updated by naruse (Yui NARUSE) almost 13 years ago
- Status changed from Assigned to Closed
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.
-
lib/net/http.rb (Net::HTTP#transport_request): retry a idempotent
request automatically. [ruby-dev:45030] [Bug #5790]
[ruby-core:41821] [Bug #5813] -
lib/net/http.rb (Net::HTTP#keep_alive_timeout=): added to specify
the second to reconnect the TCP connection on Keep-Alive.
The default value is 2 second because current servers uses 2 sec.
http://ftp-admin.blogspot.com/2009/09/keepalivetimeout2.html -
lib/net/http.rb (Net::HTTP#begin_transport): reconnect TCP
connection on keep-alive timeout.