1#![cfg(feature = "http")]
2
3mod cache;
10mod ctx;
11mod curl;
12mod file_cache;
13mod util;
14
15pub use cache::{CacheKey, CacheMode, CachePolicy};
16pub use ctx::{HttpCache, HttpClient, http_cache, http_client, set_http_cache, set_http_client, set_request_default};
17pub use curl::CurlProcessClient;
18pub use file_cache::FileSystemCache;
19
20pub type Error = Box<dyn std::error::Error + Send + Sync>;
30
31pub use http::{
32 StatusCode, header,
33 method::{self, Method},
34 uri::{self, Uri},
35};
36use serde::{Deserialize, Serialize};
37use zng_var::{Var, const_var};
38
39use std::time::Duration;
40use std::{fmt, mem};
41
42use crate::{channel::IpcBytes, http::ctx::REQUEST_DEFAULT, io::Metrics};
43
44use super::io::AsyncRead;
45
46use zng_txt::{ToTxt, Txt, formatx};
47use zng_unit::*;
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
53#[non_exhaustive]
54pub struct Request {
55 #[serde(with = "http_serde::uri")]
57 pub uri: Uri,
58 #[serde(with = "http_serde::method")]
60 pub method: Method,
61
62 #[serde(with = "http_serde::header_map")]
66 pub headers: http::HeaderMap,
67
68 pub timeout: Duration,
79
80 pub connect_timeout: Duration,
84
85 pub low_speed_timeout: (Duration, ByteLength),
89
90 pub redirect_limit: u16,
96
97 #[cfg(feature = "http_compression")]
103 pub auto_decompress: bool,
104
105 pub max_upload_speed: ByteLength,
109
110 pub max_download_speed: ByteLength,
114
115 pub require_length: bool,
119
120 pub max_length: ByteLength,
130
131 pub cache: CacheMode,
135
136 #[cfg(feature = "http_cookie")]
142 pub cookies: bool,
143
144 pub metrics: bool,
150
151 pub body: IpcBytes,
155}
156impl Request {
157 pub fn new(method: Method, uri: Uri) -> Self {
169 match REQUEST_DEFAULT.lock().clone() {
170 Some(mut r) => {
171 r.method = method;
172 r.uri = uri;
173 r
174 }
175 None => Self {
176 uri,
177 method,
178 require_length: false,
179 max_length: ByteLength::MAX,
180 headers: header::HeaderMap::new(),
181 timeout: Duration::MAX,
182 connect_timeout: 90.secs(),
183 low_speed_timeout: (Duration::MAX, 0.bytes()),
184 redirect_limit: 20,
185 #[cfg(feature = "http_compression")]
186 auto_decompress: true,
187 max_upload_speed: ByteLength::MAX,
188 max_download_speed: ByteLength::MAX,
189 cache: CacheMode::Default,
190 #[cfg(feature = "http_cookie")]
191 cookies: false,
192 metrics: true,
193 body: IpcBytes::default(),
194 },
195 }
196 }
197
198 pub fn get<U: TryInto<Uri>>(uri: U) -> Result<Self, <U as TryInto<Uri>>::Error> {
210 Ok(Self::new(Method::GET, uri.try_into()?))
211 }
212
213 pub fn put<U: TryInto<Uri>>(uri: U) -> Result<Self, <U as TryInto<Uri>>::Error> {
225 Ok(Self::new(Method::PUT, uri.try_into()?))
226 }
227
228 pub fn post<U: TryInto<Uri>>(uri: U) -> Result<Self, <U as TryInto<Uri>>::Error> {
240 Ok(Self::new(Method::POST, uri.try_into()?))
241 }
242
243 pub fn delete<U: TryInto<Uri>>(uri: U) -> Result<Self, <U as TryInto<Uri>>::Error> {
255 Ok(Self::new(Method::DELETE, uri.try_into()?))
256 }
257
258 pub fn patch<U: TryInto<Uri>>(uri: U) -> Result<Self, <U as TryInto<Uri>>::Error> {
270 Ok(Self::new(Method::PATCH, uri.try_into()?))
271 }
272
273 pub fn head<U: TryInto<Uri>>(uri: U) -> Result<Self, <U as TryInto<Uri>>::Error> {
285 Ok(Self::new(Method::HEAD, uri.try_into()?))
286 }
287
288 pub fn header<K, V>(mut self, name: K, value: V) -> Result<Self, Error>
292 where
293 K: TryInto<header::HeaderName>,
294 V: TryInto<header::HeaderValue>,
295 Error: From<<K as TryInto<header::HeaderName>>::Error>,
296 Error: From<<V as TryInto<header::HeaderValue>>::Error>,
297 {
298 self.headers.insert(name.try_into()?, value.try_into()?);
299 Ok(self)
300 }
301
302 pub fn timeout(mut self, timeout: Duration) -> Self {
306 self.timeout = timeout;
307 self
308 }
309
310 pub fn connect_timeout(mut self, timeout: Duration) -> Self {
314 self.connect_timeout = timeout;
315 self
316 }
317
318 pub fn low_speed_timeout(mut self, timeout: Duration, bytes_per_sec: ByteLength) -> Self {
322 self.low_speed_timeout = (timeout, bytes_per_sec);
323 self
324 }
325
326 pub fn redirect_limit(mut self, count: u16) -> Self {
330 self.redirect_limit = count;
331 self
332 }
333
334 #[cfg(feature = "http_compression")]
338 pub fn auto_decompress(mut self, enabled: bool) -> Self {
339 self.auto_decompress = enabled;
340 self
341 }
342
343 pub fn require_length(mut self, enabled: bool) -> Self {
347 self.require_length = enabled;
348 self
349 }
350
351 pub fn max_length(mut self, max: ByteLength) -> Self {
355 self.max_length = max;
356 self
357 }
358
359 pub fn max_upload_speed(mut self, bytes_per_sec: ByteLength) -> Self {
363 self.max_upload_speed = bytes_per_sec;
364 self
365 }
366
367 pub fn max_download_speed(mut self, bytes_per_sec: ByteLength) -> Self {
371 self.max_download_speed = bytes_per_sec;
372 self
373 }
374
375 #[cfg(feature = "http_cookie")]
379 pub fn cookies(mut self, enable: bool) -> Self {
380 self.cookies = enable;
381 self
382 }
383
384 pub fn metrics(mut self, enabled: bool) -> Self {
388 self.metrics = enabled;
389 self
390 }
391
392 pub fn body(mut self, body: IpcBytes) -> Self {
396 self.body = body;
397 self
398 }
399
400 pub fn body_text(mut self, body: &str) -> Result<Self, Error> {
404 if !self.headers.contains_key("Content-Type") {
405 self = self.header("Content-Type", "text/plain; charset=utf-8")?;
406 }
407 Ok(self.body(IpcBytes::from_slice_blocking(body.as_bytes())?))
408 }
409
410 pub fn body_json<T: Serialize>(mut self, body: &T) -> Result<Self, Error> {
414 if !self.headers.contains_key("Content-Type") {
415 self = self.header("Content-Type", "text/json; charset=utf-8")?;
416 }
417 let body = serde_json::to_vec(body)?;
418 Ok(self.body(IpcBytes::from_vec_blocking(body)?))
419 }
420}
421impl From<Request> for http::Request<IpcBytes> {
422 fn from(mut r: Request) -> Self {
423 let mut b = http::Request::builder().uri(mem::take(&mut r.uri)).method(r.method.clone());
424 if !r.headers.is_empty() {
425 *b.headers_mut().unwrap() = mem::take(&mut r.headers);
426 }
427 let body = mem::take(&mut r.body);
428 let b = b.extension(r);
429 b.body(body).unwrap()
430 }
431}
432impl From<http::Request<IpcBytes>> for Request {
433 fn from(value: http::Request<IpcBytes>) -> Self {
434 let (mut parts, body) = value.into_parts();
435 if let Some(mut r) = parts.extensions.remove::<Request>() {
436 r.method = parts.method;
437 r.uri = parts.uri;
438 r.headers = parts.headers;
439 r.body = body;
440 r
441 } else {
442 let mut r = Request::new(parts.method, parts.uri);
443 r.headers = parts.headers;
444 r.body = body;
445 r
446 }
447 }
448}
449
450pub struct Response {
452 status: StatusCode,
453 headers: header::HeaderMap,
454 effective_uri: Uri,
455 body: ResponseBody,
456 metrics: Var<Metrics>,
457}
458impl fmt::Debug for Response {
459 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
460 f.debug_struct("Response")
461 .field("status", &self.status)
462 .field("effective_uri", &self.effective_uri)
463 .field("header", &self.headers)
464 .field("metrics", &self.metrics.get())
465 .finish_non_exhaustive()
466 }
467}
468enum ResponseBody {
469 Done { bytes: IpcBytes },
470 Read { read: Box<dyn AsyncRead + Send> },
471}
472impl Response {
473 pub fn from_read(
475 status: StatusCode,
476 header: header::HeaderMap,
477 effective_uri: Uri,
478 metrics: Var<Metrics>,
479 read: Box<dyn AsyncRead + Send>,
480 ) -> Self {
481 Self {
482 status,
483 headers: header,
484 effective_uri,
485 metrics,
486 body: ResponseBody::Read { read },
487 }
488 }
489
490 pub fn from_done(status: StatusCode, mut headers: header::HeaderMap, effective_uri: Uri, metrics: Metrics, body: IpcBytes) -> Self {
492 if !headers.contains_key(header::CONTENT_LENGTH) {
493 headers.insert(header::CONTENT_LENGTH, body.len().into());
494 }
495 Self {
496 status,
497 headers,
498 effective_uri,
499 metrics: const_var(metrics),
500 body: ResponseBody::Done { bytes: body },
501 }
502 }
503
504 pub fn from_msg(status: StatusCode, msg: impl ToTxt) -> Self {
506 Self::from_done(
507 status,
508 header::HeaderMap::new(),
509 Uri::from_static("/"),
510 Metrics::zero(),
511 IpcBytes::from_slice_blocking(msg.to_txt().as_bytes()).unwrap(),
512 )
513 }
514
515 pub fn status(&self) -> StatusCode {
517 self.status
518 }
519
520 pub fn header(&self) -> &header::HeaderMap {
522 &self.headers
523 }
524
525 pub fn effective_uri(&self) -> &Uri {
529 &self.effective_uri
530 }
531
532 pub fn content_len(&self) -> Option<ByteLength> {
534 match &self.body {
535 ResponseBody::Done { bytes, .. } => Some((bytes.len() as u64).bytes()),
536 ResponseBody::Read { .. } => {
537 let len = self
538 .headers
539 .get(header::CONTENT_LENGTH)?
540 .to_str()
541 .ok()?
542 .parse::<u64>()
543 .ok()?
544 .bytes();
545 Some(len)
546 }
547 }
548 }
549
550 pub async fn download(&mut self) -> Result<(), Error> {
552 if let ResponseBody::Done { .. } = &self.body {
553 return Ok(());
554 }
555
556 let downloader = match mem::replace(
557 &mut self.body,
558 ResponseBody::Done {
559 bytes: IpcBytes::default(),
560 },
561 ) {
562 ResponseBody::Read { read: downloader } => downloader,
563 ResponseBody::Done { .. } => unreachable!(),
564 };
565 let mut downloader = Box::into_pin(downloader);
566 let body = IpcBytes::from_read(downloader.as_mut()).await?;
567
568 self.body = ResponseBody::Done { bytes: body };
569
570 Ok(())
571 }
572
573 pub async fn body(&mut self) -> Result<IpcBytes, Error> {
575 self.download().await?;
576 match &self.body {
577 ResponseBody::Done { bytes } => Ok(bytes.clone()),
578 ResponseBody::Read { .. } => unreachable!(),
579 }
580 }
581
582 pub async fn body_text(&mut self) -> Result<Txt, Error> {
584 let content_type = self
585 .headers
586 .get(header::CONTENT_TYPE)
587 .and_then(|value| value.to_str().ok())
588 .and_then(|value| value.parse::<mime::Mime>().ok());
589 let encoding_name = content_type
590 .as_ref()
591 .and_then(|mime| mime.get_param("charset").map(|charset| charset.as_str()))
592 .unwrap_or("utf-8");
593
594 let bytes = self.body().await?;
595
596 let encoding = encoding_rs::Encoding::for_label(encoding_name.as_bytes()).unwrap_or(encoding_rs::UTF_8);
597 let (text, _, _) = encoding.decode(&bytes);
598 Ok(Txt::from_str(&text))
599 }
600
601 pub async fn body_json<O>(&mut self) -> Result<O, Error>
603 where
604 O: serde::de::DeserializeOwned + std::marker::Unpin,
605 {
606 let bytes = self.body().await?;
607 let r = serde_json::from_slice(&bytes)?;
608 Ok(r)
609 }
610
611 pub fn metrics(&self) -> Var<Metrics> {
613 self.metrics.read_only()
614 }
615
616 pub async fn error(&mut self) -> Result<(), HttpError> {
623 if self.status.is_success() {
624 Ok(())
625 } else {
626 let body = match self.body_text().await {
627 Ok(b) => b,
628 Err(e) => formatx!("could not receive error body, {e}"),
629 };
630 Err(HttpError { status: self.status, body })
631 }
632 }
633}
634
635#[derive(Debug, Clone, PartialEq)]
639#[non_exhaustive]
640pub struct HttpError {
641 pub status: StatusCode,
643 pub body: Txt,
645}
646
647impl HttpError {
648 pub fn new(status: StatusCode, body: Txt) -> Self {
650 Self { status, body }
651 }
652}
653impl fmt::Display for HttpError {
655 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
656 write!(f, "HTTP error {}", self.status.as_str())?;
657 if let Some(r) = self.status.canonical_reason() {
658 write!(f, " {r}")?;
659 }
660 if f.alternate() && self.body.is_empty() {
661 for line in self.body.lines() {
662 write!(f, "\n {line}")?;
663 }
664 }
665 Ok(())
666 }
667}
668impl std::error::Error for HttpError {}
669
670pub async fn get<U>(uri: U) -> Result<Response, Error>
674where
675 U: TryInto<Uri>,
676 Error: From<<U as TryInto<Uri>>::Error>,
677{
678 send(Request::get(uri)?).await
679}
680
681pub async fn get_txt<U>(uri: U) -> Result<Txt, Error>
685where
686 U: TryInto<Uri>,
687 Error: From<<U as TryInto<Uri>>::Error>,
688{
689 let mut r = send(Request::get(uri)?).await?;
690 r.error().await?;
691 r.body_text().await
692}
693
694pub async fn get_bytes<U>(uri: U) -> Result<IpcBytes, Error>
698where
699 U: TryInto<Uri>,
700 Error: From<<U as TryInto<Uri>>::Error>,
701{
702 let mut r = send(Request::get(uri)?).await?;
703 r.error().await?;
704 r.body().await
705}
706
707pub async fn get_json<U, O>(uri: U) -> Result<O, Error>
711where
712 U: TryInto<Uri>,
713 Error: From<<U as TryInto<Uri>>::Error>,
714 O: serde::de::DeserializeOwned + std::marker::Unpin,
715{
716 let mut r = send(Request::get(uri)?).await?;
717 r.error().await?;
718 r.body_json().await
719}
720
721pub async fn head<U>(uri: U) -> Result<Response, Error>
725where
726 U: TryInto<Uri>,
727 Error: From<<U as TryInto<Uri>>::Error>,
728{
729 send(Request::head(uri)?).await
730}
731
732pub async fn put<U>(uri: U, body: IpcBytes) -> Result<Response, Error>
736where
737 U: TryInto<Uri>,
738 Error: From<<U as TryInto<Uri>>::Error>,
739{
740 send(Request::put(uri)?.body(body)).await
741}
742
743pub async fn post<U>(uri: U, body: IpcBytes) -> Result<Response, Error>
747where
748 U: TryInto<Uri>,
749 Error: From<<U as TryInto<Uri>>::Error>,
750{
751 send(Request::post(uri)?.body(body)).await
752}
753
754pub async fn delete<U>(uri: U) -> Result<Response, Error>
758where
759 U: TryInto<Uri>,
760 Error: From<<U as TryInto<Uri>>::Error>,
761{
762 send(Request::delete(uri)?).await
763}
764
765pub async fn send(request: Request) -> Result<Response, Error> {
769 let client = http_client();
770 if client.is_cache_manager() {
771 client.send(request).await
772 } else {
773 match request.cache {
774 CacheMode::NoCache => client.send(request).await,
775 CacheMode::Default => cache::send_cache(client, request).await,
776 CacheMode::Permanent => cache::send_cache_perm(client, request).await,
777 }
778 }
779}