Skip to content

Sort RFC 2231 filename continuations numerically - #13500

Open
2sumtech wants to merge 4 commits into
aio-libs:masterfrom
2sumtech:fix/rfc2231-continuation-order
Open

Sort RFC 2231 filename continuations numerically#13500
2sumtech wants to merge 4 commits into
aio-libs:masterfrom
2sumtech:fix/rfc2231-continuation-order

Conversation

@2sumtech

@2sumtech 2sumtech commented Aug 19, 2026

Copy link
Copy Markdown

What do these changes do?

Fix content_disposition_filename() assembling RFC 2231 continuation sections in lexicographic order, where filename*10 sorted before filename*2, so filenames split into 10 or more sections were silently truncated after two sections. Sections are now sorted by their integer index (RFC 2231 Section 3), and each section is decoded according to its own extended (*N*, percent-encoded) or quoted (*N, literal) marker (RFC 2231 Section 4.1), using the charset declared by the initial extended section. This also stops apostrophes inside quoted sections from being misread as an RFC 5987 charset'language' prefix, which previously raised an uncaught ValueError with one apostrophe or dropped most of the filename with two.

Are there changes in behavior for the user?

Only for inputs that were previously mishandled. Names with 10 or more sections are now complete. Apostrophes in quoted sections are literal. Extended sections are always percent-decoded even when the initial section carries no charset'' prefix. Missing-section, leading-zero, and starts-at-1 handling is unchanged and covered by the existing greenbytes-derived tests.

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

No. The change is contained in one helper, follows the RFC text section by section, and the greenbytes-derived test corpus already pins the surrounding behavior.

Related issue number

Fixes #13499

Checklist

  • I think the code is well written
  • Unit tests for the changes exist
  • Documentation reflects the changes (no docs change needed, parser-internal behavior)
  • If you provide code modification, please add yourself to CONTRIBUTORS.txt
  • Add a new news fragment into the CHANGES/ folder (13499.bugfix.rst)
Test runs

Five new tests in tests/test_multipart_helpers.py::TestContentDispositionFilename covering 11-section reassembly plain and with charset, quoted/extended mix, apostrophes, and a single-section guard. All fail or crash with the ValueError on master and pass with the fix. Full test_multipart_helpers.py + test_multipart.py: 253 passed. flake8 and mypy clean on touched files.

Drafted with Claude Code (Fable 5); opened as draft pending human review per AGENTS.md.

content_disposition_filename() assembled filename*N continuation
sections with a lexicographic sort, so filename*10 sorted before
filename*2 and any filename split into 10 or more sections was
silently truncated after the first two. RFC 2231 Section 3 requires
numeric ordering of continuation sections.

Sort the sections by their integer index and decode each section
according to its own extended (*N*, percent-encoded) or quoted (*N,
literal) marker per RFC 2231 Section 4.1, using the charset declared
by the initial extended section. This also stops apostrophes inside
quoted sections from being misread as an RFC 5987 charset prefix,
which previously raised ValueError or dropped parts of the name.
@psf-chronographer psf-chronographer Bot added the bot:chronographer:provided There is a change note present in this PR label Aug 19, 2026
@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

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

Additional details and impacted files
@@           Coverage Diff           @@
##           master   #13500   +/-   ##
=======================================
  Coverage   99.00%   99.00%           
=======================================
  Files         132      132           
  Lines       49635    49701   +66     
  Branches     2575     2583    +8     
=======================================
+ Hits        49141    49207   +66     
  Misses        370      370           
  Partials      124      124           
Flag Coverage Δ
Autobahn 22.03% <16.04%> (-0.01%) ⬇️
CI-GHA 98.91% <100.00%> (+<0.01%) ⬆️
OS-Linux 98.68% <100.00%> (-0.01%) ⬇️
OS-Windows 97.01% <100.00%> (+<0.01%) ⬆️
OS-macOS 97.93% <100.00%> (+<0.01%) ⬆️
Py-3.10 98.13% <100.00%> (+<0.01%) ⬆️
Py-3.11 98.37% <100.00%> (+<0.01%) ⬆️
Py-3.12 98.46% <100.00%> (+<0.01%) ⬆️
Py-3.13 98.44% <100.00%> (-0.01%) ⬇️
Py-3.14 98.47% <100.00%> (+<0.01%) ⬆️
Py-3.14t 97.55% <100.00%> (-0.01%) ⬇️
Py-pypy-3.11 97.42% <100.00%> (+0.01%) ⬆️
VM-macos 97.93% <100.00%> (+<0.01%) ⬆️
VM-ubuntu 98.68% <100.00%> (-0.01%) ⬇️
VM-windows 97.01% <100.00%> (+<0.01%) ⬆️
cython-coverage 82.19% <20.51%> (-0.06%) ⬇️

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

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

@2sumtech
2sumtech marked this pull request as ready for review August 19, 2026 16:34
@greptile-apps

greptile-apps Bot commented Aug 19, 2026

Copy link
Copy Markdown

Confidence Score: 5/5

The PR appears safe to merge.

The previously reported split-multibyte decoding defect and changelog bookkeeping violation are fixed, and no blocking failure remains.

Reviews (5): Last reviewed commit: "Cover a multibyte character split across..." | Re-trigger Greptile

Comment thread aiohttp/multipart.py Outdated
Comment thread CHANGES/13499.bugfix.rst Outdated
Comment on lines +1 to +4
Fixed :func:`~aiohttp.content_disposition_filename` truncating filenames split
into 10 or more RFC 2231 continuation sections by sorting the sections
numerically instead of lexicographically, and decoding each section according
to its own extended/quoted marker -- by :user:`2sumtech`.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Add the PR-numbered changelog symlink

This adds only the issue-numbered fragment, while the repository convention requires a CHANGES/13500.bugfix.rst symlink to 13499.bugfix.rst when the issue and PR numbers differ. Without it, the change does not follow the project's changelog bookkeeping convention.

Context Used: AGENTS.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@codspeed-hq

codspeed-hq Bot commented Aug 19, 2026

Copy link
Copy Markdown

Merging this PR will not alter performance

✅ 84 untouched benchmarks
⏩ 83 skipped benchmarks1


Comparing 2sumtech:fix/rfc2231-continuation-order (dcfbad1) with master (8c8906a)

Open in CodSpeed

Footnotes

  1. 83 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.

Percent-decoding each extended section on its own dropped the whole
filename when a multibyte character straddled a section boundary.
Join the octets of adjacent extended sections before decoding, and
reference the changelog fragment by PR number via a symlink.

The changelog fragment no longer uses a :func: role, because
content_disposition_filename is not in the API docs and the dangling
reference failed the docs build.
Adds tests for octets that stay undecodable once the sections are
joined, an unknown charset, and a non-numeric section index, so the
new branches are exercised.
A three-octet character can land in three different extended sections;
exercise that the whole run is still joined before decoding.
@Polandia94

Copy link
Copy Markdown
Contributor

Please follow the PR template: https://github.com/aio-libs/aiohttp/blob/master/.github/PULL_REQUEST_TEMPLATE.md

This PR description mention that is pending human review. After a human review for the operator (not aiohttp mantainers), this could be ready for review. If this was reviewed by a human, a human should change that part of the PR description

@2sumtech

Copy link
Copy Markdown
Author

fair point, restructured the description to the full template and moved it back to draft. it stays there until the operator review is done and recorded, then it goes ready

@2sumtech
2sumtech marked this pull request as draft August 27, 2026 15:59
@2sumtech

Copy link
Copy Markdown
Author

Drafted with Claude Code (Fable 5); reviewed by @2sumtech

@2sumtech
2sumtech marked this pull request as ready for review August 27, 2026 16:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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.

content_disposition_filename() truncates filenames split into 10+ RFC 2231 continuation sections (lexicographic sort)

2 participants