Skip to content

Commit 85ec192

Browse files
nametkinblurb-it[bot]gpshead
authored
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 #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 <[email protected]>
1 parent 24d8b88 commit 85ec192

File tree

4 files changed

+44
-12
lines changed

4 files changed

+44
-12
lines changed

Doc/library/http.client.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,17 @@ HTTPConnection Objects
394394
one will be automatically generated and transmitted if not provided in
395395
the headers argument.
396396

397+
398+
.. method:: HTTPConnection.get_proxy_response_headers()
399+
400+
Returns a dictionary with the headers of the response received from
401+
the proxy server to the CONNECT request.
402+
403+
If the CONNECT request was not sent, the method returns an empty dictionary.
404+
405+
.. versionadded:: 3.12
406+
407+
397408
.. method:: HTTPConnection.connect()
398409

399410
Connect to the server specified when the object was created. By default,

Lib/http/client.py

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -221,8 +221,9 @@ def _read_headers(fp):
221221
break
222222
return headers
223223

224-
def parse_headers(fp, _class=HTTPMessage):
225-
"""Parses only RFC2822 headers from a file pointer.
224+
def _parse_header_lines(header_lines, _class=HTTPMessage):
225+
"""
226+
Parses only RFC2822 headers from header lines.
226227
227228
email Parser wants to see strings rather than bytes.
228229
But a TextIOWrapper around self.rfile would buffer too many bytes
@@ -231,10 +232,15 @@ def parse_headers(fp, _class=HTTPMessage):
231232
to parse.
232233
233234
"""
234-
headers = _read_headers(fp)
235-
hstring = b''.join(headers).decode('iso-8859-1')
235+
hstring = b''.join(header_lines).decode('iso-8859-1')
236236
return email.parser.Parser(_class=_class).parsestr(hstring)
237237

238+
def parse_headers(fp, _class=HTTPMessage):
239+
"""Parses only RFC2822 headers from a file pointer."""
240+
241+
headers = _read_headers(fp)
242+
return _parse_header_lines(headers, _class)
243+
238244

239245
class HTTPResponse(io.BufferedIOBase):
240246

@@ -858,7 +864,7 @@ def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
858864
self._tunnel_host = None
859865
self._tunnel_port = None
860866
self._tunnel_headers = {}
861-
self._proxy_response_headers = None
867+
self._raw_proxy_headers = None
862868

863869
(self.host, self.port) = self._get_hostport(host, port)
864870

@@ -945,11 +951,11 @@ def _tunnel(self):
945951
try:
946952
(version, code, message) = response._read_status()
947953

948-
self._proxy_response_headers = parse_headers(response.fp)
954+
self._raw_proxy_headers = _read_headers(response.fp)
949955

950956
if self.debuglevel > 0:
951-
for hdr, val in self._proxy_response_headers.items():
952-
print("header:", hdr + ":", val)
957+
for header in self._raw_proxy_headers:
958+
print('header:', header.decode())
953959

954960
if code != http.HTTPStatus.OK:
955961
self.close()
@@ -958,6 +964,21 @@ def _tunnel(self):
958964
finally:
959965
response.close()
960966

967+
def get_proxy_response_headers(self):
968+
"""
969+
Returns a dictionary with the headers of the response
970+
received from the proxy server to the CONNECT request
971+
sent to set the tunnel.
972+
973+
If the CONNECT request was not sent, the method returns
974+
an empty dictionary.
975+
"""
976+
return (
977+
_parse_header_lines(self._raw_proxy_headers)
978+
if self._raw_proxy_headers is not None
979+
else {}
980+
)
981+
961982
def connect(self):
962983
"""Connect to the host and port specified in __init__."""
963984
sys.audit("http.client.connect", self, self.host, self.port)

Lib/test/test_httplib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2401,7 +2401,7 @@ def test_proxy_response_headers(self):
24012401
self.conn.set_tunnel('destination.com')
24022402

24032403
self.conn.request('PUT', '/', '')
2404-
headers = self.conn._proxy_response_headers
2404+
headers = self.conn.get_proxy_response_headers()
24052405
self.assertIn(expected_header, headers.items())
24062406

24072407
def test_tunnel_leak(self):
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
Added attribute '_proxy_response_headers' to HTTPConnection class. This
2-
attribute contains the headers of the proxy server response to the CONNECT
3-
request.
1+
Added :meth:`http.client.HTTPConnection.get_proxy_response_headers` that
2+
provides access to the HTTP headers on a proxy server response to the
3+
``CONNECT`` request.

0 commit comments

Comments
 (0)