pub(crate) mod exec;
mod recording;
mod reqwest;
pub use recording::{RecordedBodyKind, RecordedRequest, RecordingBackend};
pub use reqwest::ReqwestBackend;
use async_trait::async_trait;
use bytes::Bytes;
use http::{HeaderMap, Method, StatusCode};
use std::time::Duration;
use crate::cancel::CancellationToken;
use crate::streaming::BodyStream;
use crate::Result;
pub(crate) fn body_is_non_replayable(body: &HttpBody) -> bool {
matches!(body, HttpBody::Stream(_))
}
#[cfg(feature = "multipart")]
use crate::multipart::Form as MultipartForm;
#[derive(Default)]
pub enum HttpBody {
#[default]
Empty,
Bytes(Bytes),
Stream(BodyStream),
}
impl std::fmt::Debug for HttpBody {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Empty => write!(f, "Empty"),
Self::Bytes(b) => f.debug_tuple("Bytes").field(b).finish(),
Self::Stream(_) => write!(f, "Stream"),
}
}
}
impl Clone for HttpBody {
fn clone(&self) -> Self {
match self {
Self::Empty => Self::Empty,
Self::Bytes(b) => Self::Bytes(b.clone()),
Self::Stream(_) => {
debug_assert!(
false,
"HttpBody::Stream must not be cloned; body was replaced with Empty"
);
Self::Empty
}
}
}
}
#[derive(Debug)]
pub struct HttpRequest {
pub method: Method,
pub url: url::Url,
pub headers: HeaderMap,
pub body: HttpBody,
pub timeout: Option<Duration>,
pub cancellation: Option<CancellationToken>,
#[cfg(feature = "multipart")]
pub multipart: Option<MultipartForm>,
}
impl Clone for HttpRequest {
fn clone(&self) -> Self {
Self {
method: self.method.clone(),
url: self.url.clone(),
headers: self.headers.clone(),
body: self.body.clone(),
timeout: self.timeout,
cancellation: self.cancellation.clone(),
#[cfg(feature = "multipart")]
multipart: None,
}
}
}
#[derive(Debug, Clone)]
pub struct HttpResponse {
pub status: StatusCode,
pub headers: HeaderMap,
pub body: Bytes,
}
pub struct HttpStreamingResponse {
pub status: StatusCode,
pub headers: HeaderMap,
pub body: BodyStream,
}
impl std::fmt::Debug for HttpStreamingResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("HttpStreamingResponse")
.field("status", &self.status)
.field("headers", &self.headers)
.field("body", &"<stream>")
.finish()
}
}
#[async_trait]
pub trait HttpBackend: Send + Sync {
async fn execute(&self, request: HttpRequest) -> Result<HttpResponse>;
async fn execute_stream(&self, request: HttpRequest) -> Result<HttpStreamingResponse>;
}