Skip to content

feat(py-client): Support response content disposition in object URLs - #696

Merged
jan-auer merged 6 commits into
mainfrom
feat/download-response-options
Oct 22, 2026
Merged

jan-auer merged 6 commits into
mainfrom
feat/download-response-options

Conversation

@jan-auer

@jan-auer jan-auer commented Oct 20, 2026 •

Copy link
Copy Markdown
Member

Changelog Entry

  • Add a response_content_disposition option ("inline" or "attachment") to Session.object_url() and Session.presigned_object_url(). Pre-signed URLs include it in their signature.

Details

Seer needs to display Objectstore files in the browser, which requires inline content disposition. Currently, it proxies payloads through a custom endpoint to achieve this.

To allow for redirects directly to objectstore, this PR adds support for the response-content-disposition query parameter. The option is added to Python’s object_url() and presigned_object_url(). Pre-signed URLs also include the option in their signature so it cannot be changed afterwards.

  • Disposition accepts inline or attachment and defaults to attachment.
  • The option applies only to successful GET/HEAD responses, including ranged downloads. A 404 response does not include those headers, similar to how S3 and GCS handle this.
  • Successful downloads now send X-Content-Type-Options: nosniff and inline responses additionally send Content-Security-Policy: sandbox without relaxation tokens. These are safeguards to prevent browsers from interpreting the content as executable or markup.

Internally, this is implemented by a single extractor that validates the options. A small IntoResponseParts type then adds the headers to the existing endpoint responses.


Note that S3 additionally supports more headers that are not implemented in this PR:

  • Cache control: excluded because shared-cache reuse of authenticated responses needs a separate authorization and caching policy. This exceeds the scope of the current change.
  • Content type: excluded because objects already declare their MIME type.
  • Content encoding: excluded because it must match the stored compression.
  • Content language: no current need.
  • Expires: deferred.

Ref FS-570

via @lcian.

--

View Junior Session [Sentry]

@linear-code

linear-code Bot commented Oct 20, 2026

Copy link
Copy Markdown

FS-570

@codecov

codecov Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.04762% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 91.83%. Comparing base (6f76e34) to head (893bb47).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
clients/python/src/objectstore_client/client.py 90.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #696      +/-   ##
==========================================
+ Coverage   91.40%   91.83%   +0.42%     
==========================================
  Files         118      117       -1     
  Lines       24551    24137     -414     
==========================================
- Hits        22441    22166     -275     
+ Misses       2110     1971     -139     
Components Coverage Δ
Rust Backend 94.89% <100.00%> (+0.01%) ⬆️
Rust Client 82.38% <ø> (+0.55%) ⬆️
Python Client 93.46% <90.00%> (+0.18%) ⬆️

☔ 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.

Comment thread objectstore-server/src/endpoints/objects.rs Outdated
@jan-auer
jan-auer force-pushed the feat/download-response-options branch from 43720b0 to f1337b1 Compare October 21, 2026 09:59
Comment thread objectstore-server/src/endpoints/common.rs Outdated
@jan-auer
jan-auer marked this pull request as ready for review October 21, 2026 13:07
@jan-auer
jan-auer requested a review from a team as a code owner October 21, 2026 13:07
Comment thread clients/python/src/objectstore_client/client.py Outdated

@lcian lcian left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I'm not certain about Content-Security-Policy/X-Content-Type-Options etc. so I trust you on that. Other than that LGTM.

@jan-auer
jan-auer merged commit 9655c44 into main Oct 22, 2026
25 checks passed
@jan-auer
jan-auer deleted the feat/download-response-options branch October 22, 2026 11:34
@sentry-junior sentry-junior Bot changed the title feat: Support download response options feat(py-client): Support response content disposition in object URLs Oct 22, 2026
@github-actions

Copy link
Copy Markdown

Semver Impact of This PR

🟡 Minor (new features)

📋 Changelog Preview

This is how your changes will appear in the changelog.
Entries from this PR are highlighted with a left border (blockquote style).


Breaking Changes 🛠

Py Client

  • (py-client) Split the signing key from the token policy: SecretKey(kid, secret_key) now only holds the EdDSA key, and TokenGenerator(key, expiry_seconds=60, permissions=None) wraps it with the token defaults and is what you pass to Client(token=...). The deprecated sign_for_scope() alias is removed. by sentry-junior in #721
    • Replace SecretKey(kid, secret_key, expiry_seconds, permissions) or TokenGenerator(kid, secret_key, ...) with TokenGenerator(SecretKey(kid, secret_key), expiry_seconds, permissions).
      # Before
      client = Client(url, token=SecretKey(kid, secret_key, expiry_seconds=300))
      
      # After
      key = SecretKey(kid, secret_key)
      client = Client(url, token=TokenGenerator(key, expiry_seconds=300))
    • Replace .sign_for_scope(...) with .token_for_scope(...).
  • via @lcian. by sentry-junior in #721
  • -- by sentry-junior in #721
  • View Junior Session [Sentry] by sentry-junior in #721
  • (py-client) Remove the deprecated compression argument from Session.put() by sentry-junior in #718
    • Replace session.put(..., compression=...) with session.put(..., compress=...).
  • via @lcian. by sentry-junior in #718
  • -- by sentry-junior in #718
  • View Junior Session [Sentry] by sentry-junior in #718
  • (py-client) Remove the deprecated propagate_traces argument from Client by sentry-junior in #714
    • Drop propagate_traces=... from Client(...) calls. Sentry's StdlibIntegration, enabled by default, already propagates traces on Objectstore requests.
  • via @lcian. by sentry-junior in #714
  • -- by sentry-junior in #714
  • View Junior Session [Sentry] by sentry-junior in #714

Rs Client

  • (rs-client) Remove the deprecated Session::mint_token() by sentry-junior in #717
    • Replace session.mint_token() with session.get_token().
  • via @lcian. by sentry-junior in #717
  • -- by sentry-junior in #717
  • View Junior Session [Sentry] by sentry-junior in #717
  • (rs-client) Remove the deprecated TokenGenerator::sign() by sentry-junior in #716
    • Replace generator.sign(&scope) with generator.create_token(&scope).sign().
  • via @lcian. by sentry-junior in #716
  • -- by sentry-junior in #716
  • View Junior Session [Sentry] by sentry-junior in #716
  • (rs-client) Remove the deprecated PutBuilder::compression() by sentry-junior in #715
    • Replace .compression(...) with .compress(...) on put builders.
  • via @lcian. by sentry-junior in #715
  • -- by sentry-junior in #715
  • View Junior Session [Sentry] by sentry-junior in #715
  • Usecase::new now returns a Result, and the panicking constructor is now Usecase::from_static by sentry-junior in #710
    • Replace Usecase::new("name") with Usecase::from_static("name").
    • Replace Usecase::try_new(name) with Usecase::new(name).
  • Mark Error as #[non_exhaustive] so new error variants can be added without a breaking change by lcian in #706
    • Add a wildcard arm (_ => ...) to exhaustive match expressions on objectstore_client::Error.
  • Remove the multipart upload API (Session::initiate_multipart_upload(), Session::resume_multipart_upload()) and the multipart feature flag by lcian in #704
    • Use resumable uploads instead.
    • Remove match arms for the removed Error::InvalidPartNumber, Error::InvalidUploadId and Error::MultipartComplete variants.

New Features ✨

Py Client

  • Add a response_content_disposition option ("inline" or "attachment") to Session.object_url() and Session.presigned_object_url(). Pre-signed URLs include it in their signature. by jan-auer in #696
  • Add a response_content_disposition option ("inline" or "attachment") to Session.object_url() and Session.presigned_object_url(). Pre-signed URLs include it in their signature. by jan-auer in #696

🤖 This preview updates automatically when you update the PR.

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.

2 participants