1 /* 2 * Copyright (c) 2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 use crate::{Certificate, HttpClientError, Proxy, Redirect, Request, Timeout, TlsVersion}; 17 use reqwest::Response; 18 19 mod downloader; 20 mod uploader; 21 22 pub use downloader::{DownloadOperator, Downloader, DownloaderBuilder}; 23 pub use uploader::{MultiPart, Part, UploadOperator, Uploader, UploaderBuilder}; 24 25 /// An asynchronous `Client` to make requests with. 26 /// 27 /// The Client has various configuration values to tweak, but the defaults 28 /// are set to what is usually the most commonly desired value. To configure a 29 /// `Client`, use `Client::builder()`. 30 /// 31 /// The `Client` holds a connection pool internally, so it is advised that 32 /// you create one and **reuse** it. 33 /// 34 /// You do **not** have to wrap the `Client` in an [`Rc`] or [`Arc`] to **reuse** it, 35 /// because it already uses an [`Arc`] internally. 36 /// 37 /// [`Rc`]: std::rc::Rc 38 /// 39 /// # Examples 40 /// 41 /// ```no_run 42 /// # use ylong_http_client::async_impl::Client; 43 /// # use ylong_http_client::Request; 44 /// 45 /// # async fn send_request() { 46 /// // Creates a `Client`. 47 /// let client = Client::builder().build().unwrap(); 48 /// 49 /// // Constructs your `Request`. 50 /// let request = Request::builder().body("".as_bytes()).unwrap(); 51 /// 52 /// // Sends your request through `Client` and gets the response. 53 /// let _response = client.request(request).await; 54 /// # } 55 /// ``` 56 #[derive(Clone)] 57 pub struct Client(reqwest::Client); 58 59 impl Client { 60 /// Creates a `ClientBuilder` to configure a `Client`. 61 /// 62 /// This is the same as `ClientBuilder::new()`. 63 /// 64 /// # Examples 65 /// 66 /// ``` 67 /// # use reqwest::Client; 68 /// 69 /// let builder = Client::builder(); 70 /// ``` builder() -> ClientBuilder71 pub fn builder() -> ClientBuilder { 72 ClientBuilder::new() 73 } 74 75 /// Sends a `Request` and gets the `Response`. 76 /// 77 /// A `Request` can be built manually with `Request::new()` or obtained 78 /// from a RequestBuilder with `RequestBuilder::build()`. 79 /// 80 /// # Errors 81 /// 82 /// This method fails if there was an error while sending request, 83 /// redirect loop was detected or redirect limit was exhausted. 84 /// 85 /// # Examples 86 /// 87 /// ```no_run 88 /// # use ylong_http_client::async_impl::Client; 89 /// # use ylong_http_client::Request; 90 /// 91 /// # async fn send_request() { 92 /// // Creates a `Client`. 93 /// let client = Client::builder().build().unwrap(); 94 /// 95 /// // Constructs your `Request`. 96 /// let request = Request::builder().body("".as_bytes()).unwrap(); 97 /// 98 /// // Sends your request through `Client` and gets the response. 99 /// let _response = client.request(request).await; 100 /// # } 101 /// ``` request<T: Into<reqwest::Body>>( &self, request: Request<T>, ) -> Result<Response, HttpClientError>102 pub async fn request<T: Into<reqwest::Body>>( 103 &self, 104 request: Request<T>, 105 ) -> Result<Response, HttpClientError> { 106 self.0 107 .request(request.inner.method, request.inner.url) 108 .headers(request.inner.headers) 109 .version(request.inner.version) 110 .body(request.body.into()) 111 .send() 112 .await 113 .map_err(HttpClientError::from) 114 } 115 } 116 117 /// A `ClientBuilder` can be used to create a `Client` with custom configuration. 118 /// 119 /// # Examples 120 /// 121 /// ``` 122 /// # use ylong_http_client::async_impl::ClientBuilder; 123 /// 124 /// let builder = ClientBuilder::new(); 125 /// ``` 126 pub struct ClientBuilder(reqwest::ClientBuilder); 127 128 impl ClientBuilder { 129 /// Creates a `ClientBuilder` to configure a `Client`. 130 /// 131 /// This is the same as `Client::builder()`. 132 /// 133 /// # Examples 134 /// 135 /// ``` 136 /// # use ylong_http_client::async_impl::ClientBuilder; 137 /// 138 /// let builder = ClientBuilder::new(); 139 /// ``` new() -> Self140 pub fn new() -> Self { 141 Self(reqwest::ClientBuilder::new()) 142 } 143 144 /// Only uses HTTP/1. 145 /// 146 /// # Examples 147 /// 148 /// ``` 149 /// # use ylong_http_client::async_impl::ClientBuilder; 150 /// 151 /// let builder = ClientBuilder::new().http1_only(); 152 /// ``` http1_only(self) -> Self153 pub fn http1_only(self) -> Self { 154 Self(self.0.http1_only()) 155 } 156 157 /// Only use HTTP/2. 158 /// 159 /// # Examples 160 /// 161 /// ``` 162 /// # use ylong_http_client::async_impl::ClientBuilder; 163 /// 164 /// let builder = ClientBuilder::new().http2_prior_knowledge(); 165 /// ``` http2_prior_knowledge(self) -> Self166 pub fn http2_prior_knowledge(self) -> Self { 167 Self(self.0.http2_prior_knowledge()) 168 } 169 170 /// Enables a request timeout. 171 /// 172 /// The timeout is applied from when the request starts connecting until the 173 /// response body has finished. 174 /// 175 /// Default is `Timeout::none()`. 176 /// 177 /// # Examples 178 /// 179 /// ``` 180 /// # use ylong_http_client::async_impl::ClientBuilder; 181 /// # use ylong_http_client::Timeout; 182 /// 183 /// let builder = ClientBuilder::new() 184 /// .request_timeout(Timeout::none()); 185 /// ``` request_timeout(self, timeout: Timeout) -> Self186 pub fn request_timeout(self, timeout: Timeout) -> Self { 187 match timeout.inner() { 188 Some(duration) => Self(self.0.timeout(duration)), 189 None => self, 190 } 191 } 192 193 /// Sets a timeout for only the connect phase of a `Client`. 194 /// 195 /// Default is `Timeout::none()`. 196 /// 197 /// # Examples 198 /// 199 /// ``` 200 /// # use ylong_http_client::async_impl::ClientBuilder; 201 /// # use ylong_http_client::Timeout; 202 /// 203 /// let builder = ClientBuilder::new() 204 /// .connect_timeout(Timeout::none()); 205 /// ``` connect_timeout(self, timeout: Timeout) -> Self206 pub fn connect_timeout(self, timeout: Timeout) -> Self { 207 match timeout.inner() { 208 Some(duration) => Self(self.0.connect_timeout(duration)), 209 None => self, 210 } 211 } 212 213 /// Sets a `RedirectPolicy` for this client. 214 /// 215 /// Default will follow redirects up to a maximum of 10. 216 /// 217 /// # Examples 218 /// 219 /// ``` 220 /// # use ylong_http_client::async_impl::ClientBuilder; 221 /// # use ylong_http_client::Redirect; 222 /// 223 /// let builder = ClientBuilder::new().redirect(Redirect::none()); 224 /// ``` redirect(self, redirect: Redirect) -> Self225 pub fn redirect(self, redirect: Redirect) -> Self { 226 Self(self.0.redirect(redirect.inner())) 227 } 228 229 /// Adds a `Proxy` to the list of proxies the `Client` will use. 230 /// 231 /// # Note 232 /// 233 /// Adding a proxy will disable the automatic usage of the "system" proxy. 234 /// 235 /// # Examples 236 /// 237 /// ``` 238 /// # use ylong_http_client::async_impl::ClientBuilder; 239 /// # use ylong_http_client::Proxy; 240 /// 241 /// let builder = ClientBuilder::new().proxy(Proxy::none()); 242 /// ``` proxy(self, proxy: Proxy) -> Self243 pub fn proxy(self, proxy: Proxy) -> Self { 244 match proxy.inner() { 245 Some(proxy) => Self(self.0.proxy(proxy)), 246 None => Self(self.0.no_proxy()), 247 } 248 } 249 250 /// Sets the maximum allowed TLS version for connections. 251 /// 252 /// By default there's no maximum. 253 /// 254 /// # Note 255 /// 256 /// `tls::Version::TLS_1_3` cannot be set as a maximum. 257 /// 258 /// # Examples 259 /// 260 /// ``` 261 /// # use ylong_http_client::async_impl::ClientBuilder; 262 /// # use ylong_http_client::TlsVersion; 263 /// 264 /// let builder = ClientBuilder::new().max_tls_version(TlsVersion::TLS_1_2); 265 /// ``` max_tls_version(self, version: TlsVersion) -> Self266 pub fn max_tls_version(self, version: TlsVersion) -> Self { 267 Self(self.0.max_tls_version(version)) 268 } 269 270 /// Sets the minimum required TLS version for connections. 271 /// 272 /// By default the TLS backend's own default is used. 273 /// 274 /// # Note 275 /// 276 /// `tls::Version::TLS_1_3` cannot be set as a minimum. 277 /// 278 /// # Examples 279 /// 280 /// ``` 281 /// # use ylong_http_client::async_impl::ClientBuilder; 282 /// # use ylong_http_client::TlsVersion; 283 /// 284 /// let builder = ClientBuilder::new().min_tls_version(TlsVersion::TLS_1_2); 285 /// ``` min_tls_version(self, version: TlsVersion) -> Self286 pub fn min_tls_version(self, version: TlsVersion) -> Self { 287 Self(self.0.min_tls_version(version)) 288 } 289 290 /// Adds a custom root certificate. 291 /// 292 /// This can be used to connect to a server that has a self-signed 293 /// certificate for example. 294 /// 295 /// # Examples 296 /// 297 /// ``` 298 /// # use ylong_http_client::async_impl::ClientBuilder; 299 /// # use ylong_http_client::Certificate; 300 /// 301 /// # fn set_cert(cert: Certificate) { 302 /// let builder = ClientBuilder::new().add_root_certificate(cert); 303 /// # } 304 /// ``` add_root_certificate(mut self, cert: Certificate) -> Self305 pub fn add_root_certificate(mut self, cert: Certificate) -> Self { 306 for cert in cert.into_inner() { 307 self = Self(self.0.add_root_certificate(cert)); 308 } 309 self 310 } 311 312 /// Controls the use of built-in/preloaded certificates during certificate validation. 313 /// 314 /// Defaults to `true` -- built-in system certs will be used. 315 /// 316 /// # Examples 317 /// 318 /// ``` 319 /// # use ylong_http_client::async_impl::ClientBuilder; 320 /// 321 /// let builder = ClientBuilder::new().tls_built_in_root_certs(true); 322 /// ``` tls_built_in_root_certs(self, tls_built_in_root_certs: bool) -> ClientBuilder323 pub fn tls_built_in_root_certs(self, tls_built_in_root_certs: bool) -> ClientBuilder { 324 Self(self.0.tls_built_in_root_certs(tls_built_in_root_certs)) 325 } 326 327 /// Controls the use of certificate validation. 328 /// 329 /// Defaults to `false`. 330 /// 331 /// # Warning 332 /// 333 /// You should think very carefully before using this method. If 334 /// invalid certificates are trusted, *any* certificate for *any* site 335 /// will be trusted for use. This includes expired certificates. This 336 /// introduces significant vulnerabilities, and should only be used 337 /// as a last resort. 338 /// 339 /// # Examples 340 /// 341 /// ``` 342 /// # use ylong_http_client::async_impl::ClientBuilder; 343 /// 344 /// let builder = ClientBuilder::new().danger_accept_invalid_certs(true); 345 /// ``` danger_accept_invalid_certs(self, accept_invalid_certs: bool) -> ClientBuilder346 pub fn danger_accept_invalid_certs(self, accept_invalid_certs: bool) -> ClientBuilder { 347 Self(self.0.danger_accept_invalid_certs(accept_invalid_certs)) 348 } 349 350 /// Returns a `Client` that uses this `ClientBuilder` configuration. 351 /// 352 /// # Errors 353 /// 354 /// This method fails if a TLS backend cannot be initialized, or the resolver 355 /// cannot load the system configuration. 356 /// 357 /// # Examples 358 /// 359 /// ``` 360 /// # use ylong_http_client::async_impl::ClientBuilder; 361 /// # use ylong_http_client::{Redirect, TlsVersion}; 362 /// 363 /// let client = ClientBuilder::new().build().unwrap(); 364 /// ``` build(self) -> Result<Client, HttpClientError>365 pub fn build(self) -> Result<Client, HttpClientError> { 366 self.0.build().map(Client).map_err(HttpClientError::from) 367 } 368 } 369 370 impl Default for ClientBuilder { default() -> Self371 fn default() -> Self { 372 Self::new() 373 } 374 } 375