Reject absolute-form targets without an authority before parsing (#13858) by asvetlov · Pull Request #13860 · aio-libs/aiohttp · GitHub
Skip to content

Reject absolute-form targets without an authority before parsing (#13858) - #13860

Merged
asvetlov merged 1 commit into
aio-libs:3.14from
asvetlov:patchback/backports/3.14/ce960dd17e34f143762c14c32b9e14435ea7ce11/pr-13858
Sep 29, 2026
Merged

asvetlov merged 1 commit into
aio-libs:3.14from
asvetlov:patchback/backports/3.14/ce960dd17e34f143762c14c32b9e14435ea7ce11/pr-13858

Conversation

@asvetlov

Copy link
Copy Markdown
Member

Follow-up to #13855 for aio-libs/yarl#1937. With that change, yarl's default WHATWG mode reads a host from http:host/p, http:/host/p and http:///host/p, as browsers do. The Python and Cython request parsers passed an absolute-form request target to URL() and rejected it only if yarl found no host. They now first check that the target has // followed by a non-empty authority, so GET http:///protected and GET http:/protected stay rejected as RFC 9110 requires, whatever yarl version is installed.

The client tests used http:///example.com and http:///path as URLs without a host. They now use http:// where an invalid URL is needed, and http:///example.com is dropped from the invalid URL test data. With yarl#1937 it is http://example.com/, and as a redirect it leads to example.com, as in browsers.

No with the released yarl, where such targets were already rejected. With yarl#1937, the server keeps rejecting absolute-form targets without an authority instead of reading a host from them.

No, it is one string check shared by both parsers.

Follow-up to #13855; needed by aio-libs/yarl#1937.

  • I think the code is well written
  • Unit tests for the changes exist
  • Documentation reflects the changes: N/A, no documented behavior changes
  • If you provide code modification, please add yourself to CONTRIBUTORS.txt: N/A, already listed
  • Add a new news fragment into the CHANGES/ folder

Drafted with Claude Code (Claude Opus 5.5); reviewed by @asvetlov.

Agent run details (optional, for reviewers)

Tests with the Cython extensions built (llhttp generated, make cythonize, build_ext --inplace): pytest tests -n auto, 5246 passed, 22 skipped, 17 xfailed, both with yarl master (f761c37) and with the yarl#1937 branch (8d0f111). Pure-Python run of test_http_parser.py, test_client_functional.py and test_client_session.py with the yarl#1937 branch: 884 passed.
Lint: black, isort, codespell passed. The local pre-commit flake8 hook fails to load flake8-requirements (pkg_resources missing), so flake8 (E, F, W) was run directly and passed. yesqa was skipped for the same reason, because it wrongly strips # noqa: I900.

(cherry picked from commit ce960dd)

What do these changes do?

Are there changes in behavior for the user?

Is it a substantial burden for the maintainers to support this?

Related issue number

Checklist

  • I think the code is well written
  • Unit tests for the changes exist
  • Documentation reflects the changes
  • If you provide code modification, please add yourself to CONTRIBUTORS.txt
    • The format is <Name> <Surname>.
    • Please keep alphabetical order, the file is sorted by names.
  • Add a new news fragment into the CHANGES/ folder
    • name it <issue_or_pr_num>.<type>.rst (e.g. 588.bugfix.rst)

    • if you don't have an issue number, change it to the pull request
      number after creating the PR

      • .bugfix: A bug fix for something the maintainers deemed an
        improper undesired behavior that got corrected to match
        pre-agreed expectations.
      • .feature: A new behavior, public APIs. That sort of stuff.
      • .deprecation: A declaration of future API removals and breaking
        changes in behavior.
      • .breaking: When something public is removed in a breaking way.
        Could be deprecated in an earlier release.
      • .doc: Notable updates to the documentation structure or build
        process.
      • .packaging: Notes for downstreams about unobvious side effects
        and tooling. Changes in the test invocation considerations and
        runtime assumptions.
      • .contrib: Stuff that affects the contributor experience. e.g.
        Running tests, building the docs, setting up the development
        environment.
      • .misc: Changes that are hard to assign to any of the above
        categories.
    • Make sure to use full sentences with correct case and punctuation,
      for example:

      Fixed issue with non-ascii contents in doctest text files
      -- by :user:`contributor-gh-handle`.

      Use the past tense or the present tense a non-imperative mood,
      referring to what's changed compared to the last released version
      of this project.

…-libs#13858)

<!-- Thank you for your contribution! -->

Follow-up to aio-libs#13855 for aio-libs/yarl#1937. With that change, yarl's
default WHATWG mode reads a host from `http:host/p`, `http:/host/p` and
`http:///host/p`, as browsers do. The Python and Cython request parsers
passed an absolute-form request target to `URL()` and rejected it only
if yarl found no host. They now first check that the target has `//`
followed by a non-empty authority, so `GET http:///protected` and `GET
http:/protected` stay rejected as RFC 9110 requires, whatever yarl
version is installed.

The client tests used `http:///example.com` and `http:///path` as URLs
without a host. They now use `http://` where an invalid URL is needed,
and `http:///example.com` is dropped from the invalid URL test data.
With yarl#1937 it is `http://example.com/`, and as a redirect it leads
to `example.com`, as in browsers.

No with the released yarl, where such targets were already rejected.
With yarl#1937, the server keeps rejecting absolute-form targets without
an authority instead of reading a host from them.

No, it is one string check shared by both parsers.

Follow-up to aio-libs#13855; needed by aio-libs/yarl#1937.

- [x] I think the code is well written
- [x] Unit tests for the changes exist
- [ ] Documentation reflects the changes: N/A, no documented behavior
changes
- [ ] If you provide code modification, please add yourself to
`CONTRIBUTORS.txt`: N/A, already listed
- [x] Add a new news fragment into the `CHANGES/` folder

Drafted with Claude Code (Claude Opus 5.5); reviewed by @asvetlov.

<details>
<summary>Agent run details (optional, for reviewers)</summary>

Tests with the Cython extensions built (llhttp generated, `make
cythonize`, `build_ext --inplace`): `pytest tests -n auto`, 5246 passed,
22 skipped, 17 xfailed, both with yarl master (f761c37) and with the
yarl#1937 branch (8d0f111). Pure-Python run of `test_http_parser.py`,
`test_client_functional.py` and `test_client_session.py` with the
yarl#1937 branch: 884 passed.
Lint: black, isort, codespell passed. The local pre-commit flake8 hook
fails to load `flake8-requirements` (`pkg_resources` missing), so flake8
(E, F, W) was run directly and passed. yesqa was skipped for the same
reason, because it wrongly strips `# noqa: I900`.
</details>

(cherry picked from commit ce960dd)
@asvetlov
asvetlov requested a review from webknjaz as a code owner September 29, 2026 09:11
@codecov

codecov Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

@codspeed

codspeed Bot commented Sep 29, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 82 untouched benchmarks
⏩ 7 skipped benchmarks1


Comparing asvetlov:patchback/backports/3.14/ce960dd17e34f143762c14c32b9e14435ea7ce11/pr-13858 (d4763ba) with 3.14 (23083b3)

Open in CodSpeed

Footnotes

  1. 7 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

@greptile-apps

greptile-apps Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

@asvetlov
asvetlov merged commit 186065c into aio-libs:3.14 Sep 29, 2026
44 of 45 checks passed
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