WebDAV: honour If-Match / If-None-Match on PUT, DELETE and MOVE, and return an ETag after PUT and tus finalize #492
Labels
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: Cordy/Cairn#492
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Status: verified at runtime against cairnd built from
bd006ef; unchanged onmain(e1c855d).What happens
PUTwithIf-Match: "bogus"on an existing file → 204, and the file is overwritten. cairn-desktop's integration suite (interop run #32 one4aead3) records the same forDELETEandMOVEwithIf-Match, and forPUTwithIf-None-Match: *onto an existing file.Overwrite: Fon MOVE is honoured (412) — that part works.PUT(201/204) returns noETagorLast-Modified, and tus finalize returns no ETag either, so a client must PROPFIND afterwards.Why it matters
The desktop client (and the planned mobile clients) need this to avoid lost updates when two devices edit the same file: the client sends the ETag it last saw, and the server refuses the write if someone else changed the file in between. Without it the client can only re-check just before writing, which narrows the window but cannot close it. And because PUT returns no ETag, a same-size write by another client landing between our PUT and our PROPFIND is recorded under our content — the two copies then diverge silently.
Suggested fix
If-Match/If-None-Matchagainst the current ETag before PUT, DELETE and MOVE (source), answering412 Precondition Failedon mismatch (RFC 7232 §3, RFC 4918 §10.4 / §12).ETag(andLast-Modified) on PUT's 201/204 responses.ETagheader on that 204).Found while building the cairn-desktop WebDAV client (phase 2, Cordy/cairn-desktop#23/#29). Filed by Claude on behalf of @Cordy.
Shipped in v0.6.168, live on both dogfoods.
What landed:
If-Match/If-None-Match(incl.*): checked against the current stored ETag before any bytes are written; mismatch → 412. Successful PUT now returnsETagandLast-Modifiedfrom a post-write Stat.ETag(finalize now Stats the assembled file), so resumable uploads end with the same handle a plain PUT gives.W/prefixes and*per RFC 9110.TDD: lost-update round-trip tests (
cond_test.go,etag_test.go) witnessed red on the runner first. Clients can now do read-modify-write without a race window.