Skip to content

feat(types): Support Unicode in object metadata headers - #581

Open
jan-auer wants to merge 1 commit into
mainfrom
feat/unicode-metadata-headers
Open

feat(types): Support Unicode in object metadata headers#581
jan-auer wants to merge 1 commit into
mainfrom
feat/unicode-metadata-headers

Conversation

@jan-auer

@jan-auer jan-auer commented Jul 31, 2026

Copy link
Copy Markdown
Member

Object metadata can now carry arbitrary Unicode. Previously a non-ASCII value failed the whole request with 400 Bad Request and the message "error dealing with http headers". Headers only accept visible ASCII, so unicode was unstorable by any route.

Metadata values are now percent-encoded at the transport layer. Metadata itself is unchanged: it holds logical Strings throughout, and escaping exists only where a value is written into a header. Additionally, non-ASCII filenames additionally get an RFC 8187 filename* parameter on Content-Disposition, with the existing quoted-string kept as the ASCII fallback.

Wire compatibility

Valid ASCII bytes pass through untouched, so values that are already plain ASCII are byte-identical to what they were before and existing clients see no change. The one visible difference is a value containing a literal %, which is now escaped.

The batch operation key header, which had its own hand-rolled NON_ALPHANUMERIC encoding in three places across two crates, now shares this set.

Backends

Both header-based backends (GCS and S3) now store the escaped form.

GCS escapes on both of its write paths, including the JSON one that could carry Unicode verbatim. It follows Google's own advice: "you should generally avoid non-ascii characters, because they are not permitted in HTTP headers, which the XML API uses." The UTF-8-preserving behavior is undocumented, and raw UTF-8 additionally loses leading whitespace to header trimming and silently rewrites invalid UTF-8.

Not covered

Custom metadata keys are still ASCII-only and still lowercased by the header map, so MyKey round-trips as mykey as it always has. Escaping keys would work but changes the wire form of every existing key without fixing the case-folding, so it is left alone.

Fixes FS-467

@jan-auer
jan-auer requested a review from a team as a code owner July 31, 2026 20:27
@linear-code

linear-code Bot commented Jul 31, 2026

Copy link
Copy Markdown

FS-467

@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.07317% with 6 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.77%. Comparing base (4f64e0d) to head (0df7ad2).

Files with missing lines Patch % Lines
objectstore-service/src/backend/gcs.rs 94.91% 3 Missing ⚠️
objectstore-server/src/extractors/batch.rs 66.66% 2 Missing ⚠️
objectstore-types/src/headers.rs 98.52% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #581      +/-   ##
==========================================
+ Coverage   87.62%   87.77%   +0.14%     
==========================================
  Files          94       95       +1     
  Lines       15591    15718     +127     
==========================================
+ Hits        13662    13796     +134     
+ Misses       1929     1922       -7     
Components Coverage Δ
Rust Backend 92.37% <96.79%> (+0.11%) ⬆️
Rust Client 80.01% <100.00%> (+0.11%) ⬆️
Python Client 91.35% <100.00%> (+0.36%) ⬆️

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

1 participant