use crate::auth::AuthBuilder;
#[cfg(feature = "blocking")]
use crate::blocking::builder::ClientWithMiddleware as BlockingClientWithMiddleware;
#[cfg(test)]
use crate::builder::Middleware;
use crate::builder::{ArcPointer, BuilderError, ClientWithMiddleware, RequestBuilder};
use crate::config::{get_bucket, get_endpoint, get_env, BucketBase, Config, InvalidConfig};
use crate::consts::{TRUE1, TRUE2, TRUE3, TRUE4};
use crate::file::AlignBuilder;
use crate::types::{
object::{InvalidObjectPath, ObjectBase, ObjectPath},
BucketName, CanonicalizedResource, EndPoint, KeyId, KeySecret,
};
use chrono::{DateTime, Utc};
use http::{
header::{HeaderMap, HeaderName},
HeaderValue, Method,
};
use reqwest::Url;
use std::env;
#[cfg(all(feature = "blocking", test))]
use std::rc::Rc;
#[cfg(test)]
use std::sync::Arc;
use std::time::Duration;
#[non_exhaustive]
#[derive(Default, Debug, Clone, PartialEq, Eq)]
pub struct Client<M = ClientWithMiddleware> {
auth_builder: AuthBuilder,
client_middleware: M,
pub(crate) endpoint: EndPoint,
pub(crate) bucket: BucketName,
timeout: Option<Duration>,
}
impl<M> AsMut<Option<Duration>> for Client<M> {
fn as_mut(&mut self) -> &mut Option<Duration> {
&mut self.timeout
}
}
impl<M> AsRef<EndPoint> for Client<M> {
fn as_ref(&self) -> &EndPoint {
&self.endpoint
}
}
impl<M> AsRef<BucketName> for Client<M> {
fn as_ref(&self) -> &BucketName {
&self.bucket
}
}
impl<M> AsMut<BucketName> for Client<M> {
fn as_mut(&mut self) -> &mut BucketName {
&mut self.bucket
}
}
impl<M: Default> Client<M> {
pub fn new(
access_key_id: KeyId,
access_key_secret: KeySecret,
endpoint: EndPoint,
bucket: BucketName,
) -> Self {
let mut auth_builder = AuthBuilder::default();
auth_builder.key(access_key_id);
auth_builder.secret(access_key_secret);
Self::from_builder(auth_builder, endpoint, bucket)
}
#[cfg(test)]
pub fn test_init() -> Self {
Self::new(
"foo1".into(),
"foo2".into(),
EndPoint::CN_QINGDAO,
"bar".try_into().unwrap(),
)
}
pub fn from_config(config: Config) -> Self {
let (key, secret, bucket, endpoint) = config.get_all();
let mut auth_builder = AuthBuilder::default();
auth_builder.key(key);
auth_builder.secret(secret);
Self::from_builder(auth_builder, endpoint, bucket)
}
pub fn from_env() -> Result<Self, InvalidConfig> {
let key_id = get_env("ALIYUN_KEY_ID")?;
let key_secret = get_env("ALIYUN_KEY_SECRET")?;
let endpoint = get_env("ALIYUN_ENDPOINT")?;
let bucket = get_env("ALIYUN_BUCKET")?;
let mut auth_builder = AuthBuilder::default();
auth_builder.key(key_id);
auth_builder.secret(key_secret);
let mut endpoint = get_endpoint(&endpoint)?;
if let Ok(is_internal) = env::var("ALIYUN_OSS_INTERNAL") {
if is_internal == TRUE1
|| is_internal == TRUE2
|| is_internal == TRUE3
|| is_internal == TRUE4
{
endpoint.set_internal(true);
}
}
Ok(Self::from_builder(
auth_builder,
endpoint,
get_bucket(&bucket)?,
))
}
#[doc(hidden)]
#[inline]
pub fn from_builder(auth_builder: AuthBuilder, endpoint: EndPoint, bucket: BucketName) -> Self {
Self {
auth_builder,
client_middleware: M::default(),
endpoint,
bucket,
timeout: None,
}
}
}
impl<M> Client<M> {
pub(crate) fn get_bucket_name(&self) -> &BucketName {
&self.bucket
}
pub fn get_bucket_base(&self) -> BucketBase {
BucketBase::new(self.bucket.to_owned(), self.endpoint.to_owned())
}
pub fn get_bucket_url(&self) -> Url {
self.get_bucket_base().to_url()
}
pub(crate) fn get_key(&self) -> &KeyId {
self.auth_builder.get_key()
}
pub(crate) fn get_secret(&self) -> &KeySecret {
self.auth_builder.get_secret()
}
pub fn get_endpoint_url(&self) -> Url {
self.endpoint.to_url()
}
pub fn set_bucket(&mut self, bucket: BucketName) {
self.bucket = bucket;
}
pub fn set_endpoint(&mut self, endpoint: EndPoint) {
self.endpoint = endpoint;
}
pub fn timeout(&mut self, timeout: Duration) {
self.timeout = Some(timeout);
}
#[inline]
pub fn get_object_base<P>(&self, path: P) -> Result<ObjectBase, InvalidObjectPath>
where
P: TryInto<ObjectPath>,
P::Error: Into<InvalidObjectPath>,
{
ObjectBase::<ArcPointer>::from_bucket(self.get_bucket_base(), path)
}
}
#[cfg(not(test))]
#[inline]
fn now() -> DateTime<Utc> {
Utc::now()
}
#[cfg(test)]
fn now() -> DateTime<Utc> {
use chrono::NaiveDateTime;
let naive = NaiveDateTime::parse_from_str("2022/10/6 20:40:00", "%Y/%m/%d %H:%M:%S").unwrap();
DateTime::from_utc(naive, Utc)
}
pub type ClientArc = Client<ClientWithMiddleware>;
impl Client {
#[cfg(test)]
pub(crate) fn middleware(mut self, middleware: Arc<dyn Middleware>) -> Self {
self.client_middleware.middleware(middleware);
self
}
}
impl AlignBuilder for Client<ClientWithMiddleware> {
fn builder_with_header<H: IntoIterator<Item = (HeaderName, HeaderValue)>>(
&self,
method: Method,
url: Url,
resource: CanonicalizedResource,
headers: H,
) -> Result<RequestBuilder, BuilderError> {
let mut auth_builder = self.auth_builder.clone();
auth_builder.method(&method);
auth_builder.date(now());
auth_builder.canonicalized_resource(resource);
auth_builder.extend_headers(HeaderMap::from_iter(headers));
let mut builder = self
.client_middleware
.request(method, url)
.headers(auth_builder.get_headers()?);
if let Some(timeout) = self.timeout {
builder = builder.timeout(timeout);
};
Ok(builder)
}
}
#[cfg(all(feature = "blocking", test))]
use crate::blocking::builder::Middleware as BlockingMiddleware;
#[cfg(feature = "blocking")]
use crate::blocking::builder::RequestBuilder as BlockingRequestBuilder;
#[cfg(feature = "blocking")]
pub type ClientRc = Client<BlockingClientWithMiddleware>;
#[cfg(feature = "blocking")]
impl Client<BlockingClientWithMiddleware> {
#[cfg(test)]
pub(crate) fn middleware(mut self, middleware: Rc<dyn BlockingMiddleware>) -> Self {
self.client_middleware.middleware(middleware);
self
}
}
#[cfg(feature = "blocking")]
impl crate::file::blocking::AlignBuilder for Client<BlockingClientWithMiddleware> {
#[inline]
fn builder_with_header<H: IntoIterator<Item = (HeaderName, HeaderValue)>>(
&self,
method: Method,
url: Url,
resource: CanonicalizedResource,
headers: H,
) -> Result<BlockingRequestBuilder, BuilderError> {
let method = method;
let mut auth_builder = self.auth_builder.clone();
auth_builder.method(&method);
auth_builder.date(now());
auth_builder.canonicalized_resource(resource);
auth_builder.extend_headers(HeaderMap::from_iter(headers));
let mut builder = self
.client_middleware
.request(method, url)
.headers(auth_builder.get_headers()?);
if let Some(timeout) = self.timeout {
builder = builder.timeout(timeout);
};
Ok(builder)
}
}
#[cfg(test)]
mod tests {
use http::Method;
use super::*;
use crate::{
builder::ArcPointer,
config::{BucketBase, Config},
file::AlignBuilder,
types::object::ObjectBase,
BucketName,
};
use std::time::Duration;
#[test]
fn from_config() {
let config = Config::try_new("foo1", "foo2", "qingdao", "foo4").unwrap();
let client = ClientArc::from_config(config);
assert_eq!(client.bucket, "foo4".parse::<BucketName>().unwrap());
}
#[test]
fn timeout() {
let config = Config::try_new("foo1", "foo2", "qingdao", "foo4").unwrap();
let mut client = ClientArc::from_config(config);
assert!(client.timeout.is_none());
client.timeout(Duration::new(10, 0));
assert!(client.timeout.is_some());
assert_eq!(client.timeout, Some(Duration::new(10, 0)));
}
#[test]
fn test_timeout_with_builder() {
let mut client = ClientArc::test_init();
client.timeout(Duration::new(11, 0));
let (url, resource) = client
.get_object_base("9AB932LY.jpeg")
.unwrap()
.get_url_resource([]);
let builder = client.builder_with_header(Method::HEAD, url, resource, []);
let builder = builder.unwrap();
let request = builder.build().unwrap();
let timeout = request.timeout().unwrap().to_owned();
assert_eq!(timeout, Duration::new(11, 0));
}
#[cfg(feature = "blocking")]
#[test]
fn test_timeout_with_builder_blocking() {
use crate::file::blocking::AlignBuilder;
let mut client = ClientRc::test_init();
client.timeout(Duration::new(11, 0));
let (url, resource) = client
.get_object_base("9AB932LY.jpeg")
.unwrap()
.get_url_resource([]);
let builder = client.builder_with_header(Method::HEAD, url, resource, []);
let builder = builder.unwrap();
let request = builder.build().unwrap();
let timeout = request.timeout().unwrap().to_owned();
assert_eq!(timeout, Duration::new(11, 0));
}
#[test]
fn get_object_base() {
use std::sync::Arc;
let config = Config::try_new("foo1", "foo2", "qingdao", "foo4").unwrap();
let client = ClientArc::from_config(config);
let base = client.get_object_base("file111").unwrap();
let base2 = ObjectBase::<ArcPointer>::new(
Arc::new(BucketBase::new(
"foo4".parse().unwrap(),
"qingdao".parse().unwrap(),
)),
"file111",
)
.unwrap();
assert!(base == base2);
}
}