Skip to content

Commit

Permalink
pythongh-73561: Omit interface scope from IPv6 when used as Host head…
Browse files Browse the repository at this point in the history
…er (python#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]
  • Loading branch information
mib1185 authored Nov 19, 2023
1 parent 7c9f267 commit ce1096f
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 2 deletions.
12 changes: 10 additions & 2 deletions Lib/http/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand Down
16 changes: 16 additions & 0 deletions Lib/test/test_httplib.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Omit the interface scope from an IPv6 address when used as Host header by :mod:`http.client`.

0 comments on commit ce1096f

Please sign in to comment.