diff options
author | Michael Handler <mh@grendel.net> | 2023-04-05 04:55:24 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-04-05 04:55:24 (GMT) |
commit | 1a8f862e329c3872a11d4ef8eb85cf353ca2f4d5 (patch) | |
tree | ad3f1ab9e0a427dd2fa61f976e169d06eea9a053 /Lib/http | |
parent | a62ff970750267441a7175b1bd6d6b2ec59d9a2c (diff) | |
download | cpython-1a8f862e329c3872a11d4ef8eb85cf353ca2f4d5.zip cpython-1a8f862e329c3872a11d4ef8eb85cf353ca2f4d5.tar.gz cpython-1a8f862e329c3872a11d4ef8eb85cf353ca2f4d5.tar.bz2 |
gh-66897: Upgrade HTTP CONNECT to protocol HTTP/1.1 (#8305)
* bpo-22708: Upgrade HTTP CONNECT to protocol HTTP/1.1 (GH-NNNN)
Use protocol HTTP/1.1 when sending HTTP CONNECT tunnelling requests;
generate Host: headers if one is not already provided (required by
HTTP/1.1), convert IDN domains to punycode in HTTP CONNECT requests.
* Refactor tests to pass under -bb (fix ByteWarnings); missed some lines >80.
* Use consistent 'tunnelling' spelling in Lib/http/client.py
* Lib/test/test_httplib: Remove remnant of obsoleted test.
* Use dict.copy() not copy.copy()
* fix version changed
* Update Lib/http/client.py
Co-authored-by: bgehman <bgehman@users.noreply.github.com>
* Switch to for/else: syntax, as suggested
* Don't use for: else:
* Sure, fine, w/e
* Oops
* 1nm to the left
---------
Co-authored-by: Éric <merwok@netwok.org>
Co-authored-by: bgehman <bgehman@users.noreply.github.com>
Co-authored-by: Oleg Iarygin <oleg@arhadthedev.net>
Diffstat (limited to 'Lib/http')
-rw-r--r-- | Lib/http/client.py | 25 |
1 files changed, 19 insertions, 6 deletions
diff --git a/Lib/http/client.py b/Lib/http/client.py index bd55e7d..0f5cd35 100644 --- a/Lib/http/client.py +++ b/Lib/http/client.py @@ -870,9 +870,9 @@ class HTTPConnection: def set_tunnel(self, host, port=None, headers=None): """Set up host and port for HTTP CONNECT tunnelling. - In a connection that uses HTTP CONNECT tunneling, the host passed to the - constructor is used as a proxy server that relays all communication to - the endpoint passed to `set_tunnel`. This done by sending an HTTP + In a connection that uses HTTP CONNECT tunnelling, the host passed to + the constructor is used as a proxy server that relays all communication + to the endpoint passed to `set_tunnel`. This done by sending an HTTP CONNECT request to the proxy server when the connection is established. This method must be called before the HTTP connection has been @@ -880,6 +880,13 @@ class HTTPConnection: The headers argument should be a mapping of extra HTTP headers to send with the CONNECT request. + + As HTTP/1.1 is used for HTTP CONNECT tunnelling request, as per the RFC + (https://tools.ietf.org/html/rfc7231#section-4.3.6), a HTTP Host: + header must be provided, matching the authority-form of the request + target provided as the destination for the CONNECT request. If a + HTTP Host: header is not provided via the headers argument, one + is generated and transmitted automatically. """ if self.sock: @@ -887,10 +894,15 @@ class HTTPConnection: self._tunnel_host, self._tunnel_port = self._get_hostport(host, port) if headers: - self._tunnel_headers = headers + self._tunnel_headers = headers.copy() else: self._tunnel_headers.clear() + if not any(header.lower() == "host" for header in self._tunnel_headers): + encoded_host = self._tunnel_host.encode("idna").decode("ascii") + self._tunnel_headers["Host"] = "%s:%d" % ( + encoded_host, self._tunnel_port) + def _get_hostport(self, host, port): if port is None: i = host.rfind(':') @@ -915,8 +927,9 @@ class HTTPConnection: self.debuglevel = level def _tunnel(self): - connect = b"CONNECT %s:%d HTTP/1.0\r\n" % ( - self._tunnel_host.encode("ascii"), self._tunnel_port) + connect = b"CONNECT %s:%d %s\r\n" % ( + self._tunnel_host.encode("idna"), self._tunnel_port, + self._http_vsn_str.encode("ascii")) headers = [connect] for header, value in self._tunnel_headers.items(): headers.append(f"{header}: {value}\r\n".encode("latin-1")) |