]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-69152: add method get_proxy_response_headers to HTTPConnection class (#104248)
authorAlexey Namyotkin <62434915+nametkin@users.noreply.github.com>
Tue, 16 May 2023 06:20:30 +0000 (09:20 +0300)
committerGitHub <noreply@github.com>
Tue, 16 May 2023 06:20:30 +0000 (06:20 +0000)
Add http.client.HTTPConnection method get_proxy_response_headers() - this is a followup to https://github.com/python/cpython/pull/26152 which added it as a non-public attribute. This way we don't pre-compute a headers dictionary that most users will never access. The new method is properly public and documented and triggers full proxy header parsing into a dict only when actually called.

---------

Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
Doc/library/http.client.rst
Lib/http/client.py
Lib/test/test_httplib.py
Misc/NEWS.d/next/Library/2021-05-16-14-28-30.bpo-24964.Oa5Ie_.rst

index bf1f2e3920783d28a9ab4bdbeff71564e83f1415..46d616aae95c96a1b1acb0e2b0f504e3949bd8fa 100644 (file)
@@ -394,6 +394,17 @@ HTTPConnection Objects
       one will be automatically generated and transmitted if not provided in
       the headers argument.
 
+
+.. method:: HTTPConnection.get_proxy_response_headers()
+
+   Returns a dictionary with the headers of the response received from
+   the proxy server to the CONNECT request.
+
+   If the CONNECT request was not sent, the method returns an empty dictionary.
+
+   .. versionadded:: 3.12
+
+
 .. method:: HTTPConnection.connect()
 
    Connect to the server specified when the object was created.  By default,
index 50f2b4680769c8e280020312e22b0809c3d12997..59a9fd72b4722fdd793766e32647c47fdc420f45 100644 (file)
@@ -221,8 +221,9 @@ def _read_headers(fp):
             break
     return headers
 
-def parse_headers(fp, _class=HTTPMessage):
-    """Parses only RFC2822 headers from a file pointer.
+def _parse_header_lines(header_lines, _class=HTTPMessage):
+    """
+    Parses only RFC2822 headers from header lines.
 
     email Parser wants to see strings rather than bytes.
     But a TextIOWrapper around self.rfile would buffer too many bytes
@@ -231,10 +232,15 @@ def parse_headers(fp, _class=HTTPMessage):
     to parse.
 
     """
-    headers = _read_headers(fp)
-    hstring = b''.join(headers).decode('iso-8859-1')
+    hstring = b''.join(header_lines).decode('iso-8859-1')
     return email.parser.Parser(_class=_class).parsestr(hstring)
 
+def parse_headers(fp, _class=HTTPMessage):
+    """Parses only RFC2822 headers from a file pointer."""
+
+    headers = _read_headers(fp)
+    return _parse_header_lines(headers, _class)
+
 
 class HTTPResponse(io.BufferedIOBase):
 
@@ -858,7 +864,7 @@ class HTTPConnection:
         self._tunnel_host = None
         self._tunnel_port = None
         self._tunnel_headers = {}
-        self._proxy_response_headers = None
+        self._raw_proxy_headers = None
 
         (self.host, self.port) = self._get_hostport(host, port)
 
@@ -945,11 +951,11 @@ class HTTPConnection:
         try:
             (version, code, message) = response._read_status()
 
-            self._proxy_response_headers = parse_headers(response.fp)
+            self._raw_proxy_headers = _read_headers(response.fp)
 
             if self.debuglevel > 0:
-                for hdr, val in self._proxy_response_headers.items():
-                    print("header:", hdr + ":", val)
+                for header in self._raw_proxy_headers:
+                    print('header:', header.decode())
 
             if code != http.HTTPStatus.OK:
                 self.close()
@@ -958,6 +964,21 @@ class HTTPConnection:
         finally:
             response.close()
 
+    def get_proxy_response_headers(self):
+        """
+        Returns a dictionary with the headers of the response
+        received from the proxy server to the CONNECT request
+        sent to set the tunnel.
+
+        If the CONNECT request was not sent, the method returns
+        an empty dictionary.
+        """
+        return (
+            _parse_header_lines(self._raw_proxy_headers)
+            if self._raw_proxy_headers is not None
+            else {}
+        )
+
     def connect(self):
         """Connect to the host and port specified in __init__."""
         sys.audit("http.client.connect", self, self.host, self.port)
index 4b1d355f550b49bdb584ca1e98033ddb37d1e8c8..8955d45fa93dd4dec15bc3075c646464a01d3bfb 100644 (file)
@@ -2401,7 +2401,7 @@ class TunnelTests(TestCase):
         self.conn.set_tunnel('destination.com')
 
         self.conn.request('PUT', '/', '')
-        headers = self.conn._proxy_response_headers
+        headers = self.conn.get_proxy_response_headers()
         self.assertIn(expected_header, headers.items())
 
     def test_tunnel_leak(self):
index ba113673b7fbe5d269520ea0336e86c9e9e33aac..0904b162e63523b1e5c31c111ee8fc95b3b52d49 100644 (file)
@@ -1,3 +1,3 @@
-Added attribute '_proxy_response_headers' to HTTPConnection class. This
-attribute contains the headers of the proxy server response to the CONNECT
-request.
+Added :meth:`http.client.HTTPConnection.get_proxy_response_headers` that
+provides access to the HTTP headers on a proxy server response to the
+``CONNECT`` request.