diff -r b0fbaed45956 Lib/test/test_urllib.py --- a/Lib/test/test_urllib.py Tue Dec 17 21:13:16 2013 -0500 +++ b/Lib/test/test_urllib.py Wed Dec 18 14:18:04 2013 +0200 @@ -48,45 +48,49 @@ class FakeHTTPMixin(object): def fakehttp(self, fakedata): - class FakeSocket(io.BytesIO): - io_refs = 1 - - def sendall(self, data): - FakeHTTPConnection.buf = data - - def makefile(self, *args, **kwds): - self.io_refs += 1 - return self - - def read(self, amt=None): - if self.closed: - return b"" - return io.BytesIO.read(self, amt) - - def readline(self, length=None): - if self.closed: - return b"" - return io.BytesIO.readline(self, length) - - def close(self): - self.io_refs -= 1 - if self.io_refs == 0: - io.BytesIO.close(self) - - class FakeHTTPConnection(http.client.HTTPConnection): - - # buffer to store data for verification in urlopen tests. - buf = None - - def connect(self): - self.sock = FakeSocket(fakedata) - self._connection_class = http.client.HTTPConnection - http.client.HTTPConnection = FakeHTTPConnection + http.client.HTTPConnection = fakehttp(fakedata) def unfakehttp(self): http.client.HTTPConnection = self._connection_class +def fakehttp(fakedata): + class FakeSocket(io.BytesIO): + io_refs = 1 + + def sendall(self, data): + FakeHTTPConnection.buf = data + + def makefile(self, *args, **kwds): + self.io_refs += 1 + return self + + def read(self, amt=None): + if self.closed: + return b"" + return io.BytesIO.read(self, amt) + + def readline(self, length=None): + if self.closed: + return b"" + return io.BytesIO.readline(self, length) + + def close(self): + self.io_refs -= 1 + if self.io_refs == 0: + io.BytesIO.close(self) + + class FakeHTTPConnection(http.client.HTTPConnection): + + # buffer to store data for verification in urlopen tests. + buf = None + fakesock = FakeSocket(fakedata) + + def connect(self): + self.sock = self.fakesock + + return FakeHTTPConnection + class urlopen_FileTests(unittest.TestCase): """Test urlopen() opening a temporary file. diff -r b0fbaed45956 Lib/test/test_urllib2.py --- a/Lib/test/test_urllib2.py Tue Dec 17 21:13:16 2013 -0500 +++ b/Lib/test/test_urllib2.py Wed Dec 18 14:18:04 2013 +0200 @@ -1,5 +1,6 @@ import unittest from test import support +from test import test_urllib import os import io @@ -284,6 +285,7 @@ self.data = None self.raise_on_endheaders = False self._tunnel_headers = {} + self.sock = None def __call__(self, host, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): self.host = host @@ -1371,6 +1373,23 @@ self.assertEqual(len(http_handler.requests), 1) self.assertFalse(http_handler.requests[0].has_header(auth_header)) + def test_http_closed(self): + """Test the connection is cleaned up when the response is closed""" + for (transfer, data) in ( + ("Connection: close", b"data"), + ("Transfer-Encoding: chunked", b"4\r\ndata\r\n0\r\n\r\n"), + ("Content-Length: 4", b"data"), + ): + header = "HTTP/1.1 200 OK\r\n{}\r\n\r\n".format(transfer) + conn = test_urllib.fakehttp(header.encode() + data) + handler = urllib.request.AbstractHTTPHandler() + req = Request("http://dummy/") + req.timeout = None + with handler.do_open(conn, req) as resp: + resp.read() + self.assertTrue(conn.fakesock.closed, + "Connection not closed with {!r}".format(transfer)) + class MiscTests(unittest.TestCase): diff -r b0fbaed45956 Lib/urllib/request.py --- a/Lib/urllib/request.py Tue Dec 17 21:13:16 2013 -0500 +++ b/Lib/urllib/request.py Wed Dec 18 14:18:04 2013 +0200 @@ -1245,12 +1245,19 @@ h.set_tunnel(req._tunnel_host, headers=tunnel_headers) try: - h.request(req.get_method(), req.selector, req.data, headers) - except socket.error as err: # timeout error + try: + h.request(req.get_method(), req.selector, req.data, headers) + except socket.error as err: # timeout error + raise URLError(err) + r = h.getresponse() + except: h.close() - raise URLError(err) - else: - r = h.getresponse() + raise + # Explicitly close the original socket object now + # to guarantee that the connection closes + # when the response closes its socket.makefile() object + if h.sock: + h.sock.close() r.url = req.get_full_url() # This line replaces the .msg attribute of the HTTPResponse