summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey Namyotkin <62434915+nametkin@users.noreply.github.com>2023-05-16 09:20:30 +0300
committerGitHub <noreply@github.com>2023-05-16 06:20:30 +0000
commit85ec192ac4b000d4e47df6123b65eacbd1fdccfa (patch)
tree6029b8cda6865e42154c9bcea0e419c8ba0f54eb
parent24d8b88420b81fc60aeb0cbcacef1e72d633824a (diff)
downloadcpython-git-85ec192ac4b000d4e47df6123b65eacbd1fdccfa.tar.gz
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@krypto.org>
-rw-r--r--Doc/library/http.client.rst11
-rw-r--r--Lib/http/client.py37
-rw-r--r--Lib/test/test_httplib.py2
-rw-r--r--Misc/NEWS.d/next/Library/2021-05-16-14-28-30.bpo-24964.Oa5Ie_.rst6
4 files changed, 44 insertions, 12 deletions
diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst
index bf1f2e3920..46d616aae9 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 50f2b46807..59a9fd72b4 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 @@ 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)
diff --git a/Lib/test/test_httplib.py b/Lib/test/test_httplib.py
index 4b1d355f55..8955d45fa9 100644
--- a/Lib/test/test_httplib.py
+++ b/Lib/test/test_httplib.py
@@ -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):
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 ba113673b7..0904b162e6 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.