Skip to content

ZCU-PUB/fix: RFC 5987 Content-Disposition for single-file + allzip download (backport #1368) - #1369

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

ZCU-PUB/fix: RFC 5987 Content-Disposition for single-file + allzip download (backport #1368)#1369
milanmajchrak merged 3 commits into
customer/zcu-pubfrom
zcu-pub/fix-content-disposition-rfc5987

Conversation

@milanmajchrak

@milanmajchrak milanmajchrak commented Jul 16, 2026

Copy link
Copy Markdown
Collaborator

Backport of #1368 to customer/zcu-pub. Fixes the wrong filename when downloading a file whose name has diacritics — both causes.

Customer branches never merge dtq-dev (they sit ~258 commits behind and take individual backports), so this cannot ride along on #1368.

Evidence

The before/after A/B run is in #1368 — two real backend images against the same database. It was run on the dtq-dev pair, not on this branch, so treat it as evidence for the shared fix rather than for this backport specifically.

before and after

What's fixed

  1. allzipMetadataBitstreamController built attachment;filename="<raw name>" with no encoding. Raw UTF-8 in a header makes Tomcat drop the header entirely, so the browser gets no filename at all and falls back to the URL segment (allzip.zip). A " in an item name closed the quoted-string early (ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION).
  2. Single fileHttpHeadersInitializer used MimeUtility.encodeText, i.e. an RFC 2047 encoded-word, which RFC 6266 App. C.1 forbids in HTTP. Cherry-pick of vanilla fe4077acee (#11269 → 7.6.6). Note Chrome and Firefox decode RFC 2047 anyway, so this half is invisible to them — it bites Safari and curl -OJ.

As on #1368, MetadataBitstreamController carries its own private copy of vanilla's createFallbackAsciiName / createEncodedUtf8Name rather than a shared fork helper: it is a fork-only class with no upstream counterpart, so copying keeps it tracking vanilla and adds no API to maintain.

Differences from #1368 — please check these in review

  • BitstreamByHandleRestController does not exist on this branch (the curl endpoint from UFAL/Fix: add bitstream download-by-handle endpoint for curl instructions #1252 was never backported here), so it is not touched.
  • The HttpHeadersInitializer cherry-pick conflicted and was hand-resolved. 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. Unlike on dtq-dev, this file is not byte-identical to vanilla — it already wasn't.

One deviation from vanilla

The ASCII fallback escapes \ and "; vanilla omits this, so a name containing a quote breaks the header even on a clean 7.6.6. Copying vanilla verbatim would have regressed #1267. Marked in a comment at the call site.

Verified

  • mvn test-compile on dspace-api + dspace-server-webapp — pass
  • checkstyle is not wired up on this 7.6.1 branch, so it is a no-op here; the same code passes it on dtq-dev
  • ITs not run locally (need Postgres + Solr) — left to CI
  • the A/B run was done on the dtq-dev pair, not on this branch

🤖 Generated with Claude Code

(cherry picked from commit fe4077a)
(cherry picked from commit f317911)
@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: 34680c3c-7aba-4a75-b59b-75c148ec1327

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.

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 RFC 5987-compliant Content-Disposition handling to prevent mangled diacritics and malformed headers in both single-bitstream downloads and “allzip” downloads.

Changes:

  • Updated single-file download responses to emit both filename (ASCII fallback) and filename* (RFC 5987 encoded UTF-8) instead of RFC 2047 encoded-words.
  • Introduced ContentDispositionUtils and applied it to MetadataBitstreamController to safely build allzip Content-Disposition.
  • Added/updated integration + unit tests covering non-ASCII, spaces, and quoting behavior.

Reviewed changes

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

Show a summary per file
File Description
dspace-server-webapp/src/main/java/org/dspace/app/rest/utils/HttpHeadersInitializer.java Switches single-file Content-Disposition to RFC 5987-style filename* + ASCII fallback.
dspace-server-webapp/src/main/java/org/dspace/app/rest/MetadataBitstreamController.java Uses shared utility to generate safe allzip Content-Disposition.
dspace-api/src/main/java/org/dspace/util/ContentDispositionUtils.java New utility for building safe RFC 5987 Content-Disposition values.
dspace-api/src/test/java/org/dspace/util/ContentDispositionUtilsTest.java Adds unit tests for escaping, encoding, and control-char handling.
dspace-server-webapp/src/test/java/org/dspace/app/rest/BitstreamRestControllerIT.java Updates expectations for new header format on single-file download endpoint.
dspace-server-webapp/src/test/java/org/dspace/app/rest/MetadataBitstreamControllerIT.java Adds IT coverage for quotes + diacritics in allzip filenames.

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

Backport of the allzip half of #1368 (dtq-dev) to customer/zcu-pub.

The allzip endpoint built its header as a bare `attachment;filename="<name>"`,
so item names with diacritics arrived mangled and a double quote in a name
closed the quoted-string early
(ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION).

MetadataBitstreamController has no counterpart upstream, so it carries its own
private copy of vanilla's createFallbackAsciiName / createEncodedUtf8Name
rather than a shared fork utility — same as on dtq-dev.

One deliberate deviation from vanilla, marked in the code: the ASCII fallback
escapes \ and ". Vanilla omits this, so a name containing a quote closes the
quoted-string early — exactly the bug #1267 was raised for.

Differs from #1368 in one way: BitstreamByHandleRestController does not exist
on this branch (the curl endpoint from #1252 was never backported here), so it
is not touched.

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

Address Copilot review on #1369:
- createFallbackAsciiName now escapes \ and " so a filename containing a
  quote can't close the filename="..." quoted-string early (the ERR_RESPONSE_
  HEADERS_MULTIPLE_CONTENT_DISPOSITION class of bug, #1267). Brings it to
  parity with the sibling method in MetadataBitstreamController.
- createEncodedUtf8Name now uses URLEncoder.encode(String, Charset); UTF-8 is
  always supported, so the UnsupportedEncodingException catch was dead code.

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