]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.11] GH-103472: close response in HTTPConnection._tunnel (GH-103473) (#104077)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 2 May 2023 06:48:07 +0000 (23:48 -0700)
committerGitHub <noreply@github.com>
Tue, 2 May 2023 06:48:07 +0000 (23:48 -0700)
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 <tagrain@gmail.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
Lib/http/client.py
Lib/test/test_httplib.py
Misc/NEWS.d/next/Library/2023-04-12-13-04-16.gh-issue-103472.C6bOHv.rst [new file with mode: 0644]

index eabb0ca26940c73295ad8c106e9b081e0e4c8bd7..5d2196dd79af9dc481ff17f3f9c1a6e0ff31b9b0 100644 (file)
@@ -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__."""
index 15dab0356f5e35e759ea2c5c8800dea564baf87f..acae0127a13f4bf881c33063cd672959dd9de7b7 100644 (file)
@@ -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 (file)
index 0000000..01d84f0
--- /dev/null
@@ -0,0 +1,2 @@
+Avoid a potential :exc:`ResourceWarning` in :class:`http.client.HTTPConnection`
+by closing the proxy / tunnel's CONNECT response explicitly.