Commit d862db0d authored by Serhiy Storchaka's avatar Serhiy Storchaka

Issue #21032. Fixed socket leak if HTTPConnection.getresponse() fails.

Original patch by Martin Panter.
parent 169f195b
...@@ -1070,18 +1070,22 @@ class HTTPConnection: ...@@ -1070,18 +1070,22 @@ class HTTPConnection:
kwds["buffering"] = True; kwds["buffering"] = True;
response = self.response_class(*args, **kwds) response = self.response_class(*args, **kwds)
response.begin() try:
assert response.will_close != _UNKNOWN response.begin()
self.__state = _CS_IDLE assert response.will_close != _UNKNOWN
self.__state = _CS_IDLE
if response.will_close: if response.will_close:
# this effectively passes the connection to the response # this effectively passes the connection to the response
self.close() self.close()
else: else:
# remember this, so we can tell when it is complete # remember this, so we can tell when it is complete
self.__response = response self.__response = response
return response return response
except:
response.close()
raise
class HTTP: class HTTP:
......
...@@ -25,6 +25,7 @@ class FakeSocket: ...@@ -25,6 +25,7 @@ class FakeSocket:
self.text = text self.text = text
self.fileclass = fileclass self.fileclass = fileclass
self.data = '' self.data = ''
self.file_closed = False
self.host = host self.host = host
self.port = port self.port = port
...@@ -34,7 +35,13 @@ class FakeSocket: ...@@ -34,7 +35,13 @@ class FakeSocket:
def makefile(self, mode, bufsize=None): def makefile(self, mode, bufsize=None):
if mode != 'r' and mode != 'rb': if mode != 'r' and mode != 'rb':
raise httplib.UnimplementedFileMode() 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): def close(self):
pass pass
...@@ -433,6 +440,22 @@ class BasicTest(TestCase): ...@@ -433,6 +440,22 @@ class BasicTest(TestCase):
self.assertEqual(resp.read(), '') self.assertEqual(resp.read(), '')
self.assertTrue(resp.isclosed()) 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): class OfflineTest(TestCase):
def test_responses(self): def test_responses(self):
self.assertEqual(httplib.responses[httplib.NOT_FOUND], "Not Found") self.assertEqual(httplib.responses[httplib.NOT_FOUND], "Not Found")
......
...@@ -1010,6 +1010,7 @@ Todd R. Palmer ...@@ -1010,6 +1010,7 @@ Todd R. Palmer
Juan David Ibáñez Palomar Juan David Ibáñez Palomar
Jan Palus Jan Palus
Yongzhi Pan Yongzhi Pan
Martin Panter
Mathias Panzenböck Mathias Panzenböck
M. Papillon M. Papillon
Peter Parente Peter Parente
......
...@@ -13,6 +13,9 @@ Core and Builtins ...@@ -13,6 +13,9 @@ Core and Builtins
Library 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 - Issue #22609: Constructors and update methods of mapping classes in the
collections module now accept the self keyword argument. collections module now accept the self keyword argument.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment