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/opendal.git


The following commit(s) were added to refs/heads/main by this push:
     new d37828e2c refactor(services/cos): migrate to reqsign v2 (#7228)
d37828e2c is described below

commit d37828e2c41bdb9f6f18cb46b6c21b3f27914194
Author: Xuanwo <[email protected]>
AuthorDate: Thu Feb 26 21:05:35 2026 +0800

    refactor(services/cos): migrate to reqsign v2 (#7228)
---
 core/Cargo.lock                  |  21 +++++++-
 core/services/cos/Cargo.toml     |   8 ++--
 core/services/cos/src/backend.rs |  63 +++++++++++++++++-------
 core/services/cos/src/core.rs    | 100 +++++++++++++++------------------------
 core/services/cos/src/writer.rs  |   8 ++--
 5 files changed, 110 insertions(+), 90 deletions(-)

diff --git a/core/Cargo.lock b/core/Cargo.lock
index 13e66cb98..2b82c744e 100644
--- a/core/Cargo.lock
+++ b/core/Cargo.lock
@@ -6483,7 +6483,10 @@ dependencies = [
  "log",
  "opendal-core",
  "quick-xml",
- "reqsign",
+ "reqsign-core",
+ "reqsign-file-read-tokio",
+ "reqsign-http-send-reqwest",
+ "reqsign-tencent-cos",
  "reqwest",
  "serde",
  "tokio",
@@ -8792,6 +8795,22 @@ dependencies = [
  "reqsign-core",
 ]
 
+[[package]]
+name = "reqsign-tencent-cos"
+version = "2.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index";
+checksum = "3698e12fecf5976b9b6591c855afc0b3633c14526b935a61c885e573c2c9abd4"
+dependencies = [
+ "anyhow",
+ "async-trait",
+ "http 1.4.0",
+ "log",
+ "percent-encoding",
+ "reqsign-core",
+ "serde",
+ "serde_json",
+]
+
 [[package]]
 name = "reqwest"
 version = "0.12.24"
diff --git a/core/services/cos/Cargo.toml b/core/services/cos/Cargo.toml
index d58d0f71b..aae8ee078 100644
--- a/core/services/cos/Cargo.toml
+++ b/core/services/cos/Cargo.toml
@@ -36,10 +36,10 @@ http = { workspace = true }
 log = { workspace = true }
 opendal-core = { path = "../../core", version = "0.55.0", default-features = 
false }
 quick-xml = { workspace = true, features = ["serialize", "overlapped-lists"] }
-reqsign = { workspace = true, features = [
-  "services-tencent",
-  "reqwest_request",
-] }
+reqsign-core = { version = "2.0.1", default-features = false }
+reqsign-file-read-tokio = { version = "2.0.1", default-features = false }
+reqsign-http-send-reqwest = { version = "2.0.1", default-features = false }
+reqsign-tencent-cos = { version = "2.0.2", default-features = false }
 reqwest = { version = "0.12.24", default-features = false, features = [
   "stream",
 ] }
diff --git a/core/services/cos/src/backend.rs b/core/services/cos/src/backend.rs
index 366592515..164c6f6a1 100644
--- a/core/services/cos/src/backend.rs
+++ b/core/services/cos/src/backend.rs
@@ -22,9 +22,15 @@ use http::Response;
 use http::StatusCode;
 use http::Uri;
 use log::debug;
-use reqsign::TencentCosConfig;
-use reqsign::TencentCosCredentialLoader;
-use reqsign::TencentCosSigner;
+use reqsign_core::Context;
+use reqsign_core::Env as _;
+use reqsign_core::OsEnv;
+use reqsign_core::Signer;
+use reqsign_file_read_tokio::TokioFileRead;
+use reqsign_http_send_reqwest::ReqwestHttpSend;
+use reqsign_tencent_cos::DefaultCredentialProvider;
+use reqsign_tencent_cos::RequestSigner;
+use reqsign_tencent_cos::StaticCredentialProvider;
 
 use super::COS_SCHEME;
 use super::config::CosConfig;
@@ -175,21 +181,43 @@ impl Builder for CosBuilder {
         let endpoint = uri.host().unwrap().replace(&format!("//{bucket}."), 
"//");
         debug!("backend use endpoint {}", &endpoint);
 
-        let mut cfg = TencentCosConfig::default();
-        if !self.config.disable_config_load {
-            cfg = cfg.from_env();
-        }
+        let os_env = OsEnv;
+        let envs = os_env.vars();
+        let ctx = Context::new()
+            .with_file_read(TokioFileRead)
+            
.with_http_send(ReqwestHttpSend::new(GLOBAL_REQWEST_CLIENT.clone()))
+            .with_env(os_env);
+
+        let mut credential = if self.config.disable_config_load {
+            DefaultCredentialProvider::builder()
+                .disable_env(true)
+                .disable_assume_role(true)
+                .build()
+        } else {
+            DefaultCredentialProvider::new()
+        };
 
-        if let Some(v) = self.config.secret_id {
-            cfg.secret_id = Some(v);
+        if let (Some(secret_id), Some(secret_key)) = (
+            self.config.secret_id.as_deref(),
+            self.config.secret_key.as_deref(),
+        ) {
+            let security_token = envs
+                .get("TENCENTCLOUD_TOKEN")
+                .or_else(|| envs.get("TENCENTCLOUD_SECURITY_TOKEN"))
+                .or_else(|| envs.get("QCLOUD_SECRET_TOKEN"));
+
+            let static_provider = if self.config.disable_config_load {
+                StaticCredentialProvider::new(secret_id, secret_key)
+            } else if let Some(token) = security_token {
+                StaticCredentialProvider::with_security_token(secret_id, 
secret_key, token)
+            } else {
+                StaticCredentialProvider::new(secret_id, secret_key)
+            };
+
+            credential = credential.push_front(static_provider);
         }
-        if let Some(v) = self.config.secret_key {
-            cfg.secret_key = Some(v);
-        }
-
-        let cred_loader = 
TencentCosCredentialLoader::new(GLOBAL_REQWEST_CLIENT.clone(), cfg);
 
-        let signer = TencentCosSigner::new();
+        let signer = Signer::new(ctx, credential, RequestSigner::new());
 
         Ok(CosBackend {
             core: Arc::new(CosCore {
@@ -260,7 +288,6 @@ impl Builder for CosBuilder {
                 root,
                 endpoint: format!("{}://{}.{}", &scheme, &bucket, &endpoint),
                 signer,
-                loader: cred_loader,
             }),
         })
     }
@@ -399,8 +426,8 @@ impl Access for CosBackend {
                 "operation is not supported",
             )),
         };
-        let mut req = req?;
-        self.core.sign_query(&mut req, args.expire()).await?;
+        let req = req?;
+        let req = self.core.sign_query(req, args.expire()).await?;
 
         // We don't need this request anymore, consume it directly.
         let (parts, _) = req.into_parts();
diff --git a/core/services/cos/src/core.rs b/core/services/cos/src/core.rs
index ff7419974..9db6366d9 100644
--- a/core/services/cos/src/core.rs
+++ b/core/services/cos/src/core.rs
@@ -29,15 +29,12 @@ use http::header::IF_MATCH;
 use http::header::IF_MODIFIED_SINCE;
 use http::header::IF_NONE_MATCH;
 use http::header::IF_UNMODIFIED_SINCE;
-use reqsign::TencentCosCredential;
-use reqsign::TencentCosCredentialLoader;
-use reqsign::TencentCosSigner;
+use reqsign_core::Signer;
+use reqsign_tencent_cos::Credential;
 use serde::Deserialize;
 use serde::Serialize;
 
 use opendal_core::Buffer;
-use opendal_core::Error;
-use opendal_core::ErrorKind;
 use opendal_core::Result;
 use opendal_core::raw::*;
 
@@ -53,8 +50,7 @@ pub struct CosCore {
     pub root: String,
     pub endpoint: String,
 
-    pub signer: TencentCosSigner,
-    pub loader: TencentCosCredentialLoader,
+    pub signer: Signer<Credential>,
 }
 
 impl Debug for CosCore {
@@ -68,43 +64,26 @@ impl Debug for CosCore {
 }
 
 impl CosCore {
-    async fn load_credential(&self) -> Result<Option<TencentCosCredential>> {
-        let cred = self
-            .loader
-            .load()
-            .await
-            .map_err(new_request_credential_error)?;
-
-        if let Some(cred) = cred {
-            return Ok(Some(cred));
-        }
-
-        Err(Error::new(
-            ErrorKind::PermissionDenied,
-            "no valid credential found and anonymous access is not allowed",
-        ))
-    }
+    pub async fn sign<T>(&self, req: Request<T>) -> Result<Request<T>> {
+        let (mut parts, body) = req.into_parts();
 
-    pub async fn sign<T>(&self, req: &mut Request<T>) -> Result<()> {
-        let cred = if let Some(cred) = self.load_credential().await? {
-            cred
-        } else {
-            return Ok(());
-        };
+        self.signer
+            .sign(&mut parts, None)
+            .await
+            .map_err(|e| new_request_sign_error(e.into()))?;
 
-        self.signer.sign(req, &cred).map_err(new_request_sign_error)
+        Ok(Request::from_parts(parts, body))
     }
 
-    pub async fn sign_query<T>(&self, req: &mut Request<T>, duration: 
Duration) -> Result<()> {
-        let cred = if let Some(cred) = self.load_credential().await? {
-            cred
-        } else {
-            return Ok(());
-        };
+    pub async fn sign_query<T>(&self, req: Request<T>, duration: Duration) -> 
Result<Request<T>> {
+        let (mut parts, body) = req.into_parts();
 
         self.signer
-            .sign_query(req, duration, &cred)
-            .map_err(new_request_sign_error)
+            .sign(&mut parts, Some(duration))
+            .await
+            .map_err(|e| new_request_sign_error(e.into()))?;
+
+        Ok(Request::from_parts(parts, body))
     }
 
     #[inline]
@@ -120,9 +99,8 @@ impl CosCore {
         range: BytesRange,
         args: &OpRead,
     ) -> Result<Response<HttpBody>> {
-        let mut req = self.cos_get_object_request(path, range, args)?;
-
-        self.sign(&mut req).await?;
+        let req = self.cos_get_object_request(path, range, args)?;
+        let req = self.sign(req).await?;
 
         self.info.http_client().fetch(req).await
     }
@@ -231,9 +209,8 @@ impl CosCore {
     }
 
     pub async fn cos_head_object(&self, path: &str, args: &OpStat) -> 
Result<Response<Buffer>> {
-        let mut req = self.cos_head_object_request(path, args)?;
-
-        self.sign(&mut req).await?;
+        let req = self.cos_head_object_request(path, args)?;
+        let req = self.sign(req).await?;
 
         self.send(req).await
     }
@@ -293,9 +270,8 @@ impl CosCore {
 
         let req = req.extension(Operation::Delete);
 
-        let mut req = 
req.body(Buffer::new()).map_err(new_request_build_error)?;
-
-        self.sign(&mut req).await?;
+        let req = req.body(Buffer::new()).map_err(new_request_build_error)?;
+        let req = self.sign(req).await?;
 
         self.send(req).await
     }
@@ -345,13 +321,13 @@ impl CosCore {
         let source = format!("/{}/{}", self.bucket, 
percent_encode_path(&source));
         let url = format!("{}/{}", self.endpoint, 
percent_encode_path(&target));
 
-        let mut req = Request::put(&url)
+        let req = Request::put(&url)
             .extension(Operation::Copy)
             .header("x-cos-copy-source", &source)
             .body(Buffer::new())
             .map_err(new_request_build_error)?;
 
-        self.sign(&mut req).await?;
+        let req = self.sign(req).await?;
 
         self.send(req).await
     }
@@ -380,12 +356,12 @@ impl CosCore {
             url = url.push("marker", next_marker);
         }
 
-        let mut req = Request::get(url.finish())
+        let req = Request::get(url.finish())
             .extension(Operation::List)
             .body(Buffer::new())
             .map_err(new_request_build_error)?;
 
-        self.sign(&mut req).await?;
+        let req = self.sign(req).await?;
 
         self.send(req).await
     }
@@ -422,9 +398,8 @@ impl CosCore {
 
         let req = req.extension(Operation::Write);
 
-        let mut req = 
req.body(Buffer::new()).map_err(new_request_build_error)?;
-
-        self.sign(&mut req).await?;
+        let req = req.body(Buffer::new()).map_err(new_request_build_error)?;
+        let req = self.sign(req).await?;
 
         self.send(req).await
     }
@@ -453,9 +428,8 @@ impl CosCore {
         let req = req.extension(Operation::Write);
 
         // Set body
-        let mut req = req.body(body).map_err(new_request_build_error)?;
-
-        self.sign(&mut req).await?;
+        let req = req.body(body).map_err(new_request_build_error)?;
+        let req = self.sign(req).await?;
 
         self.send(req).await
     }
@@ -486,11 +460,11 @@ impl CosCore {
 
         let req = req.extension(Operation::Write);
 
-        let mut req = req
+        let req = req
             .body(Buffer::from(Bytes::from(content)))
             .map_err(new_request_build_error)?;
 
-        self.sign(&mut req).await?;
+        let req = self.sign(req).await?;
 
         self.send(req).await
     }
@@ -510,11 +484,11 @@ impl CosCore {
             percent_encode_path(upload_id)
         );
 
-        let mut req = Request::delete(&url)
+        let req = Request::delete(&url)
             .extension(Operation::Delete)
             .body(Buffer::new())
             .map_err(new_request_build_error)?;
-        self.sign(&mut req).await?;
+        let req = self.sign(req).await?;
         self.send(req).await
     }
 
@@ -547,12 +521,12 @@ impl CosCore {
             url = url.push("version-id-marker", 
&percent_encode_path(version_id_marker));
         }
 
-        let mut req = Request::get(url.finish())
+        let req = Request::get(url.finish())
             .extension(Operation::List)
             .body(Buffer::new())
             .map_err(new_request_build_error)?;
 
-        self.sign(&mut req).await?;
+        let req = self.sign(req).await?;
 
         self.send(req).await
     }
diff --git a/core/services/cos/src/writer.rs b/core/services/cos/src/writer.rs
index a94986f6c..16b85680f 100644
--- a/core/services/cos/src/writer.rs
+++ b/core/services/cos/src/writer.rs
@@ -69,11 +69,11 @@ impl CosWriter {
 
 impl oio::MultipartWrite for CosWriter {
     async fn write_once(&self, size: u64, body: Buffer) -> Result<Metadata> {
-        let mut req = self
+        let req = self
             .core
             .cos_put_object_request(&self.path, Some(size), &self.op, body)?;
 
-        self.core.sign(&mut req).await?;
+        let req = self.core.sign(req).await?;
 
         let resp = self.core.send(req).await?;
 
@@ -219,11 +219,11 @@ impl oio::AppendWrite for CosWriter {
     }
 
     async fn append(&self, offset: u64, size: u64, body: Buffer) -> 
Result<Metadata> {
-        let mut req = self
+        let req = self
             .core
             .cos_append_object_request(&self.path, offset, size, &self.op, 
body)?;
 
-        self.core.sign(&mut req).await?;
+        let req = self.core.sign(req).await?;
 
         let resp = self.core.send(req).await?;
 

Reply via email to