Issue #21032. Fixed socket leak if HTTPConnection.getresponse() fails.
Original patch by Martin Panter.
diff --git a/Lib/httplib.py b/Lib/httplib.py
index 7c27906..1224989 100644
--- a/Lib/httplib.py
+++ b/Lib/httplib.py
@@ -1070,18 +1070,22 @@
kwds["buffering"] = True;
response = self.response_class(*args, **kwds)
- response.begin()
- assert response.will_close != _UNKNOWN
- self.__state = _CS_IDLE
+ try:
+ response.begin()
+ assert response.will_close != _UNKNOWN
+ self.__state = _CS_IDLE
- if response.will_close:
- # this effectively passes the connection to the response
- self.close()
- else:
- # remember this, so we can tell when it is complete
- self.__response = response
+ if response.will_close:
+ # this effectively passes the connection to the response
+ self.close()
+ else:
+ # remember this, so we can tell when it is complete
+ self.__response = response
- return response
+ return response
+ except:
+ response.close()
+ raise
class HTTP:
diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py
index 3e7a57e..a73a28b 100644
--- a/Lib/test/test_httplib.py
+++ b/Lib/test/test_httplib.py
@@ -25,6 +25,7 @@
self.text = text
self.fileclass = fileclass
self.data = ''
+ self.file_closed = False
self.host = host
self.port = port
@@ -34,7 +35,13 @@
def makefile(self, mode, bufsize=None):
if mode != 'r' and mode != 'rb':
raise httplib.UnimplementedFileMode()
- return self.fileclass(self.text)
+ # keep the file around so we can check how much was read from it
+ self.file = self.fileclass(self.text)
+ self.file.close = self.file_close #nerf close ()
+ return self.file
+
+ def file_close(self):
+ self.file_closed = True
def close(self):
pass
@@ -433,6 +440,22 @@
self.assertEqual(resp.read(), '')
self.assertTrue(resp.isclosed())
+ def test_error_leak(self):
+ # Test that the socket is not leaked if getresponse() fails
+ conn = httplib.HTTPConnection('example.com')
+ response = []
+ class Response(httplib.HTTPResponse):
+ def __init__(self, *pos, **kw):
+ response.append(self) # Avoid garbage collector closing the socket
+ httplib.HTTPResponse.__init__(self, *pos, **kw)
+ conn.response_class = Response
+ conn.sock = FakeSocket('') # Emulate server dropping connection
+ conn.request('GET', '/')
+ self.assertRaises(httplib.BadStatusLine, conn.getresponse)
+ self.assertTrue(response)
+ #self.assertTrue(response[0].closed)
+ self.assertTrue(conn.sock.file_closed)
+
class OfflineTest(TestCase):
def test_responses(self):
self.assertEqual(httplib.responses[httplib.NOT_FOUND], "Not Found")
diff --git a/Misc/ACKS b/Misc/ACKS
index d44240f..5372ee7 100644
--- a/Misc/ACKS
+++ b/Misc/ACKS
@@ -1010,6 +1010,7 @@
Juan David Ibáñez Palomar
Jan Palus
Yongzhi Pan
+Martin Panter
Mathias Panzenböck
M. Papillon
Peter Parente
diff --git a/Misc/NEWS b/Misc/NEWS
index 4b42d62..b9835a5 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -13,6 +13,9 @@
Library
-------
+- Issue #21032. Fixed socket leak if HTTPConnection.getresponse() fails.
+ Original patch by Martin Panter.
+
- Issue #22609: Constructors and update methods of mapping classes in the
collections module now accept the self keyword argument.