Add builder style constructor for Client (#50)

This commit is contained in:
Aditya Manthramurthy 2023-09-26 18:22:37 -07:00 committed by GitHub
parent 4958c01f4c
commit e9aea2ada6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 101 additions and 36 deletions

View File

@ -21,7 +21,7 @@ jobs:
run: | run: |
cargo fmt --all -- --check cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -A clippy::result_large_err -A clippy::type_complexity -A clippy::too_many_arguments cargo clippy --all-targets --all-features -- -A clippy::result_large_err -A clippy::type_complexity -A clippy::too_many_arguments
cargo build --verbose cargo build --bins --examples --tests --benches --verbose
- name: Run tests - name: Run tests
run: | run: |

View File

@ -40,6 +40,7 @@ use std::collections::{HashMap, VecDeque};
use std::fs::File; use std::fs::File;
use std::io::prelude::*; use std::io::prelude::*;
use std::io::Read; use std::io::Read;
use std::path::{Path, PathBuf};
use std::sync::Arc; use std::sync::Arc;
use xmltree::Element; use xmltree::Element;
@ -203,6 +204,92 @@ fn parse_list_objects_common_prefixes(
Ok(()) Ok(())
} }
/// Client Builder manufactures a Client using given parameters.
#[derive(Debug, Default)]
pub struct ClientBuilder {
base_url: BaseUrl,
provider: Option<Arc<Box<(dyn Provider + Send + Sync + 'static)>>>,
ssl_cert_file: Option<PathBuf>,
ignore_cert_check: Option<bool>,
app_info: Option<(String, String)>,
}
impl ClientBuilder {
/// Creates a builder given a base URL for the MinIO service or other AWS S3
/// compatible object storage service.
pub fn new(base_url: BaseUrl) -> Self {
let mut c = ClientBuilder::default();
c.base_url = base_url;
c
}
/// Set the credential provider. If not set anonymous access is used.
pub fn provider(
mut self,
provider: Option<Box<(dyn Provider + Send + Sync + 'static)>>,
) -> Self {
self.provider = provider.map(Arc::new);
self
}
/// Set the app info as an Option of (app_name, app_version) pair. This will
/// show up in the client's user-agent.
pub fn app_info(mut self, app_info: Option<(String, String)>) -> Self {
self.app_info = app_info;
self
}
/// Set file for loading a trust certificate.
pub fn ssl_cert_file(mut self, ssl_cert_file: Option<&Path>) -> Self {
self.ssl_cert_file = ssl_cert_file.map(PathBuf::from);
self
}
/// Set flag to ignore certificate check.
pub fn ignore_cert_check(mut self, ignore_cert_check: Option<bool>) -> Self {
self.ignore_cert_check = ignore_cert_check;
self
}
/// Build the Client.
pub fn build(self) -> Result<Client, Error> {
let mut builder = reqwest::Client::builder().no_gzip();
let info = os_info::get();
let mut user_agent = String::from("MinIO (")
+ &info.os_type().to_string()
+ "; "
+ info.architecture().unwrap_or("unknown")
+ ") minio-rs/"
+ env!("CARGO_PKG_VERSION");
if let Some((app_name, app_version)) = self.app_info {
user_agent.push_str(format!(" {app_name}/{app_version}").as_str());
builder = builder.user_agent(user_agent);
}
if let Some(v) = self.ignore_cert_check {
builder = builder.danger_accept_invalid_certs(v);
}
if let Some(v) = self.ssl_cert_file {
let mut buf = Vec::new();
File::open(v)?.read_to_end(&mut buf)?;
let cert = reqwest::Certificate::from_pem(&buf)?;
builder = builder.add_root_certificate(cert);
}
let client = builder.build()?;
Ok(Client {
client,
base_url: self.base_url,
provider: self.provider,
region_map: DashMap::new(),
})
}
}
/// Simple Storage Service (aka S3) client to perform bucket and object operations. /// Simple Storage Service (aka S3) client to perform bucket and object operations.
/// ///
/// If credential provider is passed, all S3 operation requests are signed using /// If credential provider is passed, all S3 operation requests are signed using
@ -235,38 +322,14 @@ impl Client {
pub fn new( pub fn new(
base_url: BaseUrl, base_url: BaseUrl,
provider: Option<Box<(dyn Provider + Send + Sync + 'static)>>, provider: Option<Box<(dyn Provider + Send + Sync + 'static)>>,
ssl_cert_file: Option<String>, ssl_cert_file: Option<&Path>,
ignore_cert_check: Option<bool>, ignore_cert_check: Option<bool>,
) -> Result<Client, Error> { ) -> Result<Client, Error> {
let info = os_info::get(); ClientBuilder::new(base_url)
let user_agent = String::from("MinIO (") .provider(provider)
+ &info.os_type().to_string() .ssl_cert_file(ssl_cert_file)
+ "; " .ignore_cert_check(ignore_cert_check)
+ info.architecture().unwrap_or("unknown") .build()
+ ") minio-rs/"
+ env!("CARGO_PKG_VERSION");
let mut builder = reqwest::Client::builder()
.no_gzip()
.user_agent(user_agent.to_string());
if let Some(v) = ignore_cert_check {
builder = builder.danger_accept_invalid_certs(v);
}
if let Some(v) = ssl_cert_file {
let mut buf = Vec::new();
File::open(v.to_string())?.read_to_end(&mut buf)?;
let cert = reqwest::Certificate::from_pem(&buf)?;
builder = builder.add_root_certificate(cert);
}
let client = builder.build()?;
Ok(Client {
client,
base_url,
provider: provider.map(|v| Arc::new(v)),
region_map: DashMap::new(),
})
} }
fn build_headers( fn build_headers(

View File

@ -21,6 +21,7 @@ use rand::distributions::{Alphanumeric, DistString};
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
use std::collections::HashMap; use std::collections::HashMap;
use std::io::BufReader; use std::io::BufReader;
use std::path::{Path, PathBuf};
use std::{fs, io}; use std::{fs, io};
use tokio::sync::mpsc; use tokio::sync::mpsc;
@ -78,7 +79,7 @@ struct ClientTest {
access_key: String, access_key: String,
secret_key: String, secret_key: String,
ignore_cert_check: Option<bool>, ignore_cert_check: Option<bool>,
ssl_cert_file: Option<String>, ssl_cert_file: Option<PathBuf>,
client: Client, client: Client,
test_bucket: String, test_bucket: String,
} }
@ -92,7 +93,7 @@ impl ClientTest {
secret_key: String, secret_key: String,
static_provider: StaticProvider, static_provider: StaticProvider,
ignore_cert_check: Option<bool>, ignore_cert_check: Option<bool>,
ssl_cert_file: Option<String>, ssl_cert_file: Option<&Path>,
) -> ClientTest { ) -> ClientTest {
let client = Client::new( let client = Client::new(
base_url.clone(), base_url.clone(),
@ -107,7 +108,7 @@ impl ClientTest {
access_key, access_key,
secret_key, secret_key,
ignore_cert_check, ignore_cert_check,
ssl_cert_file, ssl_cert_file: ssl_cert_file.map(PathBuf::from),
client, client,
test_bucket: rand_bucket_name(), test_bucket: rand_bucket_name(),
} }
@ -537,10 +538,11 @@ impl ClientTest {
let listen_task = move || async move { let listen_task = move || async move {
let static_provider = StaticProvider::new(&access_key, &secret_key, None); let static_provider = StaticProvider::new(&access_key, &secret_key, None);
let ssl_cert_file = &ssl_cert_file;
let client = Client::new( let client = Client::new(
base_url, base_url,
Some(Box::new(static_provider)), Some(Box::new(static_provider)),
ssl_cert_file, ssl_cert_file.as_deref(),
ignore_cert_check, ignore_cert_check,
) )
.unwrap(); .unwrap();
@ -1150,7 +1152,7 @@ async fn s3_tests() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
let value = std::env::var("SSL_CERT_FILE")?; let value = std::env::var("SSL_CERT_FILE")?;
let mut ssl_cert_file = None; let mut ssl_cert_file = None;
if !value.is_empty() { if !value.is_empty() {
ssl_cert_file = Some(value); ssl_cert_file = Some(Path::new(&value));
} }
let ignore_cert_check = std::env::var("IGNORE_CERT_CHECK").is_ok(); let ignore_cert_check = std::env::var("IGNORE_CERT_CHECK").is_ok();
let region = std::env::var("SERVER_REGION").ok(); let region = std::env::var("SERVER_REGION").ok();