diff --git a/.github/services/webdav/0_nginx/action.yml b/.github/services/webdav/0_nginx/action.yml index c90a710cfa07..977e5ca0ae01 100644 --- a/.github/services/webdav/0_nginx/action.yml +++ b/.github/services/webdav/0_nginx/action.yml @@ -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 diff --git a/.github/services/webdav/nginx_with_empty_password/action.yml b/.github/services/webdav/nginx_with_empty_password/action.yml index ec1cf6212b0f..3b6a4fe053ed 100644 --- a/.github/services/webdav/nginx_with_empty_password/action.yml +++ b/.github/services/webdav/nginx_with_empty_password/action.yml @@ -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 diff --git a/.github/services/webdav/nginx_with_password/action.yml b/.github/services/webdav/nginx_with_password/action.yml index 181a75043c6f..e731666b2462 100644 --- a/.github/services/webdav/nginx_with_password/action.yml +++ b/.github/services/webdav/nginx_with_password/action.yml @@ -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 diff --git a/.github/services/webdav/nginx_with_redirect/action.yml b/.github/services/webdav/nginx_with_redirect/action.yml index 114d86177557..416957efff81 100644 --- a/.github/services/webdav/nginx_with_redirect/action.yml +++ b/.github/services/webdav/nginx_with_redirect/action.yml @@ -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 diff --git a/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java b/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java index d4cc5ad9910f..d7880101def3 100644 --- a/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java +++ b/bindings/java/src/main/java/org/apache/opendal/ServiceConfig.java @@ -3850,6 +3850,18 @@ public Map configMap() { @Data @RequiredArgsConstructor(access = AccessLevel.PRIVATE) class Webdav implements ServiceConfig { + /** + *

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

+ */ + public final Boolean disableConditionalRead; /** *

Deprecated: WebDAV copy capability is enabled by default.

* @@ -3916,6 +3928,9 @@ public String scheme() { @Override public Map configMap() { final HashMap map = new HashMap<>(); + if (disableConditionalRead != null) { + map.put("disable_conditional_read", String.valueOf(disableConditionalRead)); + } if (disableCopy != null) { map.put("disable_copy", String.valueOf(disableCopy)); } diff --git a/bindings/python/src/services.rs b/bindings/python/src/services.rs index 0bb0f190ef89..3361aec6449c 100644 --- a/bindings/python/src/services.rs +++ b/bindings/python/src/services.rs @@ -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 = ..., @@ -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. @@ -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 = ..., @@ -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. diff --git a/core/services/webdav/src/backend.rs b/core/services/webdav/src/backend.rs index 804376f285f3..36ee043dc8c3 100644 --- a/core/services/webdav/src/backend.rs +++ b/core/services/webdav/src/backend.rs @@ -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. + /// + /// Default: false + pub fn disable_conditional_read(mut self, disable: bool) -> Self { + self.config.disable_conditional_read = disable; + self + } } impl Builder for WebdavBuilder { @@ -193,6 +210,8 @@ 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 { @@ -200,6 +219,10 @@ impl Builder for WebdavBuilder { 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, diff --git a/core/services/webdav/src/config.rs b/core/services/webdav/src/config.rs index 255027afcc4d..9ef04c99ffd1 100644 --- a/core/services/webdav/src/config.rs +++ b/core/services/webdav/src/config.rs @@ -75,6 +75,19 @@ pub struct WebdavConfig { /// /// Default: `https://opendal.apache.org/ns` pub user_metadata_uri: Option, + /// 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 { @@ -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() } } diff --git a/core/services/webdav/src/core.rs b/core/services/webdav/src/core.rs index 11220b6520fc..f330bcd88f39 100644 --- a/core/services/webdav/src/core.rs +++ b/core/services/webdav/src/core.rs @@ -171,13 +171,35 @@ impl WebdavCore { ctx: &OperationContext, 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()) } @@ -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 diff --git a/website/data/services.json b/website/data/services.json index 80c78f6e2af9..751000762aa6 100644 --- a/website/data/services.json +++ b/website/data/services.json @@ -4648,6 +4648,13 @@ "required": false, "group": "General", "comments": "The XML namespace URI for user metadata properties.\n\nThis URI uniquely identifies the namespace for custom properties.\nDifferent servers may require different namespace URIs.\nFor example, Nextcloud might work better with its own namespace.\n\nDefault: `https://opendal.apache.org/ns`" + }, + { + "name": "disable_conditional_read", + "type": "bool", + "required": false, + "group": "General", + "comments": "Disable conditional read headers on GET requests.\n\nBy default, OpenDAL advertises and sends the RFC 7232 headers\n`If-Match`, `If-None-Match`, `If-Modified-Since` and\n`If-Unmodified-Since` when callers ask for conditional reads.\n\nSome WebDAV-compatible servers (e.g., nginx-dav) don't return\nETags in PROPFIND or don't honor these conditions on GET.\nEnable this option to drop the four `read_with_if_*` capabilities\nso callers fail fast instead of silently losing the condition.\n\nDefault: false" } ], "examples": [ @@ -4655,25 +4662,25 @@ "binding": "rust", "language": "rust", "minimal": "use opendal::Operator;\n\nlet op = Operator::via_iter(\"webdav\", [\n])?;", - "full": "use opendal::Operator;\n\nlet op = Operator::via_iter(\"webdav\", [\n // endpoint of this backend\n // (\"endpoint\".to_string(), \"...\".to_string()),\n\n // username of this backend\n // (\"username\".to_string(), \"...\".to_string()),\n\n // password of this backend\n // (\"password\".to_string(), \"...\".to_string()),\n\n // token of this backend\n // (\"token\".to_string(), \"...\".to_string()),\n\n // root of this backend\n // (\"root\".to_string(), \"...\".to_string()),\n\n // Disable automatic parent directory creation before write operations.\n //\n // By default, OpenDAL creates parent directories using MKCOL before writing files.\n // This requires PROPFIND support to check directory existence.\n //\n // Some WebDAV-compatible servers (e.g., bazel-remote) don't support PROPFIND\n // or don't require explicit directory creation. Enable this option to skip\n // the MKCOL calls and write files directly.\n //\n // Default: false\n // (\"disable_create_dir\".to_string(), \"true\".to_string()),\n\n // The XML namespace prefix for user metadata properties.\n //\n // This prefix is used in PROPPATCH/PROPFIND XML requests.\n // Different servers may require different prefixes.\n //\n // Default: \"opendal\"\n // (\"user_metadata_prefix\".to_string(), \"...\".to_string()),\n\n // The XML namespace URI for user metadata properties.\n //\n // This URI uniquely identifies the namespace for custom properties.\n // Different servers may require different namespace URIs.\n // For example, Nextcloud might work better with its own namespace.\n //\n // Default: `https://opendal.apache.org/ns`\n // (\"user_metadata_uri\".to_string(), \"...\".to_string()),\n\n])?;" + "full": "use opendal::Operator;\n\nlet op = Operator::via_iter(\"webdav\", [\n // endpoint of this backend\n // (\"endpoint\".to_string(), \"...\".to_string()),\n\n // username of this backend\n // (\"username\".to_string(), \"...\".to_string()),\n\n // password of this backend\n // (\"password\".to_string(), \"...\".to_string()),\n\n // token of this backend\n // (\"token\".to_string(), \"...\".to_string()),\n\n // root of this backend\n // (\"root\".to_string(), \"...\".to_string()),\n\n // Disable automatic parent directory creation before write operations.\n //\n // By default, OpenDAL creates parent directories using MKCOL before writing files.\n // This requires PROPFIND support to check directory existence.\n //\n // Some WebDAV-compatible servers (e.g., bazel-remote) don't support PROPFIND\n // or don't require explicit directory creation. Enable this option to skip\n // the MKCOL calls and write files directly.\n //\n // Default: false\n // (\"disable_create_dir\".to_string(), \"true\".to_string()),\n\n // The XML namespace prefix for user metadata properties.\n //\n // This prefix is used in PROPPATCH/PROPFIND XML requests.\n // Different servers may require different prefixes.\n //\n // Default: \"opendal\"\n // (\"user_metadata_prefix\".to_string(), \"...\".to_string()),\n\n // The XML namespace URI for user metadata properties.\n //\n // This URI uniquely identifies the namespace for custom properties.\n // Different servers may require different namespace URIs.\n // For example, Nextcloud might work better with its own namespace.\n //\n // Default: `https://opendal.apache.org/ns`\n // (\"user_metadata_uri\".to_string(), \"...\".to_string()),\n\n // Disable conditional read headers on GET requests.\n //\n // By default, OpenDAL advertises and sends the RFC 7232 headers\n // `If-Match`, `If-None-Match`, `If-Modified-Since` and\n // `If-Unmodified-Since` when callers ask for conditional reads.\n //\n // Some WebDAV-compatible servers (e.g., nginx-dav) don't return\n // ETags in PROPFIND or don't honor these conditions on GET.\n // Enable this option to drop the four `read_with_if_*` capabilities\n // so callers fail fast instead of silently losing the condition.\n //\n // Default: false\n // (\"disable_conditional_read\".to_string(), \"true\".to_string()),\n\n])?;" }, { "binding": "python", "language": "python", "minimal": "import opendal\n\nop = opendal.Operator(\n \"webdav\",\n)", - "full": "import opendal\n\nop = opendal.Operator(\n \"webdav\",\n # endpoint of this backend\n # endpoint=\"...\",\n\n # username of this backend\n # username=\"...\",\n\n # password of this backend\n # password=\"...\",\n\n # token of this backend\n # token=\"...\",\n\n # root of this backend\n # root=\"...\",\n\n # Disable automatic parent directory creation before write operations.\n #\n # By default, OpenDAL creates parent directories using MKCOL before writing files.\n # This requires PROPFIND support to check directory existence.\n #\n # Some WebDAV-compatible servers (e.g., bazel-remote) don't support PROPFIND\n # or don't require explicit directory creation. Enable this option to skip\n # the MKCOL calls and write files directly.\n #\n # Default: false\n # disable_create_dir=\"true\",\n\n # The XML namespace prefix for user metadata properties.\n #\n # This prefix is used in PROPPATCH/PROPFIND XML requests.\n # Different servers may require different prefixes.\n #\n # Default: \"opendal\"\n # user_metadata_prefix=\"...\",\n\n # The XML namespace URI for user metadata properties.\n #\n # This URI uniquely identifies the namespace for custom properties.\n # Different servers may require different namespace URIs.\n # For example, Nextcloud might work better with its own namespace.\n #\n # Default: `https://opendal.apache.org/ns`\n # user_metadata_uri=\"...\",\n\n)" + "full": "import opendal\n\nop = opendal.Operator(\n \"webdav\",\n # endpoint of this backend\n # endpoint=\"...\",\n\n # username of this backend\n # username=\"...\",\n\n # password of this backend\n # password=\"...\",\n\n # token of this backend\n # token=\"...\",\n\n # root of this backend\n # root=\"...\",\n\n # Disable automatic parent directory creation before write operations.\n #\n # By default, OpenDAL creates parent directories using MKCOL before writing files.\n # This requires PROPFIND support to check directory existence.\n #\n # Some WebDAV-compatible servers (e.g., bazel-remote) don't support PROPFIND\n # or don't require explicit directory creation. Enable this option to skip\n # the MKCOL calls and write files directly.\n #\n # Default: false\n # disable_create_dir=\"true\",\n\n # The XML namespace prefix for user metadata properties.\n #\n # This prefix is used in PROPPATCH/PROPFIND XML requests.\n # Different servers may require different prefixes.\n #\n # Default: \"opendal\"\n # user_metadata_prefix=\"...\",\n\n # The XML namespace URI for user metadata properties.\n #\n # This URI uniquely identifies the namespace for custom properties.\n # Different servers may require different namespace URIs.\n # For example, Nextcloud might work better with its own namespace.\n #\n # Default: `https://opendal.apache.org/ns`\n # user_metadata_uri=\"...\",\n\n # Disable conditional read headers on GET requests.\n #\n # By default, OpenDAL advertises and sends the RFC 7232 headers\n # `If-Match`, `If-None-Match`, `If-Modified-Since` and\n # `If-Unmodified-Since` when callers ask for conditional reads.\n #\n # Some WebDAV-compatible servers (e.g., nginx-dav) don't return\n # ETags in PROPFIND or don't honor these conditions on GET.\n # Enable this option to drop the four `read_with_if_*` capabilities\n # so callers fail fast instead of silently losing the condition.\n #\n # Default: false\n # disable_conditional_read=\"true\",\n\n)" }, { "binding": "nodejs", "language": "javascript", "minimal": "import { Operator } from \"opendal\";\n\nconst op = new Operator(\"webdav\", {\n});", - "full": "import { Operator } from \"opendal\";\n\nconst op = new Operator(\"webdav\", {\n // endpoint of this backend\n // endpoint: \"...\",\n\n // username of this backend\n // username: \"...\",\n\n // password of this backend\n // password: \"...\",\n\n // token of this backend\n // token: \"...\",\n\n // root of this backend\n // root: \"...\",\n\n // Disable automatic parent directory creation before write operations.\n //\n // By default, OpenDAL creates parent directories using MKCOL before writing files.\n // This requires PROPFIND support to check directory existence.\n //\n // Some WebDAV-compatible servers (e.g., bazel-remote) don't support PROPFIND\n // or don't require explicit directory creation. Enable this option to skip\n // the MKCOL calls and write files directly.\n //\n // Default: false\n // disable_create_dir: \"true\",\n\n // The XML namespace prefix for user metadata properties.\n //\n // This prefix is used in PROPPATCH/PROPFIND XML requests.\n // Different servers may require different prefixes.\n //\n // Default: \"opendal\"\n // user_metadata_prefix: \"...\",\n\n // The XML namespace URI for user metadata properties.\n //\n // This URI uniquely identifies the namespace for custom properties.\n // Different servers may require different namespace URIs.\n // For example, Nextcloud might work better with its own namespace.\n //\n // Default: `https://opendal.apache.org/ns`\n // user_metadata_uri: \"...\",\n\n});" + "full": "import { Operator } from \"opendal\";\n\nconst op = new Operator(\"webdav\", {\n // endpoint of this backend\n // endpoint: \"...\",\n\n // username of this backend\n // username: \"...\",\n\n // password of this backend\n // password: \"...\",\n\n // token of this backend\n // token: \"...\",\n\n // root of this backend\n // root: \"...\",\n\n // Disable automatic parent directory creation before write operations.\n //\n // By default, OpenDAL creates parent directories using MKCOL before writing files.\n // This requires PROPFIND support to check directory existence.\n //\n // Some WebDAV-compatible servers (e.g., bazel-remote) don't support PROPFIND\n // or don't require explicit directory creation. Enable this option to skip\n // the MKCOL calls and write files directly.\n //\n // Default: false\n // disable_create_dir: \"true\",\n\n // The XML namespace prefix for user metadata properties.\n //\n // This prefix is used in PROPPATCH/PROPFIND XML requests.\n // Different servers may require different prefixes.\n //\n // Default: \"opendal\"\n // user_metadata_prefix: \"...\",\n\n // The XML namespace URI for user metadata properties.\n //\n // This URI uniquely identifies the namespace for custom properties.\n // Different servers may require different namespace URIs.\n // For example, Nextcloud might work better with its own namespace.\n //\n // Default: `https://opendal.apache.org/ns`\n // user_metadata_uri: \"...\",\n\n // Disable conditional read headers on GET requests.\n //\n // By default, OpenDAL advertises and sends the RFC 7232 headers\n // `If-Match`, `If-None-Match`, `If-Modified-Since` and\n // `If-Unmodified-Since` when callers ask for conditional reads.\n //\n // Some WebDAV-compatible servers (e.g., nginx-dav) don't return\n // ETags in PROPFIND or don't honor these conditions on GET.\n // Enable this option to drop the four `read_with_if_*` capabilities\n // so callers fail fast instead of silently losing the condition.\n //\n // Default: false\n // disable_conditional_read: \"true\",\n\n});" }, { "binding": "java", "language": "java", "minimal": "import java.util.HashMap;\nimport java.util.Map;\nimport org.apache.opendal.Operator;\n\nMap conf = new HashMap<>();\nOperator op = Operator.of(\"webdav\", conf);", - "full": "import java.util.HashMap;\nimport java.util.Map;\nimport org.apache.opendal.Operator;\n\nMap conf = new HashMap<>();\n// endpoint of this backend\n// conf.put(\"endpoint\", \"...\");\n\n// username of this backend\n// conf.put(\"username\", \"...\");\n\n// password of this backend\n// conf.put(\"password\", \"...\");\n\n// token of this backend\n// conf.put(\"token\", \"...\");\n\n// root of this backend\n// conf.put(\"root\", \"...\");\n\n// Disable automatic parent directory creation before write operations.\n//\n// By default, OpenDAL creates parent directories using MKCOL before writing files.\n// This requires PROPFIND support to check directory existence.\n//\n// Some WebDAV-compatible servers (e.g., bazel-remote) don't support PROPFIND\n// or don't require explicit directory creation. Enable this option to skip\n// the MKCOL calls and write files directly.\n//\n// Default: false\n// conf.put(\"disable_create_dir\", \"true\");\n\n// The XML namespace prefix for user metadata properties.\n//\n// This prefix is used in PROPPATCH/PROPFIND XML requests.\n// Different servers may require different prefixes.\n//\n// Default: \"opendal\"\n// conf.put(\"user_metadata_prefix\", \"...\");\n\n// The XML namespace URI for user metadata properties.\n//\n// This URI uniquely identifies the namespace for custom properties.\n// Different servers may require different namespace URIs.\n// For example, Nextcloud might work better with its own namespace.\n//\n// Default: `https://opendal.apache.org/ns`\n// conf.put(\"user_metadata_uri\", \"...\");\n\nOperator op = Operator.of(\"webdav\", conf);" + "full": "import java.util.HashMap;\nimport java.util.Map;\nimport org.apache.opendal.Operator;\n\nMap conf = new HashMap<>();\n// endpoint of this backend\n// conf.put(\"endpoint\", \"...\");\n\n// username of this backend\n// conf.put(\"username\", \"...\");\n\n// password of this backend\n// conf.put(\"password\", \"...\");\n\n// token of this backend\n// conf.put(\"token\", \"...\");\n\n// root of this backend\n// conf.put(\"root\", \"...\");\n\n// Disable automatic parent directory creation before write operations.\n//\n// By default, OpenDAL creates parent directories using MKCOL before writing files.\n// This requires PROPFIND support to check directory existence.\n//\n// Some WebDAV-compatible servers (e.g., bazel-remote) don't support PROPFIND\n// or don't require explicit directory creation. Enable this option to skip\n// the MKCOL calls and write files directly.\n//\n// Default: false\n// conf.put(\"disable_create_dir\", \"true\");\n\n// The XML namespace prefix for user metadata properties.\n//\n// This prefix is used in PROPPATCH/PROPFIND XML requests.\n// Different servers may require different prefixes.\n//\n// Default: \"opendal\"\n// conf.put(\"user_metadata_prefix\", \"...\");\n\n// The XML namespace URI for user metadata properties.\n//\n// This URI uniquely identifies the namespace for custom properties.\n// Different servers may require different namespace URIs.\n// For example, Nextcloud might work better with its own namespace.\n//\n// Default: `https://opendal.apache.org/ns`\n// conf.put(\"user_metadata_uri\", \"...\");\n\n// Disable conditional read headers on GET requests.\n//\n// By default, OpenDAL advertises and sends the RFC 7232 headers\n// `If-Match`, `If-None-Match`, `If-Modified-Since` and\n// `If-Unmodified-Since` when callers ask for conditional reads.\n//\n// Some WebDAV-compatible servers (e.g., nginx-dav) don't return\n// ETags in PROPFIND or don't honor these conditions on GET.\n// Enable this option to drop the four `read_with_if_*` capabilities\n// so callers fail fast instead of silently losing the condition.\n//\n// Default: false\n// conf.put(\"disable_conditional_read\", \"true\");\n\nOperator op = Operator.of(\"webdav\", conf);" } ] },