Skip to content

Commit ce1096f

Browse files
authored
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 <[email protected]> [Google LLC]
1 parent 7c9f267 commit ce1096f

File tree

3 files changed

+27
-2
lines changed

3 files changed

+27
-2
lines changed

Lib/http/client.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,13 @@ def _encode(data, name='data'):
172172
"if you want to send it encoded in UTF-8." %
173173
(name.title(), data[err.start:err.end], name)) from None
174174

175+
def _strip_ipv6_iface(enc_name: bytes) -> bytes:
176+
"""Remove interface scope from IPv6 address."""
177+
enc_name, percent, _ = enc_name.partition(b"%")
178+
if percent:
179+
assert enc_name.startswith(b'['), enc_name
180+
enc_name += b']'
181+
return enc_name
175182

176183
class HTTPMessage(email.message.Message):
177184
# XXX The only usage of this method is in
@@ -1194,7 +1201,7 @@ def putrequest(self, method, url, skip_host=False,
11941201
netloc_enc = netloc.encode("ascii")
11951202
except UnicodeEncodeError:
11961203
netloc_enc = netloc.encode("idna")
1197-
self.putheader('Host', netloc_enc)
1204+
self.putheader('Host', _strip_ipv6_iface(netloc_enc))
11981205
else:
11991206
if self._tunnel_host:
12001207
host = self._tunnel_host
@@ -1211,8 +1218,9 @@ def putrequest(self, method, url, skip_host=False,
12111218
# As per RFC 273, IPv6 address should be wrapped with []
12121219
# when used as Host header
12131220

1214-
if host.find(':') >= 0:
1221+
if ":" in host:
12151222
host_enc = b'[' + host_enc + b']'
1223+
host_enc = _strip_ipv6_iface(host_enc)
12161224

12171225
if port == self.default_port:
12181226
self.putheader('Host', host_enc)

Lib/test/test_httplib.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,22 @@ def test_ipv6host_header(self):
283283
conn.request('GET', '/foo')
284284
self.assertTrue(sock.data.startswith(expected))
285285

286+
expected = b'GET /foo HTTP/1.1\r\nHost: [fe80::]\r\n' \
287+
b'Accept-Encoding: identity\r\n\r\n'
288+
conn = client.HTTPConnection('[fe80::%2]')
289+
sock = FakeSocket('')
290+
conn.sock = sock
291+
conn.request('GET', '/foo')
292+
self.assertTrue(sock.data.startswith(expected))
293+
294+
expected = b'GET /foo HTTP/1.1\r\nHost: [fe80::]:81\r\n' \
295+
b'Accept-Encoding: identity\r\n\r\n'
296+
conn = client.HTTPConnection('[fe80::%2]:81')
297+
sock = FakeSocket('')
298+
conn.sock = sock
299+
conn.request('GET', '/foo')
300+
self.assertTrue(sock.data.startswith(expected))
301+
286302
def test_malformed_headers_coped_with(self):
287303
# Issue 19996
288304
body = "HTTP/1.1 200 OK\r\nFirst: val\r\n: nval\r\nSecond: val\r\n\r\n"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Omit the interface scope from an IPv6 address when used as Host header by :mod:`http.client`.

0 commit comments

Comments
 (0)