commit 11710c003493cde39b0db39b26be3a2e5c45b36d Author: Justin Bull Date: Fri Jan 24 08:49:14 2020 -0500 Don't set 'Connection: close' by default if #started? is false This causes divergent and unexpected behaviour from other ways of using of Net::HTTP, and sending requests with 'Connection: close' causes a drain on server resources. Consumers of this Ruby class may unintentionally invoke it in such a way where they intend to send many many requests to a server, but unfortunately all of them closing the connection. For example, this causes 50 TIME_WAIT tcp connections on the server because the 'Connection: close' header was set every time: ``` net = Net::HTTP.new('example.com') 50.times { net.get('/') } # causes many TIME_WAIT TCP sockets server-side ``` Meanwhile neither of these invocations have this behaviour, which (for HTTP servers >= 1.1) benefit from the default behaviour of a keep-alive/persistent TCP connection: ``` 50.times { Net::HTTP.get(URI('http://example.com/')) } # no TIME_WAIT sockets net = Net::HTTP.new('example.com') 50.times { net.start { net.get('/') } } # no TIME_WAIT sockets net = Net::HTTP.new('example.com') net.start 50.times { net.get('/') } # no TIME_WAIT sockets net.finish ``` Indeed using cURL, which one can assume represents one-off HTTP requests, does not set the header either way-- defering to the server which is keep-alive/ persistence on 1.1 and greater. diff --git a/lib/net/http.rb b/lib/net/http.rb index de9963d18c..6b96bcc569 100644 --- a/lib/net/http.rb +++ b/lib/net/http.rb @@ -1233,6 +1233,9 @@ def edit_path(path) # +dest+ argument is obsolete. # It still works but you must not use it. # + # If called before the connection has started, this method will open the + # connection and finish it once complete. + # # This method never raises an exception. # # response = http.get('/index.html') @@ -1258,6 +1261,9 @@ def get(path, initheader = nil, dest = nil, &block) # :yield: +body_segment+ # # This method returns a Net::HTTPResponse object. # + # If called before the connection has started, this method will open the + # connection and finish it once complete. + # # This method never raises an exception. # # response = nil @@ -1280,6 +1286,9 @@ def head(path, initheader = nil) # the socket. Note that in this case, the returned response # object will *not* contain a (meaningful) body. # + # If called before the connection has started, this method will open the + # connection and finish it once complete. + # # +dest+ argument is obsolete. # It still works but you must not use it. # @@ -1484,14 +1493,14 @@ def send_request(name, path, data = nil, header = nil) # the block can process it using HTTPResponse#read_body, # if desired. # + # If called before the connection has started, this method will open the + # connection and finish it once complete. + # # This method never raises Net::* exceptions. # def request(req, body = nil, &block) # :yield: +response+ unless started? - start { - req['connection'] ||= 'close' - return request(req, body, &block) - } + start { return request(req, body, &block) } end if proxy_user() req.proxy_basic_auth proxy_user(), proxy_pass() unless use_ssl? @@ -1585,10 +1594,6 @@ def begin_transport(req) end end - if not req.response_body_permitted? and @close_on_empty_response - req['connection'] ||= 'close' - end - req.update_uri address, port, use_ssl? req['host'] ||= addr_port() end