This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new e0db2edb feat(webdav): support copy (#1870)
e0db2edb is described below

commit e0db2edbec988b98795cfaa7d4f257b40adb99e4
Author: Suyan <[email protected]>
AuthorDate: Fri Apr 7 20:56:35 2023 +0800

    feat(webdav): support copy (#1870)
    
    * feat(webdav): support copy
    
    Signed-off-by: suyanhanx <[email protected]>
    
    * ci(webdav): enable COPY method for test
    
    Signed-off-by: suyanhanx <[email protected]>
    
    * ensure parent path before copy
    
    Signed-off-by: suyanhanx <[email protected]>
    
    ---------
    
    Signed-off-by: suyanhanx <[email protected]>
---
 core/src/services/webdav/backend.rs                | 81 +++++++++++++++++-----
 .../webdav/fixtures/nginx-with-basic-auth.conf     |  2 +-
 core/src/services/webdav/fixtures/nginx.conf       |  2 +-
 3 files changed, 67 insertions(+), 18 deletions(-)

diff --git a/core/src/services/webdav/backend.rs 
b/core/src/services/webdav/backend.rs
index 5b1180c5..81ebbd41 100644
--- a/core/src/services/webdav/backend.rs
+++ b/core/src/services/webdav/backend.rs
@@ -44,6 +44,7 @@ use crate::*;
 ///
 /// - [x] read
 /// - [x] write
+/// - [x] copy
 /// - [x] list
 /// - [ ] ~~scan~~
 /// - [ ] ~~presign~~
@@ -265,7 +266,10 @@ impl Accessor for WebdavBackend {
         ma.set_scheme(Scheme::Webdav)
             .set_root(&self.root)
             .set_capabilities(
-                AccessorCapability::Read | AccessorCapability::Write | 
AccessorCapability::List,
+                AccessorCapability::Read
+                    | AccessorCapability::Write
+                    | AccessorCapability::Copy
+                    | AccessorCapability::List,
             )
             .set_hints(AccessorHint::ReadStreamable);
 
@@ -273,22 +277,10 @@ impl Accessor for WebdavBackend {
     }
 
     async fn create(&self, path: &str, _: OpCreate) -> Result<RpCreate> {
-        // create dir recursively, split path by `/` and create each dir 
except the last one
-        let abs_path = build_abs_path(&self.root, path);
-        let abs_path = abs_path.as_str();
-        let mut parts: Vec<&str> = abs_path.split('/').filter(|x| 
!x.is_empty()).collect();
-        if !parts.is_empty() {
-            parts.pop();
-        }
+        self.ensure_parent_path(path).await?;
 
-        let mut sub_path = String::new();
-        for sub_part in parts {
-            let sub_path_with_slash = sub_part.to_owned() + "/";
-            sub_path.push_str(&sub_path_with_slash);
-            self.create_internal(&sub_path).await?;
-        }
-
-        self.create_internal(abs_path).await
+        let abs_path = build_abs_path(&self.root, path);
+        self.create_internal(&abs_path).await
     }
 
     async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, 
Self::Reader)> {
@@ -318,6 +310,19 @@ impl Accessor for WebdavBackend {
         Ok((RpWrite::default(), WebdavWriter::new(self.clone(), args, p)))
     }
 
+    async fn copy(&self, from: &str, to: &str, _args: OpCopy) -> 
Result<RpCopy> {
+        self.ensure_parent_path(to).await?;
+
+        let resp = self.webdav_copy(from, to).await?;
+
+        let status = resp.status();
+
+        match status {
+            StatusCode::CREATED | StatusCode::NO_CONTENT => 
Ok(RpCopy::default()),
+            _ => Err(parse_error(resp).await?),
+        }
+    }
+
     async fn stat(&self, path: &str, _: OpStat) -> Result<RpStat> {
         // Stat root always returns a DIR.
         if path == "/" {
@@ -549,6 +554,31 @@ impl WebdavBackend {
         self.client.send_async(req).await
     }
 
+    async fn webdav_copy(&self, from: &str, to: &str) -> 
Result<Response<IncomingAsyncBody>> {
+        let source = build_abs_path(&self.root, from);
+        let target = build_abs_path(&self.root, to);
+
+        let source = format!("{}/{}", self.endpoint, 
percent_encode_path(&source));
+        let target = format!("{}/{}", self.endpoint, 
percent_encode_path(&target));
+
+        let mut req = Request::builder().method("COPY").uri(&source);
+
+        if let Some(auth) = &self.authorization {
+            req = req.header(header::AUTHORIZATION, auth);
+        }
+
+        req = req.header("Destination", target);
+
+        // We always specific "T" for keeping to overwrite the destination.
+        req = req.header("Overwrite", "T");
+
+        let req = req
+            .body(AsyncBody::Empty)
+            .map_err(new_request_build_error)?;
+
+        self.client.send_async(req).await
+    }
+
     async fn create_internal(&self, abs_path: &str) -> Result<RpCreate> {
         let resp = if abs_path.ends_with('/') {
             self.webdav_mkcol(abs_path, None, None, AsyncBody::Empty)
@@ -575,4 +605,23 @@ impl WebdavBackend {
             _ => Err(parse_error(resp).await?),
         }
     }
+
+    async fn ensure_parent_path(&self, path: &str) -> Result<()> {
+        // create dir recursively, split path by `/` and create each dir 
except the last one
+        let abs_path = build_abs_path(&self.root, path);
+        let abs_path = abs_path.as_str();
+        let mut parts: Vec<&str> = abs_path.split('/').filter(|x| 
!x.is_empty()).collect();
+        if !parts.is_empty() {
+            parts.pop();
+        }
+
+        let mut sub_path = String::new();
+        for sub_part in parts {
+            let sub_path_with_slash = sub_part.to_owned() + "/";
+            sub_path.push_str(&sub_path_with_slash);
+            self.create_internal(&sub_path).await?;
+        }
+
+        Ok(())
+    }
 }
diff --git a/core/src/services/webdav/fixtures/nginx-with-basic-auth.conf 
b/core/src/services/webdav/fixtures/nginx-with-basic-auth.conf
index 384defa3..b34b854e 100644
--- a/core/src/services/webdav/fixtures/nginx-with-basic-auth.conf
+++ b/core/src/services/webdav/fixtures/nginx-with-basic-auth.conf
@@ -17,7 +17,7 @@ http {
     location / {
       client_body_temp_path /tmp;
       log_not_found off;
-      dav_methods PUT DELETE MKCOL;
+      dav_methods PUT DELETE MKCOL COPY;
       dav_ext_methods PROPFIND;
       create_full_put_path on;
       client_max_body_size 1024M;
diff --git a/core/src/services/webdav/fixtures/nginx.conf 
b/core/src/services/webdav/fixtures/nginx.conf
index 22ab20df..20e15258 100644
--- a/core/src/services/webdav/fixtures/nginx.conf
+++ b/core/src/services/webdav/fixtures/nginx.conf
@@ -17,7 +17,7 @@ http {
     location / {
       client_body_temp_path /tmp;
       log_not_found off;
-      dav_methods PUT DELETE MKCOL;
+      dav_methods PUT DELETE MKCOL COPY;
       dav_ext_methods PROPFIND;
       create_full_put_path on;
       client_max_body_size 1024M;

Reply via email to