Repository navigation
Strip trailing OWS from header values in the C parser - #13246
Conversation
Merging this PR will not alter performance
Comparing Footnotes
|
Backport to 3.14: 💚 backport PR created✅ Backport PR branch: Backported as #13247 🤖 @patchback |
Backport to 3.15: 💚 backport PR created✅ Backport PR branch: Backported as #13248 🤖 @patchback |
|
Thanks for that. |
|
Happy to help, and thanks for the quick review! |
|
Thanks again for the backports. If there's anything else you'd like a hand with, feel free to ping me — I still have the differential parser harness set up, so more py-vs-C mismatches are easy for me to check. |
Well, there are known gaps already in the tests, if you wanted to look at those. |

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:RFC 9110 section 5.5 says the OWS on both sides is excluded from the field value, and
HeadersParser.parse_headers()already doesbvalue.strip(b" \t")for bothheadersandraw_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_linecovers 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_headerschanges 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
CONTRIBUTORS.txtCHANGES/folderTest run
Built with the Cython extensions (
make generate-llhttp,make cythonize,pip install -e .).New test, both parsers:
Same test against master, to check it catches the bug:
Parser suite and full suite:
The 15 failures are all
tests/test_run_app.py,OSError: [Errno 48] address already in useon 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.