[PR #13246/25561d97 backport][3.15] Strip trailing OWS from header values in the C parser by patchback[bot] · Pull Request #13248 · aio-libs/aiohttp · GitHub
Skip to content

[PR #13246/25561d97 backport][3.15] Strip trailing OWS from header values in the C parser - #13248

Merged
Dreamsorcerer merged 1 commit into
3.15from
patchback/backports/3.15/25561d975a7a9e10eec3b381bc769737b57b3c9b/pr-13246
Jul 26, 2026
Merged

Dreamsorcerer merged 1 commit into
3.15from
patchback/backports/3.15/25561d975a7a9e10eec3b381bc769737b57b3c9b/pr-13246

Conversation

@patchback

@patchback patchback Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

This is a backport of PR #13246 as merged into master (25561d9).

What do these changes do?

llhttp strips the OWS before a header value but leaves the OWS after it, and _process_header() decodes the raw bytes as-is. So the C parser keeps trailing spaces and tabs where the Python parser strips them:

>>> feed(b"GET / HTTP/1.1\r\nHost: a\r\ntest: \t value \t \r\n\r\n").headers["test"]
'value'        # HttpRequestParserPy
'value \t '    # HttpRequestParserC

RFC 9110 section 5.5 says the OWS on both sides is excluded from the field value, and HeadersParser.parse_headers() already does bvalue.strip(b" \t") for both headers and raw_headers (there is a comment there saying raw does not mean the OWS is kept). This does the same in the C parser.

test_http_request_parser_utf8_request_line covers this for the Python parser, but it is xfailed for the C parser over an unrelated URL difference, so the gap was not visible.

Are there changes in behavior for the user?

Header values read through the C parser no longer carry trailing spaces or tabs, so both parsers now return the same thing. raw_headers changes the same way.

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

No, it is three lines in _process_header() and removes a difference between the two parsers.

Related issue number

None.

Checklist

  • I think the code is well written
  • Unit tests for the changes exist
  • Documentation reflects the changes — N/A, no API change
  • If you provide code modification, please add yourself to CONTRIBUTORS.txt
  • Add a new news fragment into the CHANGES/ folder
Test run

Built with the Cython extensions (make generate-llhttp, make cythonize, pip install -e .).

New test, both parsers:

$ PYTHONPATH='.' pytest tests/test_http_parser.py -k whitespace_around_header_value -v
tests/test_http_parser.py::test_whitespace_around_header_value[py-parser] PASSED
tests/test_http_parser.py::test_whitespace_around_header_value[c-parser] PASSED

Same test against master, to check it catches the bug:

E       AssertionError: assert 'value \t ' == 'value'
FAILED tests/test_http_parser.py::test_whitespace_around_header_value[c-parser]
1 failed, 1 passed

Parser suite and full suite:

$ PYTHONPATH='.' pytest tests/test_http_parser.py
837 passed, 6 deselected, 3 xfailed

$ PYTHONPATH='.' pytest --numprocesses=auto
15 failed, 4932 passed, 23 skipped, 17 xfailed

The 15 failures are all tests/test_run_app.py, OSError: [Errno 48] address already in use on port 8080 on this machine. They fail the same way on master (4930 passed there, the +2 here is the new test).

Drafted with Claude Opus 5 (Claude Code); reviewed by @LuShadowX.

@codecov

codecov Bot commented Jul 26, 2026 •

Copy link
Copy Markdown

@codspeed

codspeed Bot commented Jul 26, 2026

Copy link
Copy Markdown

@Dreamsorcerer
Dreamsorcerer merged commit 90174c5 into 3.15 Jul 26, 2026
47 checks passed
@Dreamsorcerer
Dreamsorcerer deleted the patchback/backports/3.15/25561d975a7a9e10eec3b381bc769737b57b3c9b/pr-13246 branch July 26, 2026 21:48
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.

2 participants