1use std::{collections::HashMap, str::FromStr, time::Duration};
2
3use crate::{
4 AuthorizationService,
5 api::{
6 BaseResponse, ByteStream, GenPrivateUrlRequestBuilder, HeadFileRequestBuilder,
7 MultipartAbortRequestBuilder, MultipartFileRequestBuilder, MultipartFinishRequestBuilder,
8 MultipartInitRequestBuilder, ObjectConfig, ProgressStream, PutFileRequestBuilder,
9 },
10};
11use anyhow::Error;
12use reqwest::{Body, Client, ClientBuilder, Method, Proxy, Url, header::HeaderMap};
13
14#[derive(Clone)]
15pub struct S3Client {
16 http_client: HttpClient,
17 auth_service: AuthorizationService,
18}
19
20impl S3Client {
21 pub fn new() -> Self {
22 Self {
23 http_client: HttpClientBuilder::default().build().unwrap(),
24 auth_service: AuthorizationService,
25 }
26 }
27
28 pub fn with_http_client(mut self, http_client: HttpClient) -> Self {
29 self.http_client = http_client;
30 self
31 }
32
33 pub fn with_auth_service(mut self, auth_service: AuthorizationService) -> Self {
34 self.auth_service = auth_service;
35 self
36 }
37
38 pub fn http_client(&self) -> HttpClient {
39 self.http_client.clone()
40 }
41
42 pub fn authorization_service(&self) -> AuthorizationService {
43 self.auth_service
44 }
45
46 #[must_use]
48 pub fn put_object(&self, object_config: ObjectConfig) -> PutFileRequestBuilder {
49 PutFileRequestBuilder::default()
50 .object_config(object_config)
51 .client(self.http_client())
52 }
53
54 pub fn multipart_init(&self, object_config: ObjectConfig) -> MultipartInitRequestBuilder {
56 MultipartInitRequestBuilder::default()
57 .object_config(object_config)
58 .client(self.http_client())
59 }
60
61 pub fn multipart_upload(&self, object_config: ObjectConfig) -> MultipartFileRequestBuilder {
63 MultipartFileRequestBuilder::default()
64 .object_config(object_config)
65 .client(self.http_client())
66 }
67
68 pub fn multipart_finish(&self, object_config: ObjectConfig) -> MultipartFinishRequestBuilder {
70 MultipartFinishRequestBuilder::default()
71 .object_config(object_config)
72 .client(self.http_client())
73 }
74
75 pub fn multipart_abort(&self, object_config: ObjectConfig) -> MultipartAbortRequestBuilder {
77 MultipartAbortRequestBuilder::default()
78 .object_config(object_config)
79 .client(self.http_client())
80 }
81
82 pub fn head_object(&self, object_config: ObjectConfig) -> HeadFileRequestBuilder {
84 HeadFileRequestBuilder::default()
85 .object_config(object_config)
86 .client(self.http_client())
87 }
88
89 pub fn gen_private_url(&self) -> GenPrivateUrlRequestBuilder {
91 GenPrivateUrlRequestBuilder::default()
92 }
93}
94
95#[repr(transparent)]
96#[derive(Clone)]
97pub struct HttpClient {
98 inner: Client,
99}
100
101pub struct HttpClientBuilder {
102 builder: ClientBuilder,
103}
104
105impl HttpClient {
106 pub fn builder() -> HttpClientBuilder {
107 HttpClientBuilder::new()
108 }
109
110 pub fn get_client(&self) -> &Client {
111 &self.inner
112 }
113
114 pub fn into_inner(self) -> Client {
115 self.inner
116 }
117}
118
119impl HttpClientBuilder {
120 pub fn new() -> Self {
121 Self {
122 builder: ClientBuilder::new()
123 .connect_timeout(Duration::from_secs(5))
124 .read_timeout(Duration::from_secs(30))
126 .timeout(Duration::from_secs(3600))
127 .pool_idle_timeout(Duration::from_secs(300))
129 .pool_max_idle_per_host(5)
130 .http1_only()
132 .user_agent(format!("ufile-rus3-sdk/{}", crate::VERSION)),
133 }
134 }
135
136 pub fn with_timeout(mut self, timeout: Duration) -> Self {
137 self.builder = self.builder.timeout(timeout);
138 self
139 }
140
141 pub fn with_connect_timeout(mut self, connect_timeout: Duration) -> Self {
142 self.builder = self.builder.connect_timeout(connect_timeout);
143 self
144 }
145
146 pub fn with_headers(mut self, headers: HeaderMap) -> Self {
147 self.builder = self.builder.default_headers(headers);
148 self
149 }
150
151 pub fn with_proxy(mut self, proxy: Proxy) -> Self {
152 self.builder = self.builder.proxy(proxy);
153 self
154 }
155
156 pub fn with_pool_idle_timeout(mut self, pool_idle_timeout: Duration) -> Self {
157 self.builder = self.builder.pool_idle_timeout(pool_idle_timeout);
158 self
159 }
160
161 pub fn with_read_timeout(mut self, read_timeout: Duration) -> Self {
162 self.builder = self.builder.read_timeout(read_timeout);
163 self
164 }
165
166 pub fn with_max_idle_per_host(mut self, max_idle_per_host: usize) -> Self {
167 self.builder = self.builder.pool_max_idle_per_host(max_idle_per_host);
168 self
169 }
170
171 pub fn build(self) -> Result<HttpClient, Error> {
172 Ok(HttpClient {
173 inner: self.builder.build()?,
174 })
175 }
176}
177impl Default for HttpClientBuilder {
178 fn default() -> Self {
179 Self::new()
180 }
181}
182impl HttpClient {
183 pub async fn send_file(
188 &self,
189 url: &str,
190 method: Method,
191 headers: HeaderMap,
192 stream: ByteStream,
193 ) -> Result<BaseResponse, Error> {
194 let signature = headers.get("Authorization");
196 if signature.is_none() {
197 return Err(Error::msg("No authorization header found"));
198 }
199 let response = self
200 .inner
201 .request(method, Url::from_str(url)?)
202 .headers(headers)
203 .body(Body::wrap_stream(ProgressStream::from(stream)))
204 .send()
205 .await?;
206 tracing::debug!("send file response: {:?}", response);
207 let response_headers = response
208 .headers()
209 .iter()
210 .map(|(key, value)| Ok((key.to_string(), String::from_utf8(value.as_bytes().into())?)))
211 .collect::<Result<HashMap<String, String>, Error>>()?;
212 let status = response.status();
213 Ok(if status.is_success() {
214 BaseResponse {
216 headers: response_headers,
217 ret_code: 0,
218 message: None,
219 }
220 } else {
221 response.json::<BaseResponse>().await?
222 })
223 }
224}