Skip to content

Commit 78ebd60

Browse files
committed
Separated http_client and shared client items in Client
1 parent fd4c725 commit 78ebd60

19 files changed

+344
-235
lines changed

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,9 @@ name = "file_downloader"
7676
[[example]]
7777
name = "object_prompt"
7878

79+
[[example]]
80+
name = "append_object"
81+
7982
[[bench]]
8083
name = "s3-api"
8184
path = "benches/s3/api_benchmarks.rs"

examples/append_object.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// MinIO Rust Library for Amazon S3 Compatible Cloud Storage
2+
// Copyright 2025 MinIO, Inc.
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
mod common;
17+
18+
use crate::common::{create_bucket_if_not_exists, create_client_on_localhost};
19+
use minio::s3::Client;
20+
use minio::s3::response::{AppendObjectResponse, StatObjectResponse};
21+
use minio::s3::segmented_bytes::SegmentedBytes;
22+
use minio::s3::types::S3Api;
23+
use rand::Rng;
24+
use rand::distributions::Alphanumeric;
25+
26+
#[tokio::main]
27+
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
28+
env_logger::init(); // Note: set environment variable RUST_LOG="INFO" to log info and higher
29+
let client: Client = create_client_on_localhost()?;
30+
31+
if !client.is_minio_express() {
32+
println!("Need (MinIO) Express mode to run this example");
33+
return Ok(());
34+
}
35+
36+
let bucket_name: &str = "append-test-bucket";
37+
create_bucket_if_not_exists(bucket_name, &client).await?;
38+
39+
let object_name: &str = "append-test-object";
40+
41+
let n_segments = 1000;
42+
let segment_size = 1024 * 1024; // 1 KB
43+
let mut offset_bytes = 0;
44+
45+
for i in 0..n_segments {
46+
let rand_str: String = random_string(segment_size);
47+
48+
let data_size = rand_str.len() as u64;
49+
let data: SegmentedBytes = SegmentedBytes::from(rand_str);
50+
51+
let resp: AppendObjectResponse = client
52+
.append_object(bucket_name, object_name, data, offset_bytes)
53+
.send()
54+
.await?;
55+
56+
offset_bytes += data_size;
57+
if resp.object_size != offset_bytes {
58+
panic!(
59+
"from the append_object: size mismatch: expected {}, got {}",
60+
resp.object_size, offset_bytes
61+
)
62+
}
63+
//println!("Append response: {:#?}", resp);
64+
65+
let resp: StatObjectResponse = client.stat_object(bucket_name, object_name).send().await?;
66+
if resp.size != offset_bytes {
67+
panic!(
68+
"from the stat_Object: size mismatch: expected {}, got {}",
69+
resp.size, offset_bytes
70+
)
71+
}
72+
println!("{}/{}", i, n_segments);
73+
//println!("Stat response: {:#?}", resp);
74+
}
75+
76+
Ok(())
77+
}
78+
79+
fn random_string(len: usize) -> String {
80+
rand::thread_rng()
81+
.sample_iter(&Alphanumeric)
82+
.take(len)
83+
.map(char::from)
84+
.collect()
85+
}

examples/common.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,18 @@ pub fn create_client_on_play() -> Result<Client, Box<dyn std::error::Error + Sen
2121
Ok(client)
2222
}
2323

24+
pub fn create_client_on_localhost() -> Result<Client, Box<dyn std::error::Error + Send + Sync>> {
25+
let base_url = "http://localhost:9000/".parse::<BaseUrl>()?;
26+
log::info!("Trying to connect to MinIO at: `{:?}`", base_url);
27+
28+
let static_provider = StaticProvider::new("minioadmin", "minioadmin", None);
29+
30+
let client = ClientBuilder::new(base_url.clone())
31+
.provider(Some(Box::new(static_provider)))
32+
.build()?;
33+
Ok(client)
34+
}
35+
2436
pub async fn create_bucket_if_not_exists(
2537
bucket_name: &str,
2638
client: &Client,

src/s3/builders/copy_object.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,8 @@ impl ToS3Request for UploadPartCopy {
108108

109109
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
110110
{
111-
query_params.insert("partNumber".into(), self.part_number.to_string());
112-
query_params.insert("uploadId".into(), self.upload_id);
111+
query_params.add("partNumber", self.part_number.to_string());
112+
query_params.add("uploadId", self.upload_id);
113113
}
114114

115115
Ok(S3Request::new(self.client, Method::PUT)

src/s3/builders/get_presigned_object_url.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
use crate::s3::Client;
1717
use crate::s3::client::DEFAULT_EXPIRY_SECONDS;
18+
use crate::s3::creds::Credentials;
1819
use crate::s3::error::Error;
1920
use crate::s3::multimap::{Multimap, MultimapExt};
2021
use crate::s3::response::GetPresignedObjectUrlResponse;
@@ -60,18 +61,18 @@ impl GetPresignedObjectUrl {
6061
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
6162
query_params.add_version(self.version_id.clone());
6263

63-
let mut url = self.client.inner.base_url.build_url(
64+
let mut url = self.client.shared.base_url.build_url(
6465
&self.method,
6566
&region,
6667
&query_params,
6768
Some(&self.bucket),
6869
Some(&self.object),
6970
)?;
7071

71-
if let Some(p) = &self.client.inner.provider {
72-
let creds = p.fetch();
72+
if let Some(p) = &self.client.shared.provider {
73+
let creds: Credentials = p.fetch();
7374
if let Some(t) = creds.session_token {
74-
query_params.insert("X-Amz-Security-Token".into(), t);
75+
query_params.add("X-Amz-Security-Token", t);
7576
}
7677

7778
let date = match self.request_time {

src/s3/builders/get_presigned_policy_form_data.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ impl GetPresignedPolicyFormData {
4040
.client
4141
.get_region_cached(&self.policy.bucket, &self.policy.region)?;
4242

43-
let creds: Credentials = self.client.inner.provider.as_ref().unwrap().fetch();
43+
let creds: Credentials = self.client.shared.provider.as_ref().unwrap().fetch();
4444
self.policy.form_data(
4545
creds.access_key,
4646
creds.secret_key,

src/s3/builders/list_objects.rs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use async_trait::async_trait;
1616
use futures_util::{Stream, StreamExt, stream as futures_stream};
1717
use http::Method;
1818

19-
use crate::s3::multimap::Multimap;
19+
use crate::s3::multimap::{Multimap, MultimapExt};
2020
use crate::s3::utils::insert;
2121
use crate::s3::{
2222
client::Client,
@@ -36,11 +36,11 @@ fn add_common_list_objects_query_params(
3636
max_keys: Option<u16>,
3737
prefix: Option<String>,
3838
) {
39-
query_params.insert("delimiter".into(), delimiter.unwrap_or("".into()));
40-
query_params.insert("max-keys".into(), max_keys.unwrap_or(1000).to_string());
41-
query_params.insert("prefix".into(), prefix.unwrap_or("".into()));
39+
query_params.add("delimiter", delimiter.unwrap_or("".into()));
40+
query_params.add("max-keys", max_keys.unwrap_or(1000).to_string());
41+
query_params.add("prefix", prefix.unwrap_or("".into()));
4242
if !disable_url_encoding {
43-
query_params.insert("encoding-type".into(), "url".into());
43+
query_params.add("encoding-type", "url");
4444
}
4545
}
4646

@@ -125,7 +125,7 @@ impl ToS3Request for ListObjectsV1 {
125125
self.prefix,
126126
);
127127
if let Some(v) = self.marker {
128-
query_params.insert("marker".into(), v);
128+
query_params.add("marker", v);
129129
}
130130
}
131131

@@ -221,7 +221,7 @@ impl ToS3Request for ListObjectsV2 {
221221

222222
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
223223
{
224-
query_params.insert("list-type".into(), "2".into());
224+
query_params.add("list-type", "2");
225225
add_common_list_objects_query_params(
226226
&mut query_params,
227227
self.delimiter,
@@ -230,19 +230,19 @@ impl ToS3Request for ListObjectsV2 {
230230
self.prefix,
231231
);
232232
if let Some(v) = self.continuation_token {
233-
query_params.insert("continuation-token".into(), v);
233+
query_params.add("continuation-token", v);
234234
}
235235
if self.fetch_owner {
236-
query_params.insert("fetch-owner".into(), "true".into());
236+
query_params.add("fetch-owner", "true");
237237
}
238238
if let Some(v) = self.start_after {
239-
query_params.insert("start-after".into(), v);
239+
query_params.add("start-after", v);
240240
}
241241
if self.include_user_metadata {
242-
query_params.insert("metadata".into(), "true".into());
242+
query_params.add("metadata", "true");
243243
}
244244
if self.unsorted {
245-
query_params.insert("unsorted".into(), "true".into());
245+
query_params.add("unsorted", "true");
246246
}
247247
}
248248

@@ -353,16 +353,16 @@ impl ToS3Request for ListObjectVersions {
353353
self.prefix,
354354
);
355355
if let Some(v) = self.key_marker {
356-
query_params.insert("key-marker".into(), v);
356+
query_params.add("key-marker", v);
357357
}
358358
if let Some(v) = self.version_id_marker {
359-
query_params.insert("version-id-marker".into(), v);
359+
query_params.add("version-id-marker", v);
360360
}
361361
if self.include_user_metadata {
362-
query_params.insert("metadata".into(), "true".into());
362+
query_params.add("metadata", "true");
363363
}
364364
if self.unsorted {
365-
query_params.insert("unsorted".into(), "true".into());
365+
query_params.add("unsorted", "true");
366366
}
367367
}
368368

src/s3/builders/listen_bucket_notification.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ use async_trait::async_trait;
1717
use futures_util::Stream;
1818
use http::Method;
1919

20-
use crate::s3::multimap::Multimap;
20+
use crate::s3::multimap::{Multimap, MultimapExt};
2121
use crate::s3::{
2222
client::Client,
2323
error::Error,
@@ -102,19 +102,19 @@ impl ToS3Request for ListenBucketNotification {
102102
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
103103
{
104104
if let Some(v) = self.prefix {
105-
query_params.insert("prefix".into(), v);
105+
query_params.add("prefix", v);
106106
}
107107
if let Some(v) = self.suffix {
108-
query_params.insert("suffix".into(), v);
108+
query_params.add("suffix", v);
109109
}
110110
if let Some(v) = self.events {
111111
for e in v.into_iter() {
112-
query_params.insert("events".into(), e);
112+
query_params.add("events", e);
113113
}
114114
} else {
115-
query_params.insert("events".into(), "s3:ObjectCreated:*".into());
116-
query_params.insert("events".into(), "s3:ObjectRemoved:*".into());
117-
query_params.insert("events".into(), "s3:ObjectAccessed:*".into());
115+
query_params.add("events", "s3:ObjectCreated:*");
116+
query_params.add("events", "s3:ObjectRemoved:*");
117+
query_params.add("events", "s3:ObjectAccessed:*");
118118
}
119119
}
120120

src/s3/builders/make_bucket.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,16 +75,12 @@ impl ToS3Request for MakeBucket {
7575
check_bucket_name(&self.bucket, true)?;
7676

7777
let region1: Option<&str> = self.region.as_deref();
78-
let region2: Option<&str> = if self.client.inner.base_url.region.is_empty() {
79-
None
80-
} else {
81-
Some(&self.client.inner.base_url.region)
82-
};
78+
let region2: Option<&str> = self.client.get_region_from_url();
8379

8480
let region_str: String = match (region1, region2) {
8581
(None, None) => DEFAULT_REGION.to_string(),
8682
(Some(_), None) => self.region.unwrap(),
87-
(None, Some(_)) => self.client.inner.base_url.region.clone(),
83+
(None, Some(v)) => v.to_string(),
8884
(Some(r1), Some(r2)) if r1 == r2 => self.region.unwrap(), // Both are Some and equal
8985
(Some(r1), Some(r2)) => {
9086
return Err(Error::RegionMismatch(r1.to_string(), r2.to_string()));

src/s3/builders/object_prompt.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ impl ToS3Request for ObjectPrompt {
105105
let mut query_params: Multimap = self.extra_query_params.unwrap_or_default();
106106
query_params.add_version(self.version_id);
107107

108-
query_params.insert(
109-
"lambdaArn".into(),
108+
query_params.add(
109+
"lambdaArn",
110110
self.lambda_arn
111111
.as_ref()
112112
.map(ToString::to_string)

0 commit comments

Comments
 (0)