From ac45f57775faca9b5c7c85eb6328c45c7eb9ef78 Mon Sep 17 00:00:00 2001 From: Yuang Gao Date: Thu, 28 May 2026 22:26:55 -0700 Subject: [PATCH 1/2] feat(services/webdav): support conditional read headers --- core/services/webdav/src/backend.rs | 4 ++++ core/services/webdav/src/core.rs | 24 +++++++++++++++++++++++- core/services/webdav/src/error.rs | 3 +++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/core/services/webdav/src/backend.rs b/core/services/webdav/src/backend.rs index 2eb9b41fa04f..e9647e3221d2 100644 --- a/core/services/webdav/src/backend.rs +++ b/core/services/webdav/src/backend.rs @@ -202,6 +202,10 @@ impl Builder for WebdavBuilder { stat: true, read: true, + read_with_if_match: true, + read_with_if_none_match: true, + read_with_if_modified_since: true, + read_with_if_unmodified_since: true, write: true, write_can_empty: true, diff --git a/core/services/webdav/src/core.rs b/core/services/webdav/src/core.rs index c3d66209ddbd..cbcdeac1d34c 100644 --- a/core/services/webdav/src/core.rs +++ b/core/services/webdav/src/core.rs @@ -166,13 +166,35 @@ impl WebdavCore { &self, path: &str, range: BytesRange, - _: &OpRead, + args: &OpRead, ) -> Result> { let path = build_rooted_abs_path(&self.root, path); let url: String = format!("{}{}", self.endpoint, percent_encode_path(&path)); let mut req = Request::get(&url); + if let Some(if_match) = args.if_match() { + req = req.header(header::IF_MATCH, if_match); + } + + if let Some(if_none_match) = args.if_none_match() { + req = req.header(header::IF_NONE_MATCH, if_none_match); + } + + if let Some(if_modified_since) = args.if_modified_since() { + req = req.header( + header::IF_MODIFIED_SINCE, + if_modified_since.format_http_date(), + ); + } + + if let Some(if_unmodified_since) = args.if_unmodified_since() { + req = req.header( + header::IF_UNMODIFIED_SINCE, + if_unmodified_since.format_http_date(), + ); + } + if let Some(auth) = &self.authorization { req = req.header(header::AUTHORIZATION, auth.clone()) } diff --git a/core/services/webdav/src/error.rs b/core/services/webdav/src/error.rs index da08a0a2b2c3..50d7c827e365 100644 --- a/core/services/webdav/src/error.rs +++ b/core/services/webdav/src/error.rs @@ -30,6 +30,9 @@ pub(super) fn parse_error(resp: Response) -> Error { StatusCode::NOT_FOUND => (ErrorKind::NotFound, false), // Some services (like owncloud) return 403 while file locked. StatusCode::FORBIDDEN => (ErrorKind::PermissionDenied, true), + StatusCode::PRECONDITION_FAILED | StatusCode::NOT_MODIFIED => { + (ErrorKind::ConditionNotMatch, false) + } // Allowing retry for resource locked. StatusCode::LOCKED => (ErrorKind::Unexpected, true), StatusCode::INTERNAL_SERVER_ERROR From 37e27c97fb388a1a4137f9b4df6007df736ae25c Mon Sep 17 00:00:00 2001 From: Yuang Gao Date: Mon, 1 Jun 2026 21:55:24 -0700 Subject: [PATCH 2/2] ci(webdav): disable etag and if-modified-since read tests for nginx nginx-dav does not return getetag in PROPFIND (so if_match/if_none_match cannot resolve an etag) and does not honor If-Modified-Since on GET. owncloud/nextcloud support the full set and stay enabled. --- .github/services/webdav/0_nginx/action.yml | 2 +- .github/services/webdav/nginx_with_empty_password/action.yml | 2 +- .github/services/webdav/nginx_with_password/action.yml | 2 +- .github/services/webdav/nginx_with_redirect/action.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/services/webdav/0_nginx/action.yml b/.github/services/webdav/0_nginx/action.yml index c90a710cfa07..837739b71f40 100644 --- a/.github/services/webdav/0_nginx/action.yml +++ b/.github/services/webdav/0_nginx/action.yml @@ -30,5 +30,5 @@ runs: run: | cat << EOF >> $GITHUB_ENV OPENDAL_WEBDAV_ENDPOINT=http://127.0.0.1:8080 - OPENDAL_TEST_CAPABILITY_OVERRIDES=write_with_user_metadata=false + OPENDAL_TEST_CAPABILITY_OVERRIDES=write_with_user_metadata=false,read_with_if_match=false,read_with_if_none_match=false,read_with_if_modified_since=false EOF diff --git a/.github/services/webdav/nginx_with_empty_password/action.yml b/.github/services/webdav/nginx_with_empty_password/action.yml index ec1cf6212b0f..0d4b1fc183f4 100644 --- a/.github/services/webdav/nginx_with_empty_password/action.yml +++ b/.github/services/webdav/nginx_with_empty_password/action.yml @@ -31,5 +31,5 @@ runs: cat << EOF >> $GITHUB_ENV OPENDAL_WEBDAV_ENDPOINT=http://127.0.0.1:8080 OPENDAL_WEBDAV_USERNAME=foo - OPENDAL_TEST_CAPABILITY_OVERRIDES=write_with_user_metadata=false + OPENDAL_TEST_CAPABILITY_OVERRIDES=write_with_user_metadata=false,read_with_if_match=false,read_with_if_none_match=false,read_with_if_modified_since=false EOF diff --git a/.github/services/webdav/nginx_with_password/action.yml b/.github/services/webdav/nginx_with_password/action.yml index 181a75043c6f..63bf36ea056a 100644 --- a/.github/services/webdav/nginx_with_password/action.yml +++ b/.github/services/webdav/nginx_with_password/action.yml @@ -32,5 +32,5 @@ runs: OPENDAL_WEBDAV_ENDPOINT=http://127.0.0.1:8080 OPENDAL_WEBDAV_USERNAME=bar OPENDAL_WEBDAV_PASSWORD=bar - OPENDAL_TEST_CAPABILITY_OVERRIDES=write_with_user_metadata=false + OPENDAL_TEST_CAPABILITY_OVERRIDES=write_with_user_metadata=false,read_with_if_match=false,read_with_if_none_match=false,read_with_if_modified_since=false EOF diff --git a/.github/services/webdav/nginx_with_redirect/action.yml b/.github/services/webdav/nginx_with_redirect/action.yml index 114d86177557..4e4465186136 100644 --- a/.github/services/webdav/nginx_with_redirect/action.yml +++ b/.github/services/webdav/nginx_with_redirect/action.yml @@ -30,5 +30,5 @@ runs: run: | cat << EOF >> $GITHUB_ENV OPENDAL_WEBDAV_ENDPOINT=http://127.0.0.1:8081 - OPENDAL_TEST_CAPABILITY_OVERRIDES=write_with_user_metadata=false + OPENDAL_TEST_CAPABILITY_OVERRIDES=write_with_user_metadata=false,read_with_if_match=false,read_with_if_none_match=false,read_with_if_modified_since=false EOF