Skip to content

Support S3 CopyObject (server-side copy via x-amz-copy-source) #119

Description

@alukach

What's needed

Support S3 CopyObject (server-side copy via x-amz-copy-source) so clients can copy objects within the store without downloading and re-uploading.

Motivation

Reported by a Source Coop contributor trying to write an Icechunk Zarr store natively through the proxy. Every Icechunk commit server-side-copies the mutable repo pointer file (to archive the previous ref state) before atomically replacing it. Today the proxy hard-rejects this:

$ aws s3api copy-object --bucket <account> --key <product>/dest \
    --copy-source <account>/<product>/src --endpoint-url https://data.source.coop
An error occurred (NotImplemented) when calling the CopyObject operation:
not implemented: server-side copy (x-amz-copy-source) is not supported

Without it, contributors must build the store locally and sync files up, which loses Icechunk's transactional commit path. dynamical.org reportedly uses Icechunk-native writes for their weather datasets on AWS Open Data.

Current behavior

parse_s3_request() rejects any PUT carrying x-amz-copy-source up front:

  • crates/core/src/api/request.rs:24-28 — returns ProxyError::NotImplemented for both CopyObject and UploadPartCopy (both arrive as PUT + x-amz-copy-source).
  • Error → S3 code NotImplemented / HTTP 501: crates/core/src/error.rs:38,104,127.
  • Existing test asserting the rejection: copy_source_put_is_rejected_not_implemented at crates/core/src/api/request.rs:229.

Why it isn't a trivial header pass-through

The normal PUT write path forwards to the backend via a presigned URL and can only carry a whitelisted set of signed headers (crates/core/src/proxy.rs:802-833, whitelist at :818-827). x-amz-copy-source is not in that set, and an unsigned header can't ride a presigned URL — it must be part of the signature. So CopyObject needs the copy-source header included in the presign/signing for the destination, and the copy-source path (<src-account>/<src-product>/<key>) must map through the proxy's path-mapping/auth the same way a normal key does.

Scope / proposed approach

  • Add a CopyObject (and UploadPartCopy) variant to S3Operation (crates/core/src/types.rs:346) with the action()/bucket()/key() plumbing.
  • Parse the copy-source header in build_s3_operation() instead of blanket-rejecting; validate + rewrite the source path through path-mapping/authz (a caller must be allowed to read the source and write the destination).
  • Dispatch: presign/sign the destination PUT with x-amz-copy-source (+ optional x-amz-copy-source-if-match etc.) so the backend performs the copy.
  • Update the request.rs:229 test to assert success (or scoped rejection), and add copy round-trip coverage.
  • Update docs/ for the newly supported operation.

Pairs with conditional-write support (#120) — Icechunk needs both for native transactional commits.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions