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),
};