better_fetch/backend/
mod.rs1pub(crate) mod exec;
10mod recording;
11mod reqwest;
12
13pub use recording::{RecordedBodyKind, RecordedRequest, RecordingBackend};
14pub use reqwest::ReqwestBackend;
15
16use async_trait::async_trait;
17use bytes::Bytes;
18use http::{HeaderMap, Method, StatusCode};
19use std::time::Duration;
20
21use crate::cancel::CancellationToken;
22use crate::streaming::BodyStream;
23use crate::Result;
24
25pub(crate) fn body_is_non_replayable(body: &HttpBody) -> bool {
27 matches!(body, HttpBody::Stream(_))
28}
29
30#[cfg(feature = "multipart")]
31use crate::multipart::Form as MultipartForm;
32
33#[derive(Default)]
35pub enum HttpBody {
36 #[default]
38 Empty,
39 Bytes(Bytes),
41 Stream(BodyStream),
43}
44
45impl std::fmt::Debug for HttpBody {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 match self {
48 Self::Empty => write!(f, "Empty"),
49 Self::Bytes(b) => f.debug_tuple("Bytes").field(b).finish(),
50 Self::Stream(_) => write!(f, "Stream"),
51 }
52 }
53}
54
55impl Clone for HttpBody {
56 fn clone(&self) -> Self {
59 match self {
60 Self::Empty => Self::Empty,
61 Self::Bytes(b) => Self::Bytes(b.clone()),
62 Self::Stream(_) => {
63 debug_assert!(
64 false,
65 "HttpBody::Stream must not be cloned; body was replaced with Empty"
66 );
67 Self::Empty
68 }
69 }
70 }
71}
72
73#[derive(Debug)]
75pub struct HttpRequest {
76 pub method: Method,
78 pub url: url::Url,
80 pub headers: HeaderMap,
82 pub body: HttpBody,
84 pub timeout: Option<Duration>,
86 pub cancellation: Option<CancellationToken>,
88 #[cfg(feature = "multipart")]
89 pub multipart: Option<MultipartForm>,
91}
92
93impl Clone for HttpRequest {
94 fn clone(&self) -> Self {
95 Self {
96 method: self.method.clone(),
97 url: self.url.clone(),
98 headers: self.headers.clone(),
99 body: self.body.clone(),
100 timeout: self.timeout,
101 cancellation: self.cancellation.clone(),
102 #[cfg(feature = "multipart")]
103 multipart: None,
104 }
105 }
106}
107
108#[derive(Debug, Clone)]
110pub struct HttpResponse {
111 pub status: StatusCode,
113 pub headers: HeaderMap,
115 pub body: Bytes,
117}
118
119pub struct HttpStreamingResponse {
121 pub status: StatusCode,
123 pub headers: HeaderMap,
125 pub body: BodyStream,
127}
128
129impl std::fmt::Debug for HttpStreamingResponse {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 f.debug_struct("HttpStreamingResponse")
132 .field("status", &self.status)
133 .field("headers", &self.headers)
134 .field("body", &"<stream>")
135 .finish()
136 }
137}
138
139#[async_trait]
141pub trait HttpBackend: Send + Sync {
142 async fn execute(&self, request: HttpRequest) -> Result<HttpResponse>;
144
145 async fn execute_stream(&self, request: HttpRequest) -> Result<HttpStreamingResponse>;
147}