Skip to content

Decode percent-encoded unreserved characters during normalization - #151

Open
gaoflow wants to merge 1 commit into
python-hyper:mainfrom
gaoflow:fix-unreserved-pct-decode
Open

Decode percent-encoded unreserved characters during normalization#151
gaoflow wants to merge 1 commit into
python-hyper:mainfrom
gaoflow:fix-unreserved-pct-decode

Conversation

@gaoflow

@gaoflow gaoflow commented Jul 31, 2026

Copy link
Copy Markdown

normalize_percent_characters() only upper-cases percent-triples; RFC 3986 §6.2.2.2 also requires decoding triples of unreserved characters (ALPHA / DIGIT / "-" / "." / "_" / "~"). Because the decode step is missing, the library's own equality predicate returns the wrong answer for URIs the RFC treats as equivalent:

uri_reference("http://example.com/%7Efoo").normalized_equality(
    uri_reference("http://example.com/~foo"))   # False, RFC says equivalent

The fix decodes unreserved triples after the existing upper-case pass. Reserved octets (%2F, %21, …) and non-ASCII octets (%DF) stay encoded. One visible consequence: %2E in paths now decodes before dot-segment removal, so /a/%2E%2E/b normalizes to /b per the RFC's ordering.

The module's UNRESERVED_CHARS constant also includes ! (a sub-delimiter), so the decode set is defined directly from RFC 3986 §2.3.

Tests enumerate all 256 octets across userinfo, path, query, and fragment, plus idempotence and normalized_equality.

RFC 3986 Section 6.2.2.2 requires percent-encodings of unreserved
characters to be decoded. normalize_percent_characters() only upper-cased
them, so normalized_equality() returned False for URIs the RFC treats as
equivalent. Reserved and non-ASCII octets stay encoded.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant