Skip to content

Commit f6e11ea

Browse files
[3.11] gh-73561: Omit interface scope from IPv6 when used as Host header (GH-93324) (#112273)
gh-73561: Omit interface scope from IPv6 when used as Host header (GH-93324) Omit the `@interface_scope` from an IPv6 address when used as Host header by `http.client`. --------- (cherry picked from commit ce1096f) [Google LLC] Co-authored-by: Michael <[email protected]>
1 parent 11b91be commit f6e11ea

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
@@ -1161,7 +1168,7 @@ def putrequest(self, method, url, skip_host=False,
11611168
netloc_enc = netloc.encode("ascii")
11621169
except UnicodeEncodeError:
11631170
netloc_enc = netloc.encode("idna")
1164-
self.putheader('Host', netloc_enc)
1171+
self.putheader('Host', _strip_ipv6_iface(netloc_enc))
11651172
else:
11661173
if self._tunnel_host:
11671174
host = self._tunnel_host
@@ -1178,8 +1185,9 @@ def putrequest(self, method, url, skip_host=False,
11781185
# As per RFC 273, IPv6 address should be wrapped with []
11791186
# when used as Host header
11801187

1181-
if host.find(':') >= 0:
1188+
if ":" in host:
11821189
host_enc = b'[' + host_enc + b']'
1190+
host_enc = _strip_ipv6_iface(host_enc)
11831191

11841192
if port == self.default_port:
11851193
self.putheader('Host', host_enc)

Lib/test/test_httplib.py

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

288+
expected = b'GET /foo HTTP/1.1\r\nHost: [fe80::]\r\n' \
289+
b'Accept-Encoding: identity\r\n\r\n'
290+
conn = client.HTTPConnection('[fe80::%2]')
291+
sock = FakeSocket('')
292+
conn.sock = sock
293+
conn.request('GET', '/foo')
294+
self.assertTrue(sock.data.startswith(expected))
295+
296+
expected = b'GET /foo HTTP/1.1\r\nHost: [fe80::]:81\r\n' \
297+
b'Accept-Encoding: identity\r\n\r\n'
298+
conn = client.HTTPConnection('[fe80::%2]:81')
299+
sock = FakeSocket('')
300+
conn.sock = sock
301+
conn.request('GET', '/foo')
302+
self.assertTrue(sock.data.startswith(expected))
303+
288304
def test_malformed_headers_coped_with(self):
289305
# Issue 19996
290306
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)