feat(upload): Support If-Match precondition on chunked assembly - #234
Merged
Conversation
Add an optional `ifMatch` parameter to `uploadChunkAsync`. When set, the ETag precondition is applied ONLY to the final assembly MOVE that materializes the destination file — never to the chunk PUTs, which target brand-new chunk resources and would spuriously fail with 412. It is also cleared before the post-assembly PROPFIND readback, whose target carries a fresh ETag after a successful MOVE. This lets clients perform optimistic-concurrency conflict detection for chunked (large-file) uploads: if the destination changed since the base version the client edited, the server rejects the assembly with 412 Precondition Failed instead of silently overwriting the newer copy. Single-request PUT uploads can already carry `If-Match` via `NKRequestOptions.customHeader`; this closes the gap for the chunked path, where the shared header bag would otherwise leak the precondition onto the chunk PUTs. Signed-off-by: Iva Horn <iva.horn@nextcloud.com> Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
marinofaggiana
approved these changes
Jul 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is a fix to improve upload data integrity and conflict detection for chunked transmissions. This is based on actual customer support tickets and reports in conjunction with Adobe Creative Cloud apps.
Summary
Adds an optional
ifMatchparameter touploadChunkAsyncso clients can perform optimistic-concurrency conflict detection on chunked (large-file) uploads.When set, the ETag precondition is applied only to the final assembly
MOVEthat materializes the destination file. If the destination changed since the base version the client edited, the server rejects the assembly with 412 Precondition Failed instead of silently overwriting the newer copy.Why
Single-request
PUTuploads can already carryIf-MatchviaNKRequestOptions.customHeader. The chunked path could not:uploadChunkAsyncreuses onecustomHeaderbag for both the per-chunkPUTs and the assemblyMOVE, so injectingIf-Matchthere would ride on the chunkPUTs too — and those target brand-new chunk resources, so the precondition would spuriously fail with 412. This PR closes that gap.The macOS desktop client uses this for save-conflict protection (Adobe Creative Suite rapid re-saves, multi-device edits); the single-PUT half is at nextcloud/desktop#10393. The chunked path will be wired there once this lands and is released.
What changed
ifMatch: String? = nilparameter onuploadChunkAsync.options.customHeader["If-Match"]only immediately before the assemblyMOVE, never during the chunkPUTloop.MOVE, so it does not leak onto the post-assembly PROPFIND readback (whose target carries a fresh ETag after a successful MOVE).Default
nil→ existing callers are unaffected; behavior is identical unless a caller opts in.Testing
The core
NextcloudKittarget builds clean. The chunked path is WebDAV (PROPFIND/MKCOL/MOVE), which the repo'sMocker-based unit tests can't represent (standard HTTP methods only), so it is exercised via live-server integration testing — consistent with how the rest of the WebDAV surface is covered here. End-to-end conflict behavior will also be validated through the desktop client once its chunked path consumes this parameter.