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 2282db58e refactor: Migrate http service to context based http client 
(#5879)
2282db58e is described below

commit 2282db58e45332e3d7cabb10a0c891cda316fff8
Author: miro <[email protected]>
AuthorDate: Wed Mar 26 13:10:03 2025 +0800

    refactor: Migrate http service to context based http client (#5879)
    
    * refactor: Introduce HttpCore for improved service structure
    
    * refactor: Migrate http service to context based http client
---
 core/src/services/http/backend.rs | 205 ++++++++++++--------------------------
 core/src/services/http/core.rs    | 121 ++++++++++++++++++++++
 core/src/services/http/mod.rs     |   2 +
 3 files changed, 186 insertions(+), 142 deletions(-)

diff --git a/core/src/services/http/backend.rs 
b/core/src/services/http/backend.rs
index f41e771c6..02c955f17 100644
--- a/core/src/services/http/backend.rs
+++ b/core/src/services/http/backend.rs
@@ -19,14 +19,11 @@ use std::fmt::Debug;
 use std::fmt::Formatter;
 use std::sync::Arc;
 
-use http::header;
-use http::header::IF_MATCH;
-use http::header::IF_NONE_MATCH;
-use http::Request;
 use http::Response;
 use http::StatusCode;
 use log::debug;
 
+use super::core::HttpCore;
 use super::error::parse_error;
 use crate::raw::*;
 use crate::services::HttpConfig;
@@ -34,6 +31,8 @@ use crate::*;
 
 impl Configurator for HttpConfig {
     type Builder = HttpBuilder;
+
+    #[allow(deprecated)]
     fn into_builder(self) -> Self::Builder {
         HttpBuilder {
             config: self,
@@ -47,6 +46,8 @@ impl Configurator for HttpConfig {
 #[derive(Default)]
 pub struct HttpBuilder {
     config: HttpConfig,
+
+    #[deprecated(since = "0.53.0", note = "Use `Operator::update_http_client` 
instead")]
     http_client: Option<HttpClient>,
 }
 
@@ -119,6 +120,8 @@ impl HttpBuilder {
     ///
     /// This API is part of OpenDAL's Raw API. `HttpClient` could be changed
     /// during minor updates.
+    #[deprecated(since = "0.53.0", note = "Use `Operator::update_http_client` 
instead")]
+    #[allow(deprecated)]
     pub fn http_client(mut self, client: HttpClient) -> Self {
         self.http_client = Some(client);
         self
@@ -143,15 +146,6 @@ impl Builder for HttpBuilder {
         let root = normalize_root(&self.config.root.unwrap_or_default());
         debug!("backend use root {}", root);
 
-        let client = if let Some(client) = self.http_client {
-            client
-        } else {
-            HttpClient::new().map_err(|err| {
-                err.with_operation("Builder::build")
-                    .with_context("service", Scheme::Http)
-            })?
-        };
-
         let mut auth = None;
         if let Some(username) = &self.config.username {
             auth = Some(format_authorization_by_basic(
@@ -163,67 +157,66 @@ impl Builder for HttpBuilder {
             auth = Some(format_authorization_by_bearer(token)?)
         }
 
-        Ok(HttpBackend {
-            info: {
-                let ma = AccessorInfo::default();
-                ma.set_scheme(Scheme::Http)
-                    .set_root(&root)
-                    .set_native_capability(Capability {
-                        stat: true,
-                        stat_with_if_match: true,
-                        stat_with_if_none_match: true,
-                        stat_has_cache_control: true,
-                        stat_has_content_length: true,
-                        stat_has_content_type: true,
-                        stat_has_content_encoding: true,
-                        stat_has_content_range: true,
-                        stat_has_etag: true,
-                        stat_has_content_md5: true,
-                        stat_has_last_modified: true,
-                        stat_has_content_disposition: true,
-
-                        read: true,
-
-                        read_with_if_match: true,
-                        read_with_if_none_match: true,
-
-                        presign: auth.is_none(),
-                        presign_read: auth.is_none(),
-                        presign_stat: auth.is_none(),
-
-                        shared: true,
-
-                        ..Default::default()
-                    });
-
-                ma.into()
-            },
+        let info = AccessorInfo::default();
+        info.set_scheme(Scheme::Http)
+            .set_root(&root)
+            .set_native_capability(Capability {
+                stat: true,
+                stat_with_if_match: true,
+                stat_with_if_none_match: true,
+                stat_has_cache_control: true,
+                stat_has_content_length: true,
+                stat_has_content_type: true,
+                stat_has_content_encoding: true,
+                stat_has_content_range: true,
+                stat_has_etag: true,
+                stat_has_content_md5: true,
+                stat_has_last_modified: true,
+                stat_has_content_disposition: true,
+
+                read: true,
+
+                read_with_if_match: true,
+                read_with_if_none_match: true,
+
+                presign: auth.is_none(),
+                presign_read: auth.is_none(),
+                presign_stat: auth.is_none(),
+
+                shared: true,
+
+                ..Default::default()
+            });
+
+        // allow deprecated api here for compatibility
+        #[allow(deprecated)]
+        if let Some(client) = self.http_client {
+            info.update_http_client(|_| client);
+        }
+
+        let accessor_info = Arc::new(info);
+
+        let core = Arc::new(HttpCore {
+            info: accessor_info,
             endpoint: endpoint.to_string(),
-            authorization: auth,
             root,
-            client,
-        })
+            authorization: auth,
+        });
+
+        Ok(HttpBackend { core })
     }
 }
 
 /// Backend is used to serve `Accessor` support for http.
 #[derive(Clone)]
 pub struct HttpBackend {
-    info: Arc<AccessorInfo>,
-
-    endpoint: String,
-    root: String,
-    client: HttpClient,
-
-    authorization: Option<String>,
+    core: Arc<HttpCore>,
 }
 
 impl Debug for HttpBackend {
     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
-        f.debug_struct("Backend")
-            .field("endpoint", &self.endpoint)
-            .field("root", &self.root)
-            .field("client", &self.client)
+        f.debug_struct("HttpBackend")
+            .field("core", &self.core)
             .finish()
     }
 }
@@ -239,7 +232,7 @@ impl Access for HttpBackend {
     type BlockingDeleter = ();
 
     fn info(&self) -> Arc<AccessorInfo> {
-        self.info.clone()
+        self.core.info.clone()
     }
 
     async fn stat(&self, path: &str, args: OpStat) -> Result<RpStat> {
@@ -248,7 +241,7 @@ impl Access for HttpBackend {
             return Ok(RpStat::new(Metadata::new(EntryMode::DIR)));
         }
 
-        let resp = self.http_head(path, &args).await?;
+        let resp = self.core.http_head(path, &args).await?;
 
         let status = resp.status();
 
@@ -264,7 +257,7 @@ impl Access for HttpBackend {
     }
 
     async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, 
Self::Reader)> {
-        let resp = self.http_get(path, args.range(), &args).await?;
+        let resp = self.core.http_get(path, args.range(), &args).await?;
 
         let status = resp.status();
 
@@ -281,7 +274,7 @@ impl Access for HttpBackend {
     }
 
     async fn presign(&self, path: &str, args: OpPresign) -> Result<RpPresign> {
-        if self.has_authorization() {
+        if self.core.has_authorization() {
             return Err(Error::new(
                 ErrorKind::Unsupported,
                 "Http doesn't support presigned request on backend with 
authorization",
@@ -289,8 +282,10 @@ impl Access for HttpBackend {
         }
 
         let req = match args.operation() {
-            PresignOperation::Stat(v) => self.http_head_request(path, v)?,
-            PresignOperation::Read(v) => self.http_get_request(path, 
BytesRange::default(), v)?,
+            PresignOperation::Stat(v) => self.core.http_head_request(path, v)?,
+            PresignOperation::Read(v) => {
+                self.core.http_get_request(path, BytesRange::default(), v)?
+            }
             _ => {
                 return Err(Error::new(
                     ErrorKind::Unsupported,
@@ -308,77 +303,3 @@ impl Access for HttpBackend {
         )))
     }
 }
-
-impl HttpBackend {
-    pub fn has_authorization(&self) -> bool {
-        self.authorization.is_some()
-    }
-
-    pub fn http_get_request(
-        &self,
-        path: &str,
-        range: BytesRange,
-        args: &OpRead,
-    ) -> Result<Request<Buffer>> {
-        let p = build_rooted_abs_path(&self.root, path);
-
-        let url = format!("{}{}", self.endpoint, percent_encode_path(&p));
-
-        let mut req = Request::get(&url);
-
-        if let Some(if_match) = args.if_match() {
-            req = req.header(IF_MATCH, if_match);
-        }
-
-        if let Some(if_none_match) = args.if_none_match() {
-            req = req.header(IF_NONE_MATCH, if_none_match);
-        }
-
-        if let Some(auth) = &self.authorization {
-            req = req.header(header::AUTHORIZATION, auth.clone())
-        }
-
-        if !range.is_full() {
-            req = req.header(header::RANGE, range.to_header());
-        }
-
-        req.body(Buffer::new()).map_err(new_request_build_error)
-    }
-
-    pub async fn http_get(
-        &self,
-        path: &str,
-        range: BytesRange,
-        args: &OpRead,
-    ) -> Result<Response<HttpBody>> {
-        let req = self.http_get_request(path, range, args)?;
-        self.client.fetch(req).await
-    }
-
-    pub fn http_head_request(&self, path: &str, args: &OpStat) -> 
Result<Request<Buffer>> {
-        let p = build_rooted_abs_path(&self.root, path);
-
-        let url = format!("{}{}", self.endpoint, percent_encode_path(&p));
-
-        let mut req = Request::head(&url);
-
-        if let Some(if_match) = args.if_match() {
-            req = req.header(IF_MATCH, if_match);
-        }
-
-        if let Some(if_none_match) = args.if_none_match() {
-            req = req.header(IF_NONE_MATCH, if_none_match);
-        }
-
-        if let Some(auth) = &self.authorization {
-            req = req.header(header::AUTHORIZATION, auth.clone())
-        }
-
-        req.body(Buffer::new()).map_err(new_request_build_error)
-    }
-
-    async fn http_head(&self, path: &str, args: &OpStat) -> 
Result<Response<Buffer>> {
-        let req = self.http_head_request(path, args)?;
-        self.client.send(req).await
-    }
-}
diff --git a/core/src/services/http/core.rs b/core/src/services/http/core.rs
new file mode 100644
index 000000000..6bb745383
--- /dev/null
+++ b/core/src/services/http/core.rs
@@ -0,0 +1,121 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+use std::fmt::Debug;
+use std::fmt::Formatter;
+use std::sync::Arc;
+
+use http::header;
+use http::header::IF_MATCH;
+use http::header::IF_NONE_MATCH;
+use http::Request;
+use http::Response;
+
+use crate::raw::*;
+use crate::*;
+
+pub struct HttpCore {
+    pub info: Arc<AccessorInfo>,
+
+    pub endpoint: String,
+    pub root: String,
+
+    pub authorization: Option<String>,
+}
+
+impl Debug for HttpCore {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        f.debug_struct("HttpCore")
+            .field("endpoint", &self.endpoint)
+            .field("root", &self.root)
+            .finish()
+    }
+}
+
+impl HttpCore {
+    pub fn has_authorization(&self) -> bool {
+        self.authorization.is_some()
+    }
+
+    pub fn http_get_request(
+        &self,
+        path: &str,
+        range: BytesRange,
+        args: &OpRead,
+    ) -> Result<Request<Buffer>> {
+        let p = build_rooted_abs_path(&self.root, path);
+
+        let url = format!("{}{}", self.endpoint, percent_encode_path(&p));
+
+        let mut req = Request::get(&url);
+
+        if let Some(if_match) = args.if_match() {
+            req = req.header(IF_MATCH, if_match);
+        }
+
+        if let Some(if_none_match) = args.if_none_match() {
+            req = req.header(IF_NONE_MATCH, if_none_match);
+        }
+
+        if let Some(auth) = &self.authorization {
+            req = req.header(header::AUTHORIZATION, auth.clone())
+        }
+
+        if !range.is_full() {
+            req = req.header(header::RANGE, range.to_header());
+        }
+
+        req.body(Buffer::new()).map_err(new_request_build_error)
+    }
+
+    pub async fn http_get(
+        &self,
+        path: &str,
+        range: BytesRange,
+        args: &OpRead,
+    ) -> Result<Response<HttpBody>> {
+        let req = self.http_get_request(path, range, args)?;
+        self.info.http_client().fetch(req).await
+    }
+
+    pub fn http_head_request(&self, path: &str, args: &OpStat) -> 
Result<Request<Buffer>> {
+        let p = build_rooted_abs_path(&self.root, path);
+
+        let url = format!("{}{}", self.endpoint, percent_encode_path(&p));
+
+        let mut req = Request::head(&url);
+
+        if let Some(if_match) = args.if_match() {
+            req = req.header(IF_MATCH, if_match);
+        }
+
+        if let Some(if_none_match) = args.if_none_match() {
+            req = req.header(IF_NONE_MATCH, if_none_match);
+        }
+
+        if let Some(auth) = &self.authorization {
+            req = req.header(header::AUTHORIZATION, auth.clone())
+        }
+
+        req.body(Buffer::new()).map_err(new_request_build_error)
+    }
+
+    pub async fn http_head(&self, path: &str, args: &OpStat) -> 
Result<Response<Buffer>> {
+        let req = self.http_head_request(path, args)?;
+        self.info.http_client().send(req).await
+    }
+}
diff --git a/core/src/services/http/mod.rs b/core/src/services/http/mod.rs
index 4e222e9e1..fef894ef1 100644
--- a/core/src/services/http/mod.rs
+++ b/core/src/services/http/mod.rs
@@ -21,6 +21,8 @@ mod error;
 #[cfg(feature = "services-http")]
 mod backend;
 #[cfg(feature = "services-http")]
+mod core;
+#[cfg(feature = "services-http")]
 pub use backend::HttpBuilder as Http;
 
 mod config;

Reply via email to