diff --git a/lib/net/http.rb b/lib/net/http.rb
index 4db69b6..d9e2fcf 100644
--- a/lib/net/http.rb
+++ b/lib/net/http.rb
@@ -1403,6 +1403,7 @@ module Net   #:nodoc:
       if proxy_user()
         req.proxy_basic_auth proxy_user(), proxy_pass() unless use_ssl?
       end
+      req.retry_networking_errors = false if block_given?
       req.set_body_internal body
       res = transport_request(req, &block)
       if sspi_auth?(res)
@@ -1452,8 +1453,9 @@ module Net   #:nodoc:
              # avoid a dependency on OpenSSL
              defined?(OpenSSL::SSL) ? OpenSSL::SSL::SSLError : IOError,
              Timeout::Error => exception
-        if count == 0 && IDEMPOTENT_METHODS_.include?(req.method)
+        if count == 0 && req.retry_networking_errors
           count += 1
+          req.body_stream.rewind if req.body_stream
           @socket.close if @socket and not @socket.closed?
           D "Conn close because of error #{exception}, and retry"
           retry
diff --git a/lib/net/http/generic_request.rb b/lib/net/http/generic_request.rb
index 9b7d287..b427c05 100644
--- a/lib/net/http/generic_request.rb
+++ b/lib/net/http/generic_request.rb
@@ -9,6 +9,7 @@ class Net::HTTPGenericRequest
 
   def initialize(m, reqbody, resbody, uri_or_path, initheader = nil)
     @method = m
+    @retry_networking_errors = Net::HTTP::IDEMPOTENT_METHODS_.include?(m)
     @request_has_body = reqbody
     @response_has_body = resbody
 
@@ -58,6 +59,11 @@ class Net::HTTPGenericRequest
   # themselves.
   attr_reader :decode_content
 
+  # When true, this request may be retried if a networking error is
+  # encountered. Set to false to disable retries. Defaults to
+  # true for idempotent HTTP methods.
+  attr_accessor :retry_networking_errors
+
   def inspect
     "\#<#{self.class} #{@method}>"
   end
@@ -97,6 +103,7 @@ class Net::HTTPGenericRequest
   attr_reader :body_stream
 
   def body_stream=(input)
+    @retry_networking_errors = input.respond_to?(:rewind)
     @body = nil
     @body_stream = input
     @body_data = nil
diff --git a/test/net/http/test_http_retries.rb b/test/net/http/test_http_retries.rb
new file mode 100644
index 0000000..f15eaa3
--- /dev/null
+++ b/test/net/http/test_http_retries.rb
@@ -0,0 +1,175 @@
+# coding: US-ASCII
+require 'test/unit'
+require 'net/http'
+require 'stringio'
+require 'socket'
+
+class TestNetHTTPIdempotentRetries < Test::Unit::TestCase
+
+  class SimpleHttpServer
+
+    def initialize
+      @address = '127.0.0.1'
+      @port = 3000
+      @handlers = []
+    end
+
+    attr_reader :address, :port
+
+    # The given proc should accept the follow block arguments:
+    #
+    # * `http_method` - String
+    # * `request_uri` - String
+    # * `request_headers` - Hash<String,String>
+    # * `socket` - Socket
+    #
+    # The block is expected to read the request body and to write
+    # the full response.
+    def handle(&handler)
+      @handlers << Proc.new
+    end
+
+    def start
+      @server = TCPServer.new(@address, @port)
+      @thread = Thread.new do
+        loop do
+          begin
+            socket = @server.accept
+            method, uri, _ = socket.gets.split(/\s+/)
+            @handlers.shift.call(method, uri, headers(socket), socket)
+            socket.close unless socket.closed?
+          rescue
+            @server.close
+            @server = TCPServer.new(@address, @port)
+            retry
+          end
+        end
+      end
+    end
+
+    def stop
+      @thread.kill
+      @server.close
+    end
+
+    private
+
+    def headers(socket)
+      headers = {}
+      line = socket.gets
+      until line == "\r\n"
+        key, value = line.split(/:\s*/, 2)
+        headers[key.downcase] = value
+        line = socket.gets
+      end
+      headers
+    end
+
+  end
+
+  def setup
+    @server = SimpleHttpServer.new
+  end
+
+  def teardown
+    @server.stop
+  end
+
+  def test_idempotent_retry_default
+    @server.handle do |_, _, headers, socket|
+      socket.close # close without responding
+    end
+    @server.handle do |_, _, headers, socket|
+      socket.print("HTTP/1.1 200 OK\r\n")
+      socket.print("\r\n")
+    end
+    @server.start
+
+    req = Net::HTTP::Get.new('/')
+    http = Net::HTTP.new(@server.address, @server.port)
+    res = http.request(req)
+
+    assert_equal('200', res.code)
+  end
+
+  def test_retry_can_be_disabled
+    @server.handle do |_, _, headers, socket|
+      socket.close # close without responding
+    end
+    @server.handle do |_, _, headers, socket|
+      socket.print("HTTP/1.1 200 OK\r\n")
+      socket.print("\r\n")
+    end
+    @server.start
+
+    req = Net::HTTP::Get.new('/')
+    req.retry_networking_errors = false
+
+    http = Net::HTTP.new(@server.address, @server.port)
+    assert_raises {
+      http.request(req)
+    }
+  end
+
+  def test_idempotent_retry_rewinds_put_body_stream
+    @server.handle do |_, _, headers, socket|
+      # intentionally read some of the data, not all
+      socket.read(headers['content-length'].to_i / 2)
+      socket.close
+    end
+    @server.handle do |_, _, headers, socket|
+      body = socket.read(headers['content-length'].to_i)
+      socket.print("HTTP/1.1 200 OK\r\n")
+      socket.print("Content-Length: #{body.size}\n")
+      socket.print("\r\n")
+      socket.print(body)
+    end
+    @server.start
+
+    body = StringIO.new('io-body')
+    req = Net::HTTP::Put.new('/', 'Content-Length' => body.size.to_s )
+    req.body_stream = body
+
+    http = Net::HTTP.new(@server.address, @server.port)
+    http.read_timeout = 0.5
+
+    assert_nothing_raised {
+      http.request(req)
+    }
+  end
+
+  def test_idempotent_retry_disabled_with_request_block
+    one_meg = 1024 * 1024
+    @server.handle do |_, _, headers, socket|
+      socket.print("HTTP/1.1 200 OK\r\n")
+      socket.print("Content-Length: #{one_meg}\n")
+      socket.print("\r\n")
+      socket.print('.' * (one_meg / 2))
+      raise 'error' # forcefully close the connection
+    end
+    @server.handle do |_, _, headers, socket|
+      socket.print("HTTP/1.1 200 OK\r\n")
+      socket.print("Content-Length: #{one_meg}\n")
+      socket.print("\r\n")
+      socket.print('.' * one_meg)
+    end
+    @server.start
+
+    http = Net::HTTP.new(@server.address, @server.port)
+    http.read_timeout = 0.5
+
+    yield_count = 0
+    byte_count = 0
+
+    assert_raises {
+      http.request(Net::HTTP::Get.new('/')) do |resp|
+        yield_count += 1
+        resp.read_body do |bytes|
+          byte_count += bytes.bytesize
+        end
+      end
+    }
+    assert_equal(one_meg / 2, byte_count)
+    assert_equal(1, yield_count)
+  end
+end
