diff options
author | Alexey Namyotkin <62434915+nametkin@users.noreply.github.com> | 2023-05-16 06:20:30 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-05-16 06:20:30 (GMT) |
commit | 85ec192ac4b000d4e47df6123b65eacbd1fdccfa (patch) | |
tree | 6029b8cda6865e42154c9bcea0e419c8ba0f54eb | |
parent | 24d8b88420b81fc60aeb0cbcacef1e72d633824a (diff) | |
download | cpython-85ec192ac4b000d4e47df6123b65eacbd1fdccfa.zip cpython-85ec192ac4b000d4e47df6123b65eacbd1fdccfa.tar.gz cpython-85ec192ac4b000d4e47df6123b65eacbd1fdccfa.tar.bz2 |
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.rst | 11 | ||||
-rw-r--r-- | Lib/http/client.py | 37 | ||||
-rw-r--r-- | Lib/test/test_httplib.py | 2 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2021-05-16-14-28-30.bpo-24964.Oa5Ie_.rst | 6 |
4 files changed, 44 insertions, 12 deletions
diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst index bf1f2e3..46d616a 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 50f2b46..59a9fd7 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 4b1d355..8955d45 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 ba11367..0904b16 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. |