1use super::{
2 super::{EndpointsProvider, IpAddrWithPort, ServiceName},
3 callbacks::{Callbacks, CallbacksBuilder},
4 Backoff, CachedResolver, CallbackContext, ChainedResolver, Chooser, ErrorRetrier, ExponentialBackoff,
5 ExtendedCallbackContext, LimitedBackoff, LimitedRetrier, NeverEmptyHandedChooser, RandomizedBackoff,
6 RequestRetrier, ResolveAnswers, Resolver, ResponseError, ShuffledChooser, ShuffledResolver, SimpleResolver,
7 SimplifiedCallbackContext, SubnetChooser, SyncRequestBuilder, TimeoutResolver,
8};
9use anyhow::Result as AnyResult;
10use assert_impl::assert_impl;
11use cfg_if::cfg_if;
12use qiniu_http::{
13 HeaderName, HeaderValue, HttpCaller, Method, ResponseParts, StatusCode, TransferProgressInfo, UserAgent,
14};
15use std::{
16 mem::{replace, take},
17 sync::Arc,
18 time::Duration,
19};
20
21#[cfg(feature = "isahc")]
22use qiniu_isahc::isahc::error::Error as IsahcError;
23
24#[cfg(feature = "async")]
25use super::AsyncRequestBuilder;
26
27#[derive(Debug, Clone)]
85pub struct HttpClient {
86 inner: Arc<HttpClientInner>,
87}
88
89impl HttpClient {
90 #[allow(dead_code)]
91 fn assert() {
92 assert_impl!(Send: Self);
93 assert_impl!(Sync: Self);
94 }
95}
96
97#[derive(Debug)]
98struct HttpClientInner {
99 use_https: bool,
100 appended_user_agent: UserAgent,
101 http_caller: Box<dyn HttpCaller>,
102 request_retrier: Box<dyn RequestRetrier>,
103 backoff: Box<dyn Backoff>,
104 chooser: Box<dyn Chooser>,
105 resolver: Box<dyn Resolver>,
106 callbacks: Callbacks<'static>,
107}
108
109impl HttpClient {
110 #[inline]
112 #[cfg(feature = "ureq")]
113 #[cfg_attr(feature = "docs", doc(cfg(feature = "ureq")))]
114 pub fn ureq() -> Self {
115 Self::build_ureq().build()
116 }
117
118 #[inline]
120 #[cfg(feature = "isahc")]
121 #[cfg_attr(feature = "docs", doc(cfg(feature = "isahc")))]
122 pub fn isahc() -> Result<Self, IsahcError> {
123 Ok(Self::build_isahc()?.build())
124 }
125
126 #[inline]
128 #[cfg(feature = "reqwest")]
129 #[cfg_attr(feature = "docs", doc(cfg(feature = "reqwest")))]
130 pub fn reqwest_sync() -> Self {
131 Self::build_reqwest_sync().build()
132 }
133
134 #[inline]
136 #[cfg(all(feature = "reqwest", feature = "async"))]
137 #[cfg_attr(feature = "docs", doc(cfg(all(feature = "reqwest", feature = "async"))))]
138 pub fn reqwest_async() -> Self {
139 Self::build_reqwest_async().build()
140 }
141
142 #[inline]
144 pub fn build_default() -> HttpClientBuilder {
145 HttpClientBuilder::default()
146 }
147
148 #[inline]
150 #[cfg(feature = "ureq")]
151 #[cfg_attr(feature = "docs", doc(cfg(feature = "ureq")))]
152 pub fn build_ureq() -> HttpClientBuilder {
153 HttpClientBuilder::ureq()
154 }
155
156 #[inline]
158 #[cfg(feature = "isahc")]
159 #[cfg_attr(feature = "docs", doc(cfg(feature = "isahc")))]
160 pub fn build_isahc() -> Result<HttpClientBuilder, IsahcError> {
161 HttpClientBuilder::isahc()
162 }
163
164 #[inline]
166 #[cfg(feature = "reqwest")]
167 #[cfg_attr(feature = "docs", doc(cfg(feature = "reqwest")))]
168 pub fn build_reqwest_sync() -> HttpClientBuilder {
169 HttpClientBuilder::reqwest_sync()
170 }
171
172 #[inline]
174 #[cfg(all(feature = "reqwest", feature = "async"))]
175 #[cfg_attr(feature = "docs", doc(cfg(all(feature = "reqwest", feature = "async"))))]
176 pub fn build_reqwest_async() -> HttpClientBuilder {
177 HttpClientBuilder::reqwest_async()
178 }
179
180 #[inline]
182 pub fn new(http_caller: impl HttpCaller + 'static) -> Self {
183 HttpClientBuilder::new(http_caller).build()
184 }
185
186 #[inline]
188 pub fn builder(http_caller: impl HttpCaller + 'static) -> HttpClientBuilder {
189 HttpClientBuilder::new(http_caller)
190 }
191
192 #[inline]
196 pub fn get<'r, E: EndpointsProvider + 'r>(
197 &'r self,
198 service_names: &'r [ServiceName],
199 endpoints_provider: E,
200 ) -> SyncRequestBuilder<'r, E> {
201 self.new_request(Method::GET, service_names, endpoints_provider)
202 }
203
204 #[inline]
208 pub fn post<'r, E: EndpointsProvider + 'r>(
209 &'r self,
210 service_names: &'r [ServiceName],
211 endpoints_provider: E,
212 ) -> SyncRequestBuilder<'r, E> {
213 self.new_request(Method::POST, service_names, endpoints_provider)
214 }
215
216 #[inline]
220 pub fn put<'r, E: EndpointsProvider + 'r>(
221 &'r self,
222 service_names: &'r [ServiceName],
223 endpoints_provider: E,
224 ) -> SyncRequestBuilder<'r, E> {
225 self.new_request(Method::PUT, service_names, endpoints_provider)
226 }
227
228 #[inline]
232 pub fn delete<'r, E: EndpointsProvider + 'r>(
233 &'r self,
234 service_names: &'r [ServiceName],
235 endpoints_provider: E,
236 ) -> SyncRequestBuilder<'r, E> {
237 self.new_request(Method::DELETE, service_names, endpoints_provider)
238 }
239
240 #[inline]
244 pub fn new_request<'r, E: EndpointsProvider + 'r>(
245 &'r self,
246 method: Method,
247 service_names: &'r [ServiceName],
248 endpoints_provider: E,
249 ) -> SyncRequestBuilder<'r, E> {
250 SyncRequestBuilder::new(self, method, endpoints_provider, service_names)
251 }
252
253 #[inline]
255 #[cfg(feature = "async")]
256 #[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
257 pub fn async_get<'r, E: EndpointsProvider + 'r>(
258 &'r self,
259 service_names: &'r [ServiceName],
260 endpoints_provider: E,
261 ) -> AsyncRequestBuilder<'r, E> {
262 self.new_async_request(Method::GET, service_names, endpoints_provider)
263 }
264
265 #[inline]
267 #[cfg(feature = "async")]
268 #[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
269 pub fn async_post<'r, E: EndpointsProvider + 'r>(
270 &'r self,
271 service_names: &'r [ServiceName],
272 endpoints_provider: E,
273 ) -> AsyncRequestBuilder<'r, E> {
274 self.new_async_request(Method::POST, service_names, endpoints_provider)
275 }
276
277 #[inline]
279 #[cfg(feature = "async")]
280 #[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
281 pub fn async_put<'r, E: EndpointsProvider + 'r>(
282 &'r self,
283 service_names: &'r [ServiceName],
284 endpoints_provider: E,
285 ) -> AsyncRequestBuilder<'r, E> {
286 self.new_async_request(Method::PUT, service_names, endpoints_provider)
287 }
288
289 #[inline]
291 #[cfg(feature = "async")]
292 #[cfg_attr(feature = "docs", doc(cfg(feature = "async")))]
293 pub fn async_delete<'r, E: EndpointsProvider + 'r>(
294 &'r self,
295 service_names: &'r [ServiceName],
296 endpoints_provider: E,
297 ) -> AsyncRequestBuilder<'r, E> {
298 self.new_async_request(Method::DELETE, service_names, endpoints_provider)
299 }
300
301 #[cfg(feature = "async")]
303 pub fn new_async_request<'r, E: EndpointsProvider + 'r>(
304 &'r self,
305 method: Method,
306 service_names: &'r [ServiceName],
307 endpoints_provider: E,
308 ) -> AsyncRequestBuilder<'r, E> {
309 AsyncRequestBuilder::new(self, method, endpoints_provider, service_names)
310 }
311
312 pub(super) fn use_https(&self) -> bool {
313 self.inner.use_https
314 }
315
316 pub(super) fn appended_user_agent(&self) -> &UserAgent {
317 &self.inner.appended_user_agent
318 }
319
320 pub(super) fn callbacks(&self) -> &Callbacks<'static> {
321 &self.inner.callbacks
322 }
323
324 pub(super) fn http_caller(&self) -> &dyn HttpCaller {
325 self.inner.http_caller.as_ref()
326 }
327
328 pub(super) fn request_retrier(&self) -> &dyn RequestRetrier {
329 self.inner.request_retrier.as_ref()
330 }
331
332 pub(super) fn backoff(&self) -> &dyn Backoff {
333 self.inner.backoff.as_ref()
334 }
335
336 pub(super) fn chooser(&self) -> &dyn Chooser {
337 self.inner.chooser.as_ref()
338 }
339
340 pub(super) fn resolver(&self) -> &dyn Resolver {
341 self.inner.resolver.as_ref()
342 }
343}
344
345#[derive(Debug)]
347pub struct HttpClientBuilder {
348 use_https: bool,
349 appended_user_agent: UserAgent,
350 http_caller: Option<Box<dyn HttpCaller>>,
351 request_retrier: Option<Box<dyn RequestRetrier>>,
352 backoff: Option<Box<dyn Backoff>>,
353 chooser: Option<Box<dyn Chooser>>,
354 resolver: Option<Box<dyn Resolver>>,
355 callbacks: CallbacksBuilder<'static>,
356}
357
358impl HttpClientBuilder {
359 #[inline]
361 #[cfg(feature = "ureq")]
362 pub fn ureq() -> Self {
363 Self::_new(Some(Box::<qiniu_ureq::Client>::default()))
364 }
365
366 #[inline]
368 #[cfg(feature = "isahc")]
369 pub fn isahc() -> Result<Self, IsahcError> {
370 Ok(Self::_new(Some(Box::new(qiniu_isahc::Client::default_client()?))))
371 }
372
373 #[inline]
375 #[cfg(feature = "reqwest")]
376 pub fn reqwest_sync() -> Self {
377 Self::_new(Some(Box::<qiniu_reqwest::SyncClient>::default()))
378 }
379
380 #[inline]
382 #[cfg(all(feature = "reqwest", feature = "async"))]
383 pub fn reqwest_async() -> Self {
384 Self::_new(Some(Box::<qiniu_reqwest::AsyncClient>::default()))
385 }
386
387 #[inline]
389 pub fn new(http_caller: impl HttpCaller + 'static) -> Self {
390 Self::_new(Some(Box::new(http_caller)))
391 }
392
393 fn _new(http_caller: Option<Box<dyn HttpCaller>>) -> Self {
394 HttpClientBuilder {
395 http_caller,
396 use_https: true,
397 appended_user_agent: Default::default(),
398 request_retrier: Default::default(),
399 backoff: Default::default(),
400 chooser: Default::default(),
401 resolver: Default::default(),
402 callbacks: Default::default(),
403 }
404 }
405
406 #[inline]
410 pub fn use_https(&mut self, use_https: bool) -> &mut Self {
411 self.use_https = use_https;
412 self
413 }
414
415 #[inline]
417 pub fn appended_user_agent(&mut self, appended_user_agent: impl Into<UserAgent>) -> &mut Self {
418 self.appended_user_agent = appended_user_agent.into();
419 self
420 }
421
422 #[inline]
426 pub fn http_caller(&mut self, http_caller: impl HttpCaller + 'static) -> &mut Self {
427 self.http_caller = Some(Box::new(http_caller));
428 self
429 }
430
431 #[inline]
435 pub fn request_retrier(&mut self, request_retrier: impl RequestRetrier + 'static) -> &mut Self {
436 self.request_retrier = Some(Box::new(request_retrier));
437 self
438 }
439
440 #[inline]
444 pub fn backoff(&mut self, backoff: impl Backoff + 'static) -> &mut Self {
445 self.backoff = Some(Box::new(backoff));
446 self
447 }
448
449 #[inline]
453 pub fn chooser(&mut self, chooser: impl Chooser + 'static) -> &mut Self {
454 self.chooser = Some(Box::new(chooser));
455 self
456 }
457
458 #[inline]
462 pub fn resolver(&mut self, resolver: impl Resolver + 'static) -> &mut Self {
463 self.resolver = Some(Box::new(resolver));
464 self
465 }
466
467 #[inline]
469 pub fn on_uploading_progress(
470 &mut self,
471 callback: impl Fn(&dyn SimplifiedCallbackContext, TransferProgressInfo<'_>) -> AnyResult<()> + Send + Sync + 'static,
472 ) -> &mut Self {
473 self.callbacks.on_uploading_progress(callback);
474 self
475 }
476
477 #[inline]
479 pub fn on_receive_response_status(
480 &mut self,
481 callback: impl Fn(&dyn SimplifiedCallbackContext, StatusCode) -> AnyResult<()> + Send + Sync + 'static,
482 ) -> &mut Self {
483 self.callbacks.on_receive_response_status(callback);
484 self
485 }
486
487 #[inline]
489 pub fn on_receive_response_header(
490 &mut self,
491 callback: impl Fn(&dyn SimplifiedCallbackContext, &HeaderName, &HeaderValue) -> AnyResult<()>
492 + Send
493 + Sync
494 + 'static,
495 ) -> &mut Self {
496 self.callbacks.on_receive_response_header(callback);
497 self
498 }
499
500 #[inline]
502 pub fn on_to_resolve_domain(
503 &mut self,
504 callback: impl Fn(&mut dyn CallbackContext, &str) -> AnyResult<()> + Send + Sync + 'static,
505 ) -> &mut Self {
506 self.callbacks.on_to_resolve_domain(callback);
507 self
508 }
509
510 #[inline]
512 pub fn on_domain_resolved(
513 &mut self,
514 callback: impl Fn(&mut dyn CallbackContext, &str, &ResolveAnswers) -> AnyResult<()> + Send + Sync + 'static,
515 ) -> &mut Self {
516 self.callbacks.on_domain_resolved(callback);
517 self
518 }
519
520 #[inline]
522 pub fn on_to_choose_ips(
523 &mut self,
524 callback: impl Fn(&mut dyn CallbackContext, &[IpAddrWithPort]) -> AnyResult<()> + Send + Sync + 'static,
525 ) -> &mut Self {
526 self.callbacks.on_to_choose_ips(callback);
527 self
528 }
529
530 #[inline]
532 pub fn on_ips_chosen(
533 &mut self,
534 callback: impl Fn(&mut dyn CallbackContext, &[IpAddrWithPort], &[IpAddrWithPort]) -> AnyResult<()>
535 + Send
536 + Sync
537 + 'static,
538 ) -> &mut Self {
539 self.callbacks.on_ips_chosen(callback);
540 self
541 }
542
543 #[inline]
545 pub fn on_before_request_signed(
546 &mut self,
547 callback: impl Fn(&mut dyn ExtendedCallbackContext) -> AnyResult<()> + Send + Sync + 'static,
548 ) -> &mut Self {
549 self.callbacks.on_before_request_signed(callback);
550 self
551 }
552
553 #[inline]
555 pub fn on_after_request_signed(
556 &mut self,
557 callback: impl Fn(&mut dyn ExtendedCallbackContext) -> AnyResult<()> + Send + Sync + 'static,
558 ) -> &mut Self {
559 self.callbacks.on_after_request_signed(callback);
560 self
561 }
562
563 #[inline]
565 pub fn on_response(
566 &mut self,
567 callback: impl Fn(&mut dyn ExtendedCallbackContext, &ResponseParts) -> AnyResult<()> + Send + Sync + 'static,
568 ) -> &mut Self {
569 self.callbacks.on_response(callback);
570 self
571 }
572
573 #[inline]
575 pub fn on_error(
576 &mut self,
577 callback: impl Fn(&mut dyn ExtendedCallbackContext, &mut ResponseError) -> AnyResult<()> + Send + Sync + 'static,
578 ) -> &mut Self {
579 self.callbacks.on_error(callback);
580 self
581 }
582
583 #[inline]
585 pub fn on_before_backoff(
586 &mut self,
587 callback: impl Fn(&mut dyn ExtendedCallbackContext, Duration) -> AnyResult<()> + Send + Sync + 'static,
588 ) -> &mut Self {
589 self.callbacks.on_before_backoff(callback);
590 self
591 }
592
593 #[inline]
595 pub fn on_after_backoff(
596 &mut self,
597 callback: impl Fn(&mut dyn ExtendedCallbackContext, Duration) -> AnyResult<()> + Send + Sync + 'static,
598 ) -> &mut Self {
599 self.callbacks.on_after_backoff(callback);
600 self
601 }
602
603 pub fn build(&mut self) -> HttpClient {
605 HttpClient {
606 inner: Arc::new(HttpClientInner {
607 use_https: replace(&mut self.use_https, true),
608 appended_user_agent: take(&mut self.appended_user_agent),
609 http_caller: take(&mut self.http_caller).unwrap_or_else(HttpClient::default_http_caller),
610 request_retrier: take(&mut self.request_retrier).unwrap_or_else(HttpClient::default_retrier),
611 backoff: take(&mut self.backoff).unwrap_or_else(HttpClient::default_backoff),
612 chooser: take(&mut self.chooser).unwrap_or_else(HttpClient::default_chooser),
613 resolver: take(&mut self.resolver).unwrap_or_else(HttpClient::default_resolver),
614 callbacks: self.callbacks.build(),
615 }),
616 }
617 }
618
619 #[allow(dead_code)]
620 fn assert() {
621 assert_impl!(Send: Self);
622 assert_impl!(Sync: Self);
623 }
624}
625
626impl Default for HttpClientBuilder {
627 #[inline]
628 fn default() -> Self {
629 HttpClientBuilder::_new(None)
630 }
631}
632
633impl Default for HttpClient {
634 #[inline]
635 fn default() -> Self {
636 HttpClientBuilder::default().build()
637 }
638}
639
640impl HttpClient {
641 #[inline]
645 pub fn default_http_caller() -> Box<dyn HttpCaller> {
646 cfg_if! {
647 if #[cfg(all(feature = "ureq", not(feature = "async")))] {
648 Box::new(qiniu_ureq::Client::default())
649 } else if #[cfg(feature = "isahc")] {
650 Box::new(qiniu_isahc::Client::default_client().expect("Failed to initialize isahc"))
651 } else if #[cfg(all(feature = "reqwest", not(feature = "async")))] {
652 Box::new(qiniu_reqwest::SyncClient::default())
653 } else if #[cfg(all(feature = "reqwest", feature = "async"))] {
654 Box::new(qiniu_reqwest::AsyncClient::default())
655 } else {
656 panic!("No http caller available, can you enable feature `isahc` to resolve this problem?")
657 }
658 }
659 }
660
661 pub fn default_resolver() -> Box<dyn Resolver> {
665 let chained_resolver = {
666 let base_resolver = Box::<TimeoutResolver<SimpleResolver>>::default();
667
668 #[allow(unused_mut)]
669 let mut builder = ChainedResolver::builder(base_resolver);
670
671 #[cfg(feature = "c_ares")]
672 if let Ok(resolver) = super::CAresResolver::new() {
673 builder.prepend_resolver(Box::new(resolver));
674 }
675
676 #[cfg(all(feature = "trust_dns", feature = "async"))]
677 if let Ok(resolver) = async_std::task::block_on(async { super::TrustDnsResolver::from_system_conf().await })
678 {
679 builder.prepend_resolver(Box::new(resolver));
680 }
681
682 builder.build()
683 };
684 let cached_resolver = CachedResolver::builder(chained_resolver).in_memory();
685 let shuffled_resolver = ShuffledResolver::new(cached_resolver);
686 Box::new(shuffled_resolver)
687 }
688
689 #[inline]
693 pub fn default_chooser() -> Box<dyn Chooser> {
694 Box::<NeverEmptyHandedChooser<ShuffledChooser<SubnetChooser>>>::default()
695 }
696
697 #[inline]
701 pub fn default_retrier() -> Box<dyn RequestRetrier> {
702 Box::<LimitedRetrier<ErrorRetrier>>::default()
703 }
704
705 #[inline]
709 pub fn default_backoff() -> Box<dyn Backoff> {
710 Box::<LimitedBackoff<RandomizedBackoff<ExponentialBackoff>>>::default()
711 }
712}