gh-73561: Omit interface scope from IPv6 when used as Host header (#93324)

Omit the `@interface_scope` from an IPv6 address when used as Host header by `http.client`.

---------

Co-authored-by: Gregory P. Smith <greg@krypto.org> [Google LLC]
This commit is contained in:
Michael 2023-11-19 23:37:13 +01:00 committed by GitHub
parent 7c9f2677fb
commit ce1096f974
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 27 additions and 2 deletions

View file

@ -172,6 +172,13 @@ def _encode(data, name='data'):
"if you want to send it encoded in UTF-8." %
(name.title(), data[err.start:err.end], name)) from None
def _strip_ipv6_iface(enc_name: bytes) -> bytes:
"""Remove interface scope from IPv6 address."""
enc_name, percent, _ = enc_name.partition(b"%")
if percent:
assert enc_name.startswith(b'['), enc_name
enc_name += b']'
return enc_name
class HTTPMessage(email.message.Message):
# XXX The only usage of this method is in
@ -1194,7 +1201,7 @@ def putrequest(self, method, url, skip_host=False,
netloc_enc = netloc.encode("ascii")
except UnicodeEncodeError:
netloc_enc = netloc.encode("idna")
self.putheader('Host', netloc_enc)
self.putheader('Host', _strip_ipv6_iface(netloc_enc))
else:
if self._tunnel_host:
host = self._tunnel_host
@ -1211,8 +1218,9 @@ def putrequest(self, method, url, skip_host=False,
# As per RFC 273, IPv6 address should be wrapped with []
# when used as Host header
if host.find(':') >= 0:
if ":" in host:
host_enc = b'[' + host_enc + b']'
host_enc = _strip_ipv6_iface(host_enc)
if port == self.default_port:
self.putheader('Host', host_enc)

View file

@ -283,6 +283,22 @@ def test_ipv6host_header(self):
conn.request('GET', '/foo')
self.assertTrue(sock.data.startswith(expected))
expected = b'GET /foo HTTP/1.1\r\nHost: [fe80::]\r\n' \
b'Accept-Encoding: identity\r\n\r\n'
conn = client.HTTPConnection('[fe80::%2]')
sock = FakeSocket('')
conn.sock = sock
conn.request('GET', '/foo')
self.assertTrue(sock.data.startswith(expected))
expected = b'GET /foo HTTP/1.1\r\nHost: [fe80::]:81\r\n' \
b'Accept-Encoding: identity\r\n\r\n'
conn = client.HTTPConnection('[fe80::%2]:81')
sock = FakeSocket('')
conn.sock = sock
conn.request('GET', '/foo')
self.assertTrue(sock.data.startswith(expected))
def test_malformed_headers_coped_with(self):
# Issue 19996
body = "HTTP/1.1 200 OK\r\nFirst: val\r\n: nval\r\nSecond: val\r\n\r\n"

View file

@ -0,0 +1 @@
Omit the interface scope from an IPv6 address when used as Host header by :mod:`http.client`.