Skip to content

http.client: AssertionError in putrequest() on an absolute-URL netloc with percent-encoded userinfo #158182

Description

@not-ekalabya

Bug report

HTTPConnection.putrequest() raises a raw AssertionError instead of
sending the request (or raising an http.client-family exception) when
given an absolute-URL request whose netloc contains RFC 3986-legal
percent-encoding outside of an IPv6 zone identifier -- most plausibly
percent-encoded userinfo, which is required whenever a username or
password contains @, :, /, or %.

Root cause

_strip_ipv6_iface() (Lib/http/client.py:187-193) is meant to strip an
IPv6 zone id from a bracketed literal ([fe80::1%eth0]). It partitions the
whole netloc on the first % and asserts the remainder starts with [:

enc_name = enc_name.partition(b"%")[0]
assert enc_name.startswith(b'[')

HTTPConnection.putrequest() (Lib/http/client.py:1268-1276) passes the
entire netloc of an absolute-URL request (the normal shape for HTTP-proxy
requests, which is exactly what urllib.request uses when going through an
HTTP proxy) into this helper -- not just a bracketed IPv6 literal. Any %
earlier in the netloc than an IPv6 zone id (e.g. in percent-encoded
userinfo) makes the assertion fail.

Reproduction

import http.client
conn = http.client.HTTPConnection("example.com")
conn.putrequest("GET", "http://user:p%40ss@host.example/path")
AssertionError: b'user:p'

A percent-encoded hostname alone triggers the same thing:

conn.putrequest("GET", "http://ex%41mple.com/path")
AssertionError: b'ex'

A plain host with no % in the netloc proceeds normally.

Note this would also break the case it's actually meant to handle,
whenever userinfo is present alongside an IPv6 zone id (e.g.
user%41@[fe80::1%eth0]/), since the netloc is partitioned at the
userinfo's % first.

Your environment

  • CPython versions tested: main (commit 64d315ac11a341fa5622718a679d0f6ef1a8dd6b)
  • Also reproduces under shipped CPython 3.12; this is long-standing, not a
    new regression.

Suggested fix

Have _strip_ipv6_iface only act on an actual bracketed literal, e.g.
return the input unchanged unless it starts with [ after the partition,
and/or strip userinfo from the netloc before generating the Host header
(RFC 7230 forbids userinfo in Host regardless), which fixes both the
crash and the currently-malformed Host: user:p%40ss@host value that would
otherwise be sent.

I'll follow up with a PR.

Found via an automated code-review pass using GLM-5.3-flash paired with
scopegrep, a semantic code-retrieval tool; independently re-verified by
hand against the source above.

Linked PRs

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions