[Python-checkins] gh-69152: add method get_proxy_response_headers to HTTPConnection class (#104248)

gpshead webhook-mailer at python.org
Tue May 16 02:20:51 EDT 2023


https://github.com/python/cpython/commit/85ec192ac4b000d4e47df6123b65eacbd1fdccfa
commit: 85ec192ac4b000d4e47df6123b65eacbd1fdccfa
branch: main
author: Alexey Namyotkin <62434915+nametkin at users.noreply.github.com>
committer: gpshead <greg at krypto.org>
date: 2023-05-16T06:20:30Z
summary:

gh-69152: add method get_proxy_response_headers to HTTPConnection class (#104248)

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 at krypto.org>

files:
M Doc/library/http.client.rst
M Lib/http/client.py
M Lib/test/test_httplib.py
M Misc/NEWS.d/next/Library/2021-05-16-14-28-30.bpo-24964.Oa5Ie_.rst

diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst
index bf1f2e392078..46d616aae95c 100644
--- a/Doc/library/http.client.rst
+++ b/Doc/library/http.client.rst
@@ -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,
diff --git a/Lib/http/client.py b/Lib/http/client.py
index 50f2b4680769..59a9fd72b472 100644
--- a/Lib/http/client.py
+++ b/Lib/http/client.py
@@ -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 @@ def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
         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 @@ def _tunnel(self):
         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 @@ def _tunnel(self):
         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)
diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py
index 4b1d355f550b..8955d45fa93d 100644
--- a/Lib/test/test_httplib.py
+++ b/Lib/test/test_httplib.py
@@ -2401,7 +2401,7 @@ def test_proxy_response_headers(self):
         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):
diff --git a/Misc/NEWS.d/next/Library/2021-05-16-14-28-30.bpo-24964.Oa5Ie_.rst b/Misc/NEWS.d/next/Library/2021-05-16-14-28-30.bpo-24964.Oa5Ie_.rst
index ba113673b7fb..0904b162e635 100644
--- a/Misc/NEWS.d/next/Library/2021-05-16-14-28-30.bpo-24964.Oa5Ie_.rst
+++ b/Misc/NEWS.d/next/Library/2021-05-16-14-28-30.bpo-24964.Oa5Ie_.rst
@@ -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.



More information about the Python-checkins mailing list