Skip to content

ZCU-DATA/fix: RFC 5987 Content-Disposition for single-file download (backport #1368) - #1370

Merged
milanmajchrak merged 3 commits into
customer/zcu-datafrom
zcu-data/fix-content-disposition-rfc5987
Jul 21, 2026
Merged

ZCU-DATA/fix: RFC 5987 Content-Disposition for single-file download (backport #1368)#1370
milanmajchrak merged 3 commits into
customer/zcu-datafrom
zcu-data/fix-content-disposition-rfc5987

Conversation

@milanmajchrak

@milanmajchrak milanmajchrak commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Backport of #1368 to customer/zcu-data. Fixes the wrong filename when downloading a single file.

allzip was already fixed here by #1267 — this covers the other half, which was never fixed on any branch.

What's fixed

HttpHeadersInitializer encoded the name with MimeUtility.encodeText, i.e. an RFC 2047 encoded-word (=?UTF-8?Q?...?=) — an email header format that RFC 6266 App. C.1 does not allow in HTTP.

Be aware what this does and does not change in practice. Chrome and Firefox decode RFC 2047 anyway, so for them nothing changes — measured, not assumed (see the middle block of the screenshot in #1368). It bites clients that follow the spec: Safari, and curl -OJ, which saved the encoded-word verbatim with the extension buried mid-name. That is why this looked intermittent depending on who reported it.

Cherry-pick of vanilla fe4077acee (#11269 → 7.6.6). The cherry-pick conflicted — this branch's copy predates vanilla's isNullOrEmpty(disposition) guard and has its own METHOD_HEAD early-return. I kept this branch's structure and swapped only the encoding, so the backport stays surgical.

Alignment with vanilla

The private buildContentDisposition() added by #1267 now uses vanilla's createFallbackAsciiName / createEncodedUtf8Name, so allzip and single-file render the same way and both track upstream. No shared fork helper — MetadataBitstreamController is fork-only, so it keeps its own copy, exactly as vanilla's HttpHeadersInitializer does.

The escaping of \ and " that #1267 added is kept and marked as a deliberate deviation: vanilla omits it and still has that bug, so copying verbatim would have regressed #1267.

Behaviour change to note in review

The fallback now transliterates instead of blanking out, so allzip yields "Prilis zlutoucky kun.zip" where #1267 produced "P__li_ _lu_ou_k_ k__.zip". The IT from #1267 is updated to match. Only clients that ignore filename* ever see this value.

Verified

🤖 Generated with Claude Code

(cherry picked from commit fe4077a)
(cherry picked from commit f317911)
(cherry picked from commit 9b63181)
@coderabbitai

coderabbitai Bot commented Jul 16, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a026abf6-0856-4d6f-8a44-13fa595b869c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Backport of the single-file half of #1368 (dtq-dev) to customer/zcu-data.

allzip was already fixed here by #1267, but single-file download was not:
HttpHeadersInitializer encoded the name with MimeUtility.encodeText, i.e.
RFC 2047 encoded-words, which RFC 6266 Appendix C.1 forbids in HTTP. Safari
shows the raw =?UTF-8?Q?...?= string; Chrome and Firefox decode it anyway,
which is why this looked intermittent.

Also aligns the private buildContentDisposition added by #1267 with vanilla's
createFallbackAsciiName / createEncodedUtf8Name, so allzip and single-file
render the same way and both track upstream. The fallback now transliterates
rather than blanking out, so it reads "Prilis zlutoucky kun.zip" instead of
"P__li_ _lu_ou_k_ k__.zip"; the IT from #1267 is updated accordingly. Only
clients that ignore filename* ever see that value.

The escaping of \ and " that #1267 added is kept, and marked in the code as a
deliberate deviation — vanilla omits it and still has that bug.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Backports upstream-aligned RFC 5987 Content-Disposition handling so single-file downloads (and the allzip endpoint) provide a standards-compliant filename* value while retaining an ASCII filename fallback for legacy clients.

Changes:

  • Update HttpHeadersInitializer to generate Content-Disposition: <disposition>; filename="..."; filename*=UTF-8''... (RFC 5987) instead of RFC 2047 encoded-words.
  • Align allzip’s Content-Disposition building with the same fallback/encoding approach (diacritics stripped in fallback; UTF-8 percent-encoded in filename*).
  • Update integration tests to assert the new header format and updated ASCII fallback behavior.

Reviewed changes

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

File Description
dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java Switch single-file Content-Disposition generation to RFC 5987 filename* + ASCII fallback.
dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java Update allzip Content-Disposition builder to match the RFC 5987 approach used elsewhere.
dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java Adjust expected Content-Disposition for diacritics item-name allzip scenario.
dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java Update single-bitstream Content-Disposition assertion to RFC 5987 filename* + ASCII fallback.

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

Restrict createFallbackAsciiName to printable ASCII ([\x20-\x7E]) in both
HttpHeadersInitializer (single-file) and MetadataBitstreamController (allzip),
so control chars — notably CR/LF — can no longer reach the quoted-string
filename= value and inject a header. HttpHeadersInitializer additionally now
escapes \ and ", matching MetadataBitstreamController; previously the two paths
were inconsistent.

MetadataBitstreamController had regressed from [\x20-\x7E] to [\x00-\x7F] while
aligning with vanilla; this restores the printable-only filter while keeping
the NFD transliteration. Existing IT assertions are unaffected (all use
printable names).

Addresses Copilot review comments on #1370.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@milanmajchrak
milanmajchrak merged commit 20f9cbb into customer/zcu-data Jul 21, 2026
9 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.

3 participants