From d0d9349c3239e5f66a5ff4fe04f71d85fb80eb3f Mon Sep 17 00:00:00 2001 From: Nametkin Date: Sat, 6 May 2023 23:05:10 +0300 Subject: [PATCH 1/7] add method get_proxy_response_headers --- Lib/http/client.py | 37 +++++++++++++++++++++++++++++-------- Lib/test/test_httplib.py | 2 +- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/Lib/http/client.py b/Lib/http/client.py index 50f2b4680769c8..59a9fd72b4722f 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 4b1d355f550b49..8955d45fa93dd4 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): From f39feb945454130b30cacf5a1fd087866b85badc Mon Sep 17 00:00:00 2001 From: Nametkin Date: Sat, 6 May 2023 23:05:10 +0300 Subject: [PATCH 2/7] add method get_proxy_response_headers --- Lib/http/client.py | 37 +++++++++++++++++++++++++++++-------- Lib/test/test_httplib.py | 2 +- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/Lib/http/client.py b/Lib/http/client.py index 50f2b4680769c8..59a9fd72b4722f 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 4b1d355f550b49..8955d45fa93dd4 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): From 0be0d47b708428c9069c11030fc3120d84030d44 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Mon, 8 May 2023 11:52:57 +0000 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst diff --git a/Misc/NEWS.d/next/Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst b/Misc/NEWS.d/next/Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst new file mode 100644 index 00000000000000..3388f6df4d9563 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst @@ -0,0 +1,7 @@ +Added attribute '_raw_proxy_headers' to HTTPConnection class. This +attribute contains the unprocessed header lines of the proxy server response +to the CONNECT request. +Added method get_proxy_response_headers to HTTPConnection class that allows +to get a dictionary with headers of the proxy server response +to the CONNECT request. +The HTTPConnection class attribute _proxy_response_headers has been removed. From cdff36018545c5cc343f1f48305e7a2b544b1db3 Mon Sep 17 00:00:00 2001 From: Alexey Namyotkin <62434915+nametkin@users.noreply.github.com> Date: Mon, 8 May 2023 15:02:48 +0300 Subject: [PATCH 4/7] Delete trailing whitespaces --- .../Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst b/Misc/NEWS.d/next/Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst index 3388f6df4d9563..8fce6ef29850bb 100644 --- a/Misc/NEWS.d/next/Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst +++ b/Misc/NEWS.d/next/Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst @@ -1,7 +1,7 @@ Added attribute '_raw_proxy_headers' to HTTPConnection class. This -attribute contains the unprocessed header lines of the proxy server response +attribute contains the unprocessed header lines of the proxy server response to the CONNECT request. Added method get_proxy_response_headers to HTTPConnection class that allows -to get a dictionary with headers of the proxy server response +to get a dictionary with headers of the proxy server response to the CONNECT request. -The HTTPConnection class attribute _proxy_response_headers has been removed. +The HTTPConnection class attribute _proxy_response_headers has been removed. From 7288cf65f06db90258f0ca1595ddfe64f59cfe2e Mon Sep 17 00:00:00 2001 From: Nametkin Date: Wed, 10 May 2023 11:51:50 +0300 Subject: [PATCH 5/7] update docs and news --- Doc/library/http.client.rst | 11 +++++++++++ Lib/http/client.py | 3 +-- .../Library/2021-05-16-14-28-30.bpo-24964.Oa5Ie_.rst | 6 +++--- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Doc/library/http.client.rst b/Doc/library/http.client.rst index eb8c1e198e2b09..b5db79e7034108 100644 --- a/Doc/library/http.client.rst +++ b/Doc/library/http.client.rst @@ -378,6 +378,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 59a9fd72b4722f..ef09f9eb756eb9 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -967,8 +967,7 @@ def _tunnel(self): 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. + received from the proxy server to the CONNECT request. If the CONNECT request was not sent, the method returns an empty dictionary. 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 ba113673b7fbe5..ae49f1d0e13742 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 method get_proxy_response_headers to HTTPConnection class that allows +to get a dictionary with headers of the proxy server response +to the CONNECT request. From 2dfc32635366ac87c1e54d691252f308f8713ae4 Mon Sep 17 00:00:00 2001 From: Nametkin Date: Wed, 10 May 2023 11:59:49 +0300 Subject: [PATCH 6/7] delete Misc/NEWS.d/next/Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst --- .../Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 Misc/NEWS.d/next/Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst diff --git a/Misc/NEWS.d/next/Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst b/Misc/NEWS.d/next/Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst deleted file mode 100644 index 8fce6ef29850bb..00000000000000 --- a/Misc/NEWS.d/next/Library/2023-05-08-11-52-56.gh-issue-69152.tlH-q1.rst +++ /dev/null @@ -1,7 +0,0 @@ -Added attribute '_raw_proxy_headers' to HTTPConnection class. This -attribute contains the unprocessed header lines of the proxy server response -to the CONNECT request. -Added method get_proxy_response_headers to HTTPConnection class that allows -to get a dictionary with headers of the proxy server response -to the CONNECT request. -The HTTPConnection class attribute _proxy_response_headers has been removed. From a21af198c99bd132209af447ba94c928e0f2a34c Mon Sep 17 00:00:00 2001 From: "Gregory P. Smith" Date: Mon, 15 May 2023 22:53:14 -0700 Subject: [PATCH 7/7] reworded news and add ReST formatting. --- .../next/Library/2021-05-16-14-28-30.bpo-24964.Oa5Ie_.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 ae49f1d0e13742..0904b162e63523 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 method get_proxy_response_headers to HTTPConnection class that allows -to get a dictionary with 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.