feat(services/webdav): support rename with if_not_exists - #8089
Open
PDGGK wants to merge 1 commit into
Open
Conversation
Implements the webdav box of apache#7828 (RFC-7818), which the RFC calls a strong candidate: RFC 4918 defines Overwrite for MOVE, and section 10.6 makes "F" mean the server must not perform the move if the destination maps to a resource. webdav_move hardcoded Overwrite: T, so OpRename was ignored -- the backend took it as _args. It now threads the flag through and sends F when if_not_exists is set. Nothing was needed on the error side. RFC 4918 section 9.9.4 has the server answer 412 when the precondition fails, and webdav already maps PRECONDITION_FAILED to ErrorKind::ConditionNotMatch, which is what the behavior test expects. The mkcol on the destination's parent stays. test_rename_with_if_not_ exists_nested renames onto a three-level path that does not exist yet, so the parent creation is what makes it pass. The consequence is that a 412 can leave an empty parent behind; the destination itself is untouched, so it does not violate the RFC. Verified against the nginx fixture from .github/services/webdav rather than by reading: all three if_not_exists behavior tests pass all 10 rename behavior tests pass the whole webdav behavior suite, 124 tests pass test_rename_overwrite is in that set, so the default path still sends T. Hardcoding T back while keeping the capability bit fails exactly one test -- the one asserting ConditionNotMatch -- and leaves the other nine green, which is what tells you the header rather than the bit is doing the work. Only webdav here. hdfs_native can follow, also docker-verifiable. azfile and azdls need 1Password secrets for their CI, so a fork PR gets no behavior coverage on them and I would rather not advertise an atomicity-gated capability on a code reading alone.
This was referenced Aug 15, 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.
Which issue does this PR close?
Part of #7828 — the
webdavbox of RFC-7818. Announced the intended shape there first; this is that.Rationale for this change
The RFC calls webdav a "Strong candidate: RFC 4918 defines
Overwrite: Fand a failed precondition", and §10.6 is explicit —Fmeans the server must not perform the MOVE if the destination maps to a resource.webdav_movehardcoded the header, soOpRenamewas ignored entirely — the backend took it as_args:It now threads the flag through and sends
Fwhenif_not_existsis set.Nothing was needed on the error side. RFC 4918 §9.9.4 has the server answer
412when the precondition fails, and webdav already maps it:which is exactly what
test_rename_with_if_not_exists_returns_condition_not_matchasserts.On the capability, since #7828 asks
Plain
rename_with_if_not_exists: true, no config flag.Overwriteis not an optional extension — RFC 4918 §10.6 makes it part of MOVE and a server that ignores it is non-conforming. That is unlike conditional read, where servers genuinely differ andenable_conditional_readearns its keep. Happy to move it behind a flag if you would rather have consistency with #7637.On the mkcol
webdav_movecreates the destination's parent before issuing the MOVE, and that stays:test_rename_with_if_not_exists_nestedrenames onto a three-level path that does not exist yet, so the parent creation is what makes it pass. The consequence is that a412can leave an empty parent behind. The destination itself is untouched, so it does not violate the RFC — flagging it rather than leaving you to find it.Verification
Against the
nginxfixture from.github/services/webdav, not by reading:if_not_existsbehaviour teststest_rename_overwriteis in that set, so the default path still sendsT.And the control that makes those numbers mean something — hardcoding
Tback while keeping the capability bit:Exactly one test, the one asserting
ConditionNotMatch. The header rather than the bit is doing the work.cargo clippy -p opendal-service-webdav --all-targets(zero warnings) andcargo fmt --all -- --checkare clean.Scope
Only webdav.
hdfs_nativecan follow — also docker-verifiable, no secrets.I would leave
azfileandazdlsalone for now: their CI fixtures load credentials through1Password/load-secrets-action, so a fork PR gets no behaviour coverage on them, and I would rather not advertise an atomicity-gated capability on a code reading alone. Happy to write them if someone who can run that CI wants to take the verification.Are there any user-facing changes?
Yes —
op.rename_with(from, to).if_not_exists(true)now works on webdav, returningErrorKind::ConditionNotMatchwhen the destination exists. Ordinaryrenameis unchanged.