From 0d4026432591d43185568dd31cef6a034c4b9261 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Mon, 1 May 2023 23:48:07 -0700 Subject: [3.11] GH-103472: close response in HTTPConnection._tunnel (GH-103473) (#104077) GH-103472: close response in HTTPConnection._tunnel (GH-103473) Avoid a potential `ResourceWarning` in `http.client.HTTPConnection` by closing the proxy / tunnel's CONNECT response explicitly. --------- (cherry picked from commit 9de0cf20fa0485e327e57cc0864c7476da85cfad) Co-authored-by: Thomas Grainger Co-authored-by: Gregory P. Smith --- Lib/http/client.py | 33 ++++++++++++---------- Lib/test/test_httplib.py | 23 +++++++++++++++ .../2023-04-12-13-04-16.gh-issue-103472.C6bOHv.rst | 2 ++ 3 files changed, 43 insertions(+), 15 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2023-04-12-13-04-16.gh-issue-103472.C6bOHv.rst diff --git a/Lib/http/client.py b/Lib/http/client.py index eabb0ca..5d2196d 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -918,23 +918,26 @@ class HTTPConnection: del headers response = self.response_class(self.sock, method=self._method) - (version, code, message) = response._read_status() + try: + (version, code, message) = response._read_status() - if code != http.HTTPStatus.OK: - self.close() - raise OSError(f"Tunnel connection failed: {code} {message.strip()}") - while True: - line = response.fp.readline(_MAXLINE + 1) - if len(line) > _MAXLINE: - raise LineTooLong("header line") - if not line: - # for sites which EOF without sending a trailer - break - if line in (b'\r\n', b'\n', b''): - break + if code != http.HTTPStatus.OK: + self.close() + raise OSError(f"Tunnel connection failed: {code} {message.strip()}") + while True: + line = response.fp.readline(_MAXLINE + 1) + if len(line) > _MAXLINE: + raise LineTooLong("header line") + if not line: + # for sites which EOF without sending a trailer + break + if line in (b'\r\n', b'\n', b''): + break - if self.debuglevel > 0: - print('header:', line.decode()) + if self.debuglevel > 0: + print('header:', line.decode()) + finally: + response.close() def connect(self): """Connect to the host and port specified in __init__.""" diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py index 15dab03..acae012 100644 --- a/Lib/test/test_httplib.py +++ b/Lib/test/test_httplib.py @@ -2249,6 +2249,29 @@ class TunnelTests(TestCase): lines = output.getvalue().splitlines() self.assertIn('header: {}'.format(expected_header), lines) + def test_tunnel_leak(self): + sock = None + + def _create_connection(address, timeout=None, source_address=None): + nonlocal sock + sock = FakeSocket( + 'HTTP/1.1 404 NOT FOUND\r\n\r\n', + host=address[0], + port=address[1], + ) + return sock + + self.conn._create_connection = _create_connection + self.conn.set_tunnel('destination.com') + exc = None + try: + self.conn.request('HEAD', '/', '') + except OSError as e: + # keeping a reference to exc keeps response alive in the traceback + exc = e + self.assertIsNotNone(exc) + self.assertTrue(sock.file_closed) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/Misc/NEWS.d/next/Library/2023-04-12-13-04-16.gh-issue-103472.C6bOHv.rst b/Misc/NEWS.d/next/Library/2023-04-12-13-04-16.gh-issue-103472.C6bOHv.rst new file mode 100644 index 0000000..01d84f0 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-04-12-13-04-16.gh-issue-103472.C6bOHv.rst @@ -0,0 +1,2 @@ +Avoid a potential :exc:`ResourceWarning` in :class:`http.client.HTTPConnection` +by closing the proxy / tunnel's CONNECT response explicitly. -- cgit v0.12