Skip to content

Conversation

@magicmark
Copy link
Contributor

@magicmark magicmark commented Dec 19, 2025

Fixes multipart parser when encountering an empty body.

@magicmark magicmark requested a review from asvetlov as a code owner December 19, 2025 20:14
@magicmark magicmark requested a review from webknjaz as a code owner December 19, 2025 20:15
@magicmark
Copy link
Contributor Author

magicmark commented Dec 19, 2025

FWIW, a real world application of this:

https://strawberry.rocks/docs/general/multipart-subscriptions

Minimal implementation here: https://github.com/magicmark/gql-book-server/

Raw response:

curl -X POST https://gql-book-server.fly.dev/graphql \
     -H "Content-Type: application/json" \
     -H "Accept: multipart/mixed;boundary=graphql;subscriptionSpec=1.0,application/json" \
     -d '{"query":"subscription GetBook { book(target: 2) { title author } }"}'

Content-Type: application/json; charset=utf-8
Content-Length: 2

{}
--graphql
Content-Type: application/json; charset=utf-8
Content-Length: 95

{"payload": {"data": {"book": {"title": "A Tale of Two Cities", "author": "Charles Dickens"}}}}
--graphql
Content-Type: application/json; charset=utf-8
Content-Length: 84

{"payload": {"data": {"book": {"title": "Animal Farm", "author": "George Orwell"}}}}
--graphql
--graphql--

It's also possible the fix needs to be made in Strawberry? cc @patrick91 @erikwrede

@codecov
Copy link

codecov bot commented Dec 19, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.74%. Comparing base (8b919d3) to head (ac1cba1).
⚠️ Report is 12 commits behind head on master.
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #11857      +/-   ##
==========================================
- Coverage   98.74%   98.74%   -0.01%     
==========================================
  Files         127      127              
  Lines       44171    44187      +16     
  Branches     2342     2343       +1     
==========================================
+ Hits        43618    43631      +13     
- Misses        392      395       +3     
  Partials      161      161              
Flag Coverage Δ
CI-GHA 98.60% <100.00%> (-0.01%) ⬇️
OS-Linux 98.34% <100.00%> (-0.01%) ⬇️
OS-Windows 96.68% <100.00%> (+<0.01%) ⬆️
OS-macOS 97.56% <100.00%> (+<0.01%) ⬆️
Py-3.10.11 97.10% <100.00%> (-0.02%) ⬇️
Py-3.10.19 97.60% <100.00%> (-0.01%) ⬇️
Py-3.11.14 97.80% <100.00%> (-0.01%) ⬇️
Py-3.11.9 97.31% <100.00%> (+<0.01%) ⬆️
Py-3.12.10 97.41% <100.00%> (+<0.01%) ⬆️
Py-3.12.12 97.91% <100.00%> (+<0.01%) ⬆️
Py-3.13.11 98.16% <100.00%> (-0.01%) ⬇️
Py-3.14.2 98.17% <100.00%> (-0.01%) ⬇️
Py-3.14.2t 97.25% <100.00%> (+<0.01%) ⬆️
Py-pypy3.11.13-7.3.20 97.41% <100.00%> (-0.01%) ⬇️
VM-macos 97.56% <100.00%> (+<0.01%) ⬆️
VM-ubuntu 98.34% <100.00%> (-0.01%) ⬇️
VM-windows 96.68% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@codspeed-hq
Copy link

codspeed-hq bot commented Dec 19, 2025

CodSpeed Performance Report

Merging #11857 will not alter performance

Comparing magicmark:add_failing_test_for_empty_part (ac1cba1) with master (8b919d3)

Summary

✅ 59 untouched

@magicmark magicmark force-pushed the add_failing_test_for_empty_part branch from 764cb47 to 0e70964 Compare December 19, 2025 22:57
@Dreamsorcerer
Copy link
Member

Dreamsorcerer commented Dec 20, 2025

After its boundary delimiter line, each body part then consists of a header area, a blank line, and a body area.
...
A body part that starts with a blank line, therefore, is allowed and is a body part for which all default values are to be assumed.
https://datatracker.ietf.org/doc/html/rfc2046#section-5.1

It is then terminated by either another CRLF and the header fields for the next part, or by two CRLFs
...

     encapsulation := delimiter transport-padding
                     CRLF body-part

    delimiter := CRLF dash-boundary

    close-delimiter := delimiter "--"

https://datatracker.ietf.org/doc/html/rfc2046#section-5.1.1

So, an empty body part looks like:

\r
--graphql\r
\r
--graphql--

So, the example provided appears to be invalid syntax to me (not to mention that it seems illogical to be wasting bandwidth sending nothing). If we allowed this, there's a possibility it could open up an HTTP request smuggling vulnerability.

@magicmark
Copy link
Contributor Author

Thanks @Dreamsorcerer.

Will make the fix in Strawberry then.

@magicmark magicmark closed this Dec 20, 2025
@Dreamsorcerer
Copy link
Member

Tests are still xfailing with correct syntax, so there may be a fix needed in both.

@Dreamsorcerer Dreamsorcerer reopened this Dec 20, 2025
@Dreamsorcerer
Copy link
Member

OK, I have a fix. Going to make some tweaks to the this test and then will followup with the actual fix.

@Dreamsorcerer Dreamsorcerer added the bot:chronographer:skip This PR does not need to include a change note label Dec 20, 2025
@Dreamsorcerer
Copy link
Member

Causes too many warnings. Will have to add the fix here.

@Dreamsorcerer Dreamsorcerer changed the title Add failing test for empty multipart response part Fix multipart parsing for empty body parts Dec 20, 2025
@Dreamsorcerer Dreamsorcerer removed the bot:chronographer:skip This PR does not need to include a change note label Dec 20, 2025
@psf-chronographer psf-chronographer bot added the bot:chronographer:provided There is a change note present in this PR label Dec 20, 2025
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a bug in multipart parsing when encountering empty body parts. Despite the description stating it only adds failing tests marked with @pytest.mark.xfail, the PR actually includes both test cases and implementation fixes.

Key Changes:

  • Adds two test cases for empty body parts in multipart parsing
  • Fixes CRLF handling in _read_chunk_from_stream by prepending and later stripping the CRLF consumed during header parsing
  • Corrects the boundary truncation logic by removing an incorrect conditional check
  • Changes header line processing from strip() to rstrip(b"\r\n") for more precise whitespace handling

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
tests/test_multipart.py Adds two test cases to verify correct handling of empty body parts with and without headers
aiohttp/multipart.py Fixes multipart parsing logic to correctly handle empty body parts by adjusting CRLF handling and boundary detection
CONTRIBUTORS.txt Adds Mark Larah to the contributors list
CHANGES/11857.bugfix.rst Documents the bugfix with credit to Dreamsorcerer

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Member

@bdraco bdraco left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good once PR description is updated

@Dreamsorcerer Dreamsorcerer merged commit 0a915b8 into aio-libs:master Jan 2, 2026
47 checks passed
@patchback
Copy link
Contributor

patchback bot commented Jan 2, 2026

Backport to 3.13: 💚 backport PR created

✅ Backport PR branch: patchback/backports/3.13/0a915b8fb0fc02904b59ede9af8328b9ad305a1b/pr-11857

Backported as #11879

🤖 @patchback
I'm built with octomachinery and
my source is open — https://github.com/sanitizers/patchback-github-app.

patchback bot pushed a commit that referenced this pull request Jan 2, 2026
Fixes multipart parser when encountering an empty body.

---------

Co-authored-by: Sam Bull <git@sambull.org>
(cherry picked from commit 0a915b8)
@patchback
Copy link
Contributor

patchback bot commented Jan 2, 2026

Backport to 3.14: 💚 backport PR created

✅ Backport PR branch: patchback/backports/3.14/0a915b8fb0fc02904b59ede9af8328b9ad305a1b/pr-11857

Backported as #11880

🤖 @patchback
I'm built with octomachinery and
my source is open — https://github.com/sanitizers/patchback-github-app.

patchback bot pushed a commit that referenced this pull request Jan 2, 2026
Fixes multipart parser when encountering an empty body.

---------

Co-authored-by: Sam Bull <git@sambull.org>
(cherry picked from commit 0a915b8)
Dreamsorcerer added a commit that referenced this pull request Jan 2, 2026
…ody parts (#11880)

**This is a backport of PR #11857 as merged into master
(0a915b8).**

Co-authored-by: Mark Larah <mark@larah.me>
Co-authored-by: Sam Bull <git@sambull.org>
Dreamsorcerer added a commit that referenced this pull request Jan 2, 2026
…ody parts (#11879)

**This is a backport of PR #11857 as merged into master
(0a915b8).**

Co-authored-by: Mark Larah <mark@larah.me>
Co-authored-by: Sam Bull <git@sambull.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backport-3.13 Trigger automatic backporting to the 3.13 release branch by Patchback robot backport-3.14 Trigger automatic backporting to the 3.14 release branch by Patchback robot bot:chronographer:provided There is a change note present in this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants