From 9af351debc5e7deff0ed1be2fa67729ba235de36 Mon Sep 17 00:00:00 2001 From: suyanhanx Date: Sat, 22 Apr 2023 21:10:27 +0800 Subject: [PATCH] set test for read_with_override_content_disposition Signed-off-by: suyanhanx --- core/src/services/azblob/backend.rs | 10 +++-- core/src/services/s3/backend.rs | 16 +++++++- core/src/services/s3/core.rs | 11 +++++- core/src/types/capability.rs | 14 +++---- core/src/types/operator/metadata.rs | 5 +++ core/tests/behavior/write.rs | 58 +++++++++++++++++++++++++++++ 6 files changed, 101 insertions(+), 13 deletions(-) diff --git a/core/src/services/azblob/backend.rs b/core/src/services/azblob/backend.rs index f40b6f43997..be81ac2541d 100644 --- a/core/src/services/azblob/backend.rs +++ b/core/src/services/azblob/backend.rs @@ -452,16 +452,20 @@ impl Accessor for AzblobBackend { .set_root(&self.core.root) .set_name(&self.core.container) .set_capability(Capability { + stat: true, + stat_with_if_match: true, + stat_with_if_none_match: true, + read: true, read_can_next: true, read_with_if_match: true, read_with_if_none_match: true, - stat: true, - stat_with_if_match: true, - stat_with_if_none_match: true, + read_with_override_content_disposition: true, + write: true, write_with_content_type: true, write_with_cache_control: true, + delete: true, create_dir: true, list: true, diff --git a/core/src/services/s3/backend.rs b/core/src/services/s3/backend.rs index a8de40ae690..3ebba114dc8 100644 --- a/core/src/services/s3/backend.rs +++ b/core/src/services/s3/backend.rs @@ -912,8 +912,16 @@ impl Accessor for S3Backend { .set_root(&self.core.root) .set_name(&self.core.bucket) .set_capability(Capability { + stat: true, + stat_with_if_match: true, + stat_with_if_none_match: true, + read: true, read_can_next: true, + read_with_if_match: true, + read_with_if_none_match: true, + read_with_override_content_disposition: true, + write: true, list: true, scan: true, @@ -951,7 +959,13 @@ impl Accessor for S3Backend { async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, Self::Reader)> { let resp = self .core - .s3_get_object(path, args.range(), args.if_none_match(), args.if_match()) + .s3_get_object( + path, + args.range(), + args.if_none_match(), + args.if_match(), + args.override_content_disposition(), + ) .await?; let status = resp.status(); diff --git a/core/src/services/s3/core.rs b/core/src/services/s3/core.rs index db16c35577a..120d6ec311a 100644 --- a/core/src/services/s3/core.rs +++ b/core/src/services/s3/core.rs @@ -296,9 +296,16 @@ impl S3Core { range: BytesRange, if_none_match: Option<&str>, if_match: Option<&str>, + override_content_disposition: Option<&str>, ) -> Result> { - let mut req = - self.s3_get_object_request(path, range, None, None, if_none_match, if_match)?; + let mut req = self.s3_get_object_request( + path, + range, + override_content_disposition, + None, + if_none_match, + if_match, + )?; self.sign(&mut req).await?; diff --git a/core/src/types/capability.rs b/core/src/types/capability.rs index 1259643e926..db2f7af6b18 100644 --- a/core/src/types/capability.rs +++ b/core/src/types/capability.rs @@ -44,6 +44,13 @@ /// - Operation with limtations should be named like `batch_max_operations`. #[derive(Copy, Clone, Debug, Default)] pub struct Capability { + /// If operator supports stat natively, it will be true. + pub stat: bool, + /// If operator supports stat with if match natively, it will be true. + pub stat_with_if_match: bool, + /// If operator supports stat with if none match natively, it will be true. + pub stat_with_if_none_match: bool, + /// If operator supports read natively, it will be true. pub read: bool, /// If operator supports seek on returning reader natively, it will @@ -63,13 +70,6 @@ pub struct Capability { /// if operator supports read with override content disposition natively, it will be true. pub read_with_override_content_disposition: bool, - /// If operator supports stat natively, it will be true. - pub stat: bool, - /// If operator supports stat with if match natively, it will be true. - pub stat_with_if_match: bool, - /// If operator supports stat with if none match natively, it will be true. - pub stat_with_if_none_match: bool, - /// If operator supports write natively, it will be true. pub write: bool, /// If operator supports write with without content length, it will diff --git a/core/src/types/operator/metadata.rs b/core/src/types/operator/metadata.rs index 599ac753262..240e7305b99 100644 --- a/core/src/types/operator/metadata.rs +++ b/core/src/types/operator/metadata.rs @@ -47,6 +47,11 @@ impl OperatorInfo { self.0.name() } + /// Get [`Capability`] of operator. + pub fn capability(&self) -> Capability { + self.0.capability() + } + /// Check if current backend supports [`Accessor::read`] or not. pub fn can_read(&self) -> bool { self.0.capability().read diff --git a/core/tests/behavior/write.rs b/core/tests/behavior/write.rs index 10082b57a14..7dffcb24461 100644 --- a/core/tests/behavior/write.rs +++ b/core/tests/behavior/write.rs @@ -21,11 +21,15 @@ use futures::AsyncSeekExt; use futures::StreamExt; use log::debug; use log::warn; +use opendal::ops::OpRead; use opendal::EntryMode; use opendal::ErrorKind; use opendal::Operator; +use reqwest::Url; use sha2::Digest; use sha2::Sha256; +use std::str::FromStr; +use std::time::Duration; use super::utils::*; @@ -92,6 +96,7 @@ macro_rules! behavior_write_tests { test_fuzz_part_reader, test_read_with_dir_path, test_read_with_special_chars, + test_read_with_override_content_disposition, test_delete, test_delete_empty_dir, test_delete_with_special_chars, @@ -580,6 +585,59 @@ pub async fn test_read_with_special_chars(op: Operator) -> Result<()> { Ok(()) } +// Read file with override_content_disposition should succeed. +pub async fn test_read_with_override_content_disposition(op: Operator) -> Result<()> { + if !(op + .info() + .capability() + .read_with_override_content_disposition + && op.info().can_presign()) + { + return Ok(()); + } + + let path = uuid::Uuid::new_v4().to_string(); + let (content, _) = gen_bytes(); + + op.write(&path, content.clone()) + .await + .expect("write must succeed"); + + let target_content_disposition = "attachment; filename=foo.txt"; + + let mut op_read = OpRead::default(); + op_read = op_read.with_override_content_disposition(target_content_disposition); + + let signed_req = op + .presign_read_with(&path, op_read, Duration::from_secs(60)) + .await + .expect("presign must succeed"); + + let client = reqwest::Client::new(); + let mut req = client.request( + signed_req.method().clone(), + Url::from_str(&signed_req.uri().to_string()).expect("must be valid url"), + ); + for (k, v) in signed_req.header() { + req = req.header(k, v); + } + + let resp = req.send().await.expect("send must succeed"); + + assert_eq!(resp.status(), http::StatusCode::OK); + assert_eq!( + resp.headers() + .get(http::header::CONTENT_DISPOSITION) + .unwrap(), + target_content_disposition + ); + assert_eq!(resp.bytes().await?, content); + + op.delete(&path).await.expect("delete must succeed"); + + Ok(()) +} + // Delete existing file should succeed. pub async fn test_writer_abort(op: Operator) -> Result<()> { let path = uuid::Uuid::new_v4().to_string();