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 1ecd69996 feat(services/onedrive): implement read_with_if_none_match 
(#5763)
1ecd69996 is described below

commit 1ecd69996b28da5238899eda37f71cd0e3a8455a
Author: Erick Guan <[email protected]>
AuthorDate: Sat Mar 15 03:55:05 2025 +0100

    feat(services/onedrive): implement read_with_if_none_match (#5763)
    
    * feat(services/onedrive): implement read_with_if_none_match
    
    * fixup! feat(services/onedrive): implement read_with_if_none_match
    
    * chore: improve http client documentation
---
 core/src/raw/http_util/body.rs        |  6 +++---
 core/src/raw/http_util/client.rs      | 12 +++++++++---
 core/src/services/onedrive/backend.rs |  2 +-
 core/src/services/onedrive/builder.rs |  2 ++
 core/src/services/onedrive/core.rs    | 16 ++++++++++++++--
 core/src/services/onedrive/error.rs   |  1 +
 6 files changed, 30 insertions(+), 9 deletions(-)

diff --git a/core/src/raw/http_util/body.rs b/core/src/raw/http_util/body.rs
index 313a2bca1..268915f88 100644
--- a/core/src/raw/http_util/body.rs
+++ b/core/src/raw/http_util/body.rs
@@ -24,10 +24,10 @@ use oio::Read;
 use crate::raw::*;
 use crate::*;
 
-/// HttpBody is the streaming body that opendal's HttpClient returned.
+/// The streaming body that OpenDAL's HttpClient returned.
 ///
-/// It implements the `oio::Read` trait, service implementors can return it as
-/// `Access::Read`.
+/// We implement [`oio::Read`] for the `HttpBody`. Services can use `HttpBody` 
as
+/// [`Access::Read`].
 pub struct HttpBody {
     #[cfg(not(target_arch = "wasm32"))]
     stream: Box<dyn Stream<Item = Result<Buffer>> + Send + Sync + Unpin + 
'static>,
diff --git a/core/src/raw/http_util/client.rs b/core/src/raw/http_util/client.rs
index f0943b295..117973194 100644
--- a/core/src/raw/http_util/client.rs
+++ b/core/src/raw/http_util/client.rs
@@ -45,7 +45,11 @@ pub(crate) static GLOBAL_REQWEST_CLIENT: 
Lazy<reqwest::Client> = Lazy::new(reqwe
 /// HttpFetcher is a type erased [`HttpFetch`].
 pub type HttpFetcher = Arc<dyn HttpFetchDyn>;
 
-/// HttpClient that used across opendal.
+/// A HTTP client instance for OpenDAL's services.
+///
+/// # Notes
+///
+/// * A http client must support redirections that follows 3xx response.
 #[derive(Clone)]
 pub struct HttpClient {
     fetcher: HttpFetcher,
@@ -88,14 +92,16 @@ impl HttpClient {
         Ok(Self { fetcher })
     }
 
-    /// Send a request in async way.
+    /// Send a request and consume response.
     pub async fn send(&self, req: Request<Buffer>) -> Result<Response<Buffer>> 
{
         let (parts, mut body) = self.fetch(req).await?.into_parts();
         let buffer = body.read_all().await?;
         Ok(Response::from_parts(parts, buffer))
     }
 
-    /// Fetch a request in async way.
+    /// Fetch a request and return a streamable [`HttpBody`].
+    ///
+    /// Services can use [`HttpBody`] as [`Access::Read`].
     pub async fn fetch(&self, req: Request<Buffer>) -> 
Result<Response<HttpBody>> {
         self.fetcher.fetch(req).await
     }
diff --git a/core/src/services/onedrive/backend.rs 
b/core/src/services/onedrive/backend.rs
index 48d52673d..75843a4ef 100644
--- a/core/src/services/onedrive/backend.rs
+++ b/core/src/services/onedrive/backend.rs
@@ -80,7 +80,7 @@ impl Access for OnedriveBackend {
     }
 
     async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, 
Self::Reader)> {
-        let response = self.core.onedrive_get_content(path, 
args.range()).await?;
+        let response = self.core.onedrive_get_content(path, &args).await?;
 
         let status = response.status();
         match status {
diff --git a/core/src/services/onedrive/builder.rs 
b/core/src/services/onedrive/builder.rs
index 42ed8b096..4e88e0c65 100644
--- a/core/src/services/onedrive/builder.rs
+++ b/core/src/services/onedrive/builder.rs
@@ -144,6 +144,8 @@ impl Builder for OnedriveBuilder {
             .set_root(&root)
             .set_native_capability(Capability {
                 read: true,
+                read_with_if_none_match: true,
+
                 write: true,
 
                 stat: true,
diff --git a/core/src/services/onedrive/core.rs 
b/core/src/services/onedrive/core.rs
index 31229b52f..bd0cef0a0 100644
--- a/core/src/services/onedrive/core.rs
+++ b/core/src/services/onedrive/core.rs
@@ -136,10 +136,19 @@ impl OneDriveCore {
         self.info.http_client().send(request).await
     }
 
+    /// Download a file
+    ///
+    /// OneDrive handles a download in 2 steps:
+    /// 1. Returns a 302 with a presigned URL. If `If-None-Match` succeed, 
returns 304.
+    /// 2. With the presigned URL, we can send a GET:
+    ///   1. When getting an item succeed with a `Range` header, we get a 206 
Partial Content response.
+    ///   2. When succeed, we get a 200 response.
+    ///
+    /// Read more at 
https://learn.microsoft.com/en-us/graph/api/driveitem-get-content
     pub(crate) async fn onedrive_get_content(
         &self,
         path: &str,
-        range: BytesRange,
+        args: &OpRead,
     ) -> Result<Response<HttpBody>> {
         let path = build_rooted_abs_path(&self.root, path);
         let url: String = format!(
@@ -148,7 +157,10 @@ impl OneDriveCore {
             percent_encode_path(&path),
         );
 
-        let request = Request::get(&url).header(header::RANGE, 
range.to_header());
+        let mut request = Request::get(&url).header(header::RANGE, 
args.range().to_header());
+        if let Some(etag) = args.if_none_match() {
+            request = request.header(header::IF_NONE_MATCH, etag);
+        }
 
         let mut request = request
             .body(Buffer::new())
diff --git a/core/src/services/onedrive/error.rs 
b/core/src/services/onedrive/error.rs
index 22073aaf8..e5a6585de 100644
--- a/core/src/services/onedrive/error.rs
+++ b/core/src/services/onedrive/error.rs
@@ -34,6 +34,7 @@ pub(super) fn parse_error(response: Response<Buffer>) -> 
Error {
         | StatusCode::BAD_GATEWAY
         | StatusCode::SERVICE_UNAVAILABLE
         | StatusCode::GATEWAY_TIMEOUT => (ErrorKind::Unexpected, true),
+        StatusCode::NOT_MODIFIED => (ErrorKind::ConditionNotMatch, false),
         _ => (ErrorKind::Unexpected, false),
     };
 

Reply via email to