Repository navigation
Potential issue: dot-segment removal drops the trailing slash (RFC 3986 section 5.2.4), so "http://e.org/a/.." != "http://e.org/" #3819
Replies: 1 comment
|
Confirmed, and the mechanism is a single edge case in normalize_path(). The trailing slash is not "dropped" as a separate step. The empty leading segment that represents it is all that is left after the .. is processed, and an empty string is not a slash. For the path "/a/.." the components are an empty string, "a", and "..". The empty string is appended, "a" is appended, then ".." pops "a" (the output-is-not-just-root check is true at that point). The remaining list is a single empty string, and joining it with "/" produces an empty string. Measured on this checkout: normalize_path on "/a/.." returns an empty string. So httpx.URL with "http://e.org/a/.." stringifies to "http://e.org" (path empty, and str omits an empty path under an authority), and "http://e.org/a/b/.." becomes "http://e.org/a". Per RFC 3986 section 5.2.4, removing the last segment and its preceding slash from /a leaves /, so /a/.. should normalize to /, not empty, and /a/b/.. should normalize to /a/. The "/a/../" case only survives because the trailing empty component is appended after the pop and re-forms the slash, which shows the loss is specific to a path whose final segment is . or ... The guard that checks output is non-empty and not just the root marker also suppresses the pop when the buffer is only the root marker, which is why /.. collapses to an empty string instead of /. Fix direction: after the loop, when the last consumed component was . or .. (meaning the reference denotes a directory), ensure the joined result ends with /, and treat the root-only case as / rather than an empty string. Both changes are local to normalize_path(). urlparse() calls it only when the URL has a scheme or authority, so relative paths are unaffected by the root-marker part. |
Uh oh!
There was an error while loading. Please reload this page.
Found while property-based testing URL normalization.
httpx._urlparse.normalize_pathclaims to implement RFC 3986 section 5.2.4, but a trailing "." or ".." segment loses its trailing slash:User-visible consequence:
Browsers (WHATWG) agree with the RFC here. The existing tests pin mid-path cases ("/abc/def/../ghi/./jkl" -> "/abc/ghi/jkl") but nothing with a trailing dot segment. Note the docstring example itself would change under a strict reading: normalize_path("/path/./to/somewhere/..") is currently documented as "/path/to", where the RFC algorithm gives "/path/to/" — so I wanted to check whether the current behaviour is a deliberate simplification before sending a PR.
The fix is ready on a fork branch: https://github.com/Sun-Wukhan/httpx/tree/fix/url-normalize-path-rfc (the full tests/models suite passes with it). Raising it here first since PR creation on this repo is limited to collaborators; happy for a maintainer to open the PR from the branch.
All reactions