aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog14
-rw-r--r--lib/net/http.rb33
-rw-r--r--test/net/http/test_http.rb44
-rw-r--r--test/net/http/utils.rb1
4 files changed, 90 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index d78b1f81f9..d3e94c3e67 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+Fri Jan 20 14:31:43 2012 NARUSE, Yui <naruse@ruby-lang.org>
+
+ * 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.
+
Thu Jan 19 07:53:09 2012 Tadayoshi Funaba <tadf@dotrb.org>
* ext/date/date_strptime.c: moved detector of leftover.
diff --git a/lib/net/http.rb b/lib/net/http.rb
index 879cfe0045..53093492c2 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -578,7 +578,8 @@ module Net #:nodoc:
@address = address
@port = (port || HTTP.default_port)
@curr_http_version = HTTPVersion
- @no_keepalive_server = false
+ @keep_alive_timeout = 2
+ @last_communicated = nil
@close_on_empty_response = false
@socket = nil
@started = false
@@ -648,6 +649,12 @@ module Net #:nodoc:
@continue_timeout = sec
end
+ # Seconds to reuse the connection of the previous request.
+ # If the idle time is less than this Keep-Alive Timeout,
+ # Net::HTTP reuses the TCP/IP socket used by the previous communication.
+ # The default value is 2 seconds.
+ attr_accessor :keep_alive_timeout
+
# Returns true if the HTTP session has been started.
def started?
@started
@@ -1332,7 +1339,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 +1356,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?
@@ -1353,7 +1373,14 @@ module Net #:nodoc:
end
def begin_transport(req)
- connect if @socket.closed?
+ if @socket.closed?
+ connect
+ elsif @last_communicated && @last_communicated + @keep_alive_timeout < Time.now
+ D 'Conn close because of keep_alive_timeout'
+ @socket.close
+ connect
+ end
+
if not req.response_body_permitted? and @close_on_empty_response
req['connection'] ||= 'close'
end
@@ -1362,6 +1389,7 @@ module Net #:nodoc:
def end_transport(req, res)
@curr_http_version = res.http_version
+ @last_communicated = nil
if @socket.closed?
D 'Conn socket closed'
elsif not res.body and @close_on_empty_response
@@ -1369,6 +1397,7 @@ module Net #:nodoc:
@socket.close
elsif keep_alive?(req, res)
D 'Conn keep-alive'
+ @last_communicated = Time.now
else
D 'Conn close'
@socket.close
diff --git a/test/net/http/test_http.rb b/test/net/http/test_http.rb
index 15158546f1..b27aeb1cdc 100644
--- a/test/net/http/test_http.rb
+++ b/test/net/http/test_http.rb
@@ -564,3 +564,47 @@ 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' => 1,
+ }
+
+ include TestNetHTTPUtils
+
+ def test_keep_alive_get_auto_reconnect
+ start {|http|
+ http.set_debug_output($stderr)
+ res = http.get('/')
+ http.keep_alive_timeout = 1
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ sleep 1.5
+ assert_nothing_raised {
+ res = http.get('/')
+ }
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ }
+ end
+
+ def test_keep_alive_get_auto_retry
+ start {|http|
+ http.set_debug_output($stderr)
+ res = http.get('/')
+ http.keep_alive_timeout = 5
+ assert_kind_of Net::HTTPResponse, res
+ assert_kind_of String, res.body
+ sleep 1.5
+ 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 50f616f29f..07e0b9fa2d 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,