Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/services/webdav/0_nginx/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ runs:
run: |
cat << EOF >> $GITHUB_ENV
OPENDAL_WEBDAV_ENDPOINT=http://127.0.0.1:8080
OPENDAL_WEBDAV_DISABLE_CONDITIONAL_READ=true
OPENDAL_TEST_CAPABILITY_OVERRIDES=write_with_user_metadata=false
EOF
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@ runs:
cat << EOF >> $GITHUB_ENV
OPENDAL_WEBDAV_ENDPOINT=http://127.0.0.1:8080
OPENDAL_WEBDAV_USERNAME=foo
OPENDAL_WEBDAV_DISABLE_CONDITIONAL_READ=true
OPENDAL_TEST_CAPABILITY_OVERRIDES=write_with_user_metadata=false
EOF
1 change: 1 addition & 0 deletions .github/services/webdav/nginx_with_password/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@ runs:
OPENDAL_WEBDAV_ENDPOINT=http://127.0.0.1:8080
OPENDAL_WEBDAV_USERNAME=bar
OPENDAL_WEBDAV_PASSWORD=bar
OPENDAL_WEBDAV_DISABLE_CONDITIONAL_READ=true
OPENDAL_TEST_CAPABILITY_OVERRIDES=write_with_user_metadata=false
EOF
1 change: 1 addition & 0 deletions .github/services/webdav/nginx_with_redirect/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ runs:
run: |
cat << EOF >> $GITHUB_ENV
OPENDAL_WEBDAV_ENDPOINT=http://127.0.0.1:8081
OPENDAL_WEBDAV_DISABLE_CONDITIONAL_READ=true
OPENDAL_TEST_CAPABILITY_OVERRIDES=write_with_user_metadata=false
EOF
Original file line number Diff line number Diff line change
Expand Up @@ -3850,6 +3850,18 @@ public Map<String, String> configMap() {
@Data
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
class Webdav implements ServiceConfig {
/**
* <p>Disable conditional read headers on GET requests.</p>
* <p>By default, OpenDAL advertises and sends the RFC 7232 headers
* <code>If-Match</code>, <code>If-None-Match</code>, <code>If-Modified-Since</code> and
* <code>If-Unmodified-Since</code> when callers ask for conditional reads.</p>
* <p>Some WebDAV-compatible servers (e.g., nginx-dav) don't return
* ETags in PROPFIND or don't honor these conditions on GET.
* Enable this option to drop the four <code>read_with_if_*</code> capabilities
* so callers fail fast instead of silently losing the condition.</p>
* <p>Default: false</p>
*/
public final Boolean disableConditionalRead;
/**
* <p>Deprecated: WebDAV copy capability is enabled by default.</p>
*
Expand Down Expand Up @@ -3916,6 +3928,9 @@ public String scheme() {
@Override
public Map<String, String> configMap() {
final HashMap<String, String> map = new HashMap<>();
if (disableConditionalRead != null) {
map.put("disable_conditional_read", String.valueOf(disableConditionalRead));
}
if (disableCopy != null) {
map.put("disable_copy", String.valueOf(disableCopy));
}
Expand Down
28 changes: 28 additions & 0 deletions bindings/python/src/services.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2657,6 +2657,7 @@ submit! {
scheme: typing.Literal[opendal.services.Scheme.Webdav, "webdav"],
/,
*,
disable_conditional_read: builtins.bool = ...,
disable_copy: builtins.bool = ...,
disable_create_dir: builtins.bool = ...,
enable_user_metadata: builtins.bool = ...,
Expand All @@ -2673,6 +2674,19 @@ submit! {

Parameters
----------
disable_conditional_read : builtins.bool, optional
Disable conditional read headers on GET requests.
By default, OpenDAL advertises and sends the RFC
7232 headers `If-Match`, `If-None-Match`,
`If-Modified-Since` and `If-Unmodified-Since` when
callers ask for conditional reads.
Some WebDAV-compatible servers (e.g., nginx-dav)
don't return ETags in PROPFIND or don't honor these
conditions on GET.
Enable this option to drop the four `read_with_if_*`
capabilities so callers fail fast instead of
silently losing the condition.
Default: false
disable_copy : builtins.bool, optional
Deprecated: WebDAV copy capability is enabled by
default.
Expand Down Expand Up @@ -5301,6 +5315,7 @@ submit! {
scheme: typing.Literal[opendal.services.Scheme.Webdav, "webdav"],
/,
*,
disable_conditional_read: builtins.bool = ...,
disable_copy: builtins.bool = ...,
disable_create_dir: builtins.bool = ...,
enable_user_metadata: builtins.bool = ...,
Expand All @@ -5317,6 +5332,19 @@ submit! {

Parameters
----------
disable_conditional_read : builtins.bool, optional
Disable conditional read headers on GET requests.
By default, OpenDAL advertises and sends the RFC
7232 headers `If-Match`, `If-None-Match`,
`If-Modified-Since` and `If-Unmodified-Since` when
callers ask for conditional reads.
Some WebDAV-compatible servers (e.g., nginx-dav)
don't return ETags in PROPFIND or don't honor these
conditions on GET.
Enable this option to drop the four `read_with_if_*`
capabilities so callers fail fast instead of
silently losing the condition.
Default: false
disable_copy : builtins.bool, optional
Deprecated: WebDAV copy capability is enabled by
default.
Expand Down
23 changes: 23 additions & 0 deletions core/services/webdav/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,23 @@ impl WebdavBuilder {
}
self
}

/// Disable conditional read headers on GET requests.
///
/// By default, OpenDAL advertises and sends the RFC 7232 headers
/// `If-Match`, `If-None-Match`, `If-Modified-Since` and
/// `If-Unmodified-Since` when callers ask for conditional reads.
///
/// Some WebDAV-compatible servers (e.g., nginx-dav) don't return
/// ETags in PROPFIND or don't honor these conditions on GET.
/// Enable this option to drop the four `read_with_if_*` capabilities
/// so callers fail fast instead of silently losing the condition.
Comment on lines +165 to +166

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Enable this option to drop the four `read_with_if_*` capabilities
/// so callers fail fast instead of silently losing the condition.
/// Set this option to disable conditional read, specifically, disabling capabilities:
/// - `read_with_if_match`
/// - `read_with_if_none_match`
/// - `read_with_if_modified_since`
/// - `read_with_if_unmodified_since`
/// so callers fail fast instead of silently losing the condition.

I don't understand the last sentence. Is OpenDAL sending data to a webdav server and webdav server will respond with an error? Can we send data to a webdav server without an error with this flag?
Backend has this code:

    fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
        let output: oio::StreamReader<WebdavReader> = {
            Ok(oio::StreamReader::new(WebdavReader::new(

///
/// Default: false
pub fn disable_conditional_read(mut self, disable: bool) -> Self {
self.config.disable_conditional_read = disable;
self
}
}

impl Builder for WebdavBuilder {
Expand Down Expand Up @@ -193,13 +210,19 @@ impl Builder for WebdavBuilder {
authorization = Some(format_authorization_by_bearer(token)?)
}

let conditional_read = !self.config.disable_conditional_read;

let core = Arc::new(WebdavCore {
info: ServiceInfo::new(WEBDAV_SCHEME, &root, ""),
capability: Capability {
stat: true,

read: true,
read_with_suffix: true,
read_with_if_match: conditional_read,
read_with_if_none_match: conditional_read,
read_with_if_modified_since: conditional_read,
read_with_if_unmodified_since: conditional_read,

write: true,
write_can_empty: true,
Expand Down
14 changes: 14 additions & 0 deletions core/services/webdav/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,19 @@ pub struct WebdavConfig {
///
/// Default: `https://opendal.apache.org/ns`
pub user_metadata_uri: Option<String>,
/// Disable conditional read headers on GET requests.
///
/// By default, OpenDAL advertises and sends the RFC 7232 headers
/// `If-Match`, `If-None-Match`, `If-Modified-Since` and
/// `If-Unmodified-Since` when callers ask for conditional reads.
///
/// Some WebDAV-compatible servers (e.g., nginx-dav) don't return
/// ETags in PROPFIND or don't honor these conditions on GET.
/// Enable this option to drop the four `read_with_if_*` capabilities
/// so callers fail fast instead of silently losing the condition.
///
/// Default: false
pub disable_conditional_read: bool,
}

impl Debug for WebdavConfig {
Expand All @@ -86,6 +99,7 @@ impl Debug for WebdavConfig {
.field("disable_create_dir", &self.disable_create_dir)
.field("user_metadata_prefix", &self.user_metadata_prefix)
.field("user_metadata_uri", &self.user_metadata_uri)
.field("disable_conditional_read", &self.disable_conditional_read)
.finish_non_exhaustive()
}
}
Expand Down
27 changes: 26 additions & 1 deletion core/services/webdav/src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,35 @@ impl WebdavCore {
ctx: &OperationContext,
path: &str,
range: BytesRange,
_: &OpRead,
args: &OpRead,
) -> Result<Response<HttpBody>> {
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())
}
Expand Down Expand Up @@ -1537,6 +1559,9 @@ mod 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
Expand Down
Loading
Loading