You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@opendal.apache.org by xu...@apache.org on 2025/04/15 10:40:42 UTC

(opendal) 01/01: feat(core): Use Buffer as http_body::Body directly

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

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

commit 47990b55c8ce883a779f5cf9d2546ffa9c58d08e
Author: Xuanwo <gi...@xuanwo.io>
AuthorDate: Tue Apr 15 18:40:14 2025 +0800

    feat(core): Use Buffer as http_body::Body directly
    
    Signed-off-by: Xuanwo <gi...@xuanwo.io>
---
 core/Cargo.lock                  |  1 +
 core/Cargo.toml                  |  5 ++---
 core/src/raw/http_util/client.rs | 34 +++++++++++++++++++++++++++++++++-
 3 files changed, 36 insertions(+), 4 deletions(-)

diff --git a/core/Cargo.lock b/core/Cargo.lock
index 6623bcd50..7a90449cf 100644
--- a/core/Cargo.lock
+++ b/core/Cargo.lock
@@ -5441,6 +5441,7 @@ dependencies = [
  "hdrs",
  "hmac",
  "http 1.3.1",
+ "http-body 1.0.1",
  "js-sys",
  "libtest-mimic",
  "log",
diff --git a/core/Cargo.toml b/core/Cargo.toml
index ce7d322e6..45141d418 100644
--- a/core/Cargo.toml
+++ b/core/Cargo.toml
@@ -252,13 +252,12 @@ futures = { version = "0.3", default-features = false, features = [
 ] }
 ghac = { version = "0.2.0", optional = true }
 http = "1.1"
+http-body = "1"
 log = "0.4"
 md-5 = "0.10"
 percent-encoding = "2"
 quick-xml = { version = "0.37", features = ["serialize", "overlapped-lists"] }
-reqwest = { version = "0.12.2", features = [
-  "stream",
-], default-features = false }
+reqwest = { version = "0.12.8", default-features = false }
 serde = { version = "1", features = ["derive"] }
 serde_json = "1"
 tokio = { version = "1.27", features = ["sync", "io-util"] }
diff --git a/core/src/raw/http_util/client.rs b/core/src/raw/http_util/client.rs
index 60038b8f2..ada330ca9 100644
--- a/core/src/raw/http_util/client.rs
+++ b/core/src/raw/http_util/client.rs
@@ -15,18 +15,25 @@
 // specific language governing permissions and limitations
 // under the License.
 
+use std::convert::Infallible;
 use std::fmt::Debug;
 use std::fmt::Formatter;
 use std::future;
 use std::mem;
 use std::ops::Deref;
+use std::pin::Pin;
 use std::str::FromStr;
 use std::sync::Arc;
+use std::task::Context;
+use std::task::Poll;
 
+use bytes::Bytes;
 use futures::Future;
 use futures::TryStreamExt;
 use http::Request;
 use http::Response;
+use http_body::Frame;
+use http_body::SizeHint;
 use raw::oio::Read;
 use std::sync::LazyLock;
 
@@ -171,7 +178,7 @@ impl HttpFetch for reqwest::Client {
         if !body.is_empty() {
             #[cfg(not(target_arch = "wasm32"))]
             {
-                req_builder = req_builder.body(reqwest::Body::wrap_stream(body))
+                req_builder = req_builder.body(reqwest::Body::wrap(HttpBufferBody(body)))
             }
             #[cfg(target_arch = "wasm32")]
             {
@@ -240,3 +247,28 @@ fn is_temporary_error(err: &reqwest::Error) -> bool {
     // error decoding response body, for example, connection reset.
     err.is_decode()
 }
+
+struct HttpBufferBody(Buffer);
+
+impl http_body::Body for HttpBufferBody {
+    type Data = Bytes;
+    type Error = Infallible;
+
+    fn poll_frame(
+        mut self: Pin<&mut Self>,
+        _: &mut Context<'_>,
+    ) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
+        match self.0.next() {
+            Some(bs) => Poll::Ready(Some(Ok(Frame::data(bs)))),
+            None => Poll::Ready(None),
+        }
+    }
+
+    fn is_end_stream(&self) -> bool {
+        self.0.is_empty()
+    }
+
+    fn size_hint(&self) -> SizeHint {
+        SizeHint::with_exact(self.0.len() as u64)
+    }
+}