Skip to main content

better_fetch/backend/
mod.rs

1//! HTTP transport abstraction.
2//!
3//! The default backend is [`ReqwestBackend`]. Inject a custom [`HttpBackend`] via
4//! [`ClientBuilder::backend`](crate::ClientBuilder::backend) for tests or alternate transports.
5//!
6//! Buffered responses use [`HttpBackend::execute`]. Streaming responses use
7//! [`HttpBackend::execute_stream`] (implemented by [`ReqwestBackend`] by default).
8
9pub(crate) mod exec;
10mod reqwest;
11
12pub use reqwest::ReqwestBackend;
13
14use async_trait::async_trait;
15use bytes::Bytes;
16use http::{HeaderMap, Method, StatusCode};
17use std::time::Duration;
18
19use crate::cancel::CancellationToken;
20use crate::streaming::BodyStream;
21use crate::Result;
22
23#[cfg(feature = "multipart")]
24use crate::multipart::Form as MultipartForm;
25
26/// Request body encoding for the transport layer.
27#[derive(Debug, Clone, Default)]
28pub enum HttpBody {
29    /// No body.
30    #[default]
31    Empty,
32    /// Raw bytes body.
33    Bytes(Bytes),
34}
35
36/// Prepared HTTP request passed to a backend.
37#[derive(Debug)]
38pub struct HttpRequest {
39    /// HTTP method.
40    pub method: Method,
41    /// Fully resolved URL.
42    pub url: url::Url,
43    /// Request headers.
44    pub headers: HeaderMap,
45    /// Body when not using multipart.
46    pub body: HttpBody,
47    /// Per-request timeout.
48    pub timeout: Option<Duration>,
49    /// Cooperative cancellation.
50    pub cancellation: Option<CancellationToken>,
51    #[cfg(feature = "multipart")]
52    /// Multipart form (feature `multipart`).
53    pub multipart: Option<MultipartForm>,
54}
55
56impl Clone for HttpRequest {
57    fn clone(&self) -> Self {
58        Self {
59            method: self.method.clone(),
60            url: self.url.clone(),
61            headers: self.headers.clone(),
62            body: self.body.clone(),
63            timeout: self.timeout,
64            cancellation: self.cancellation.clone(),
65            #[cfg(feature = "multipart")]
66            multipart: None,
67        }
68    }
69}
70
71/// Raw HTTP response from a backend (fully buffered).
72#[derive(Debug, Clone)]
73pub struct HttpResponse {
74    /// HTTP status.
75    pub status: StatusCode,
76    /// Response headers.
77    pub headers: HeaderMap,
78    /// Response body bytes.
79    pub body: Bytes,
80}
81
82/// Raw HTTP response with a streaming body from a backend.
83pub struct HttpStreamingResponse {
84    /// HTTP status.
85    pub status: StatusCode,
86    /// Response headers.
87    pub headers: HeaderMap,
88    /// Response body stream.
89    pub body: BodyStream,
90}
91
92impl std::fmt::Debug for HttpStreamingResponse {
93    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94        f.debug_struct("HttpStreamingResponse")
95            .field("status", &self.status)
96            .field("headers", &self.headers)
97            .field("body", &"<stream>")
98            .finish()
99    }
100}
101
102/// Pluggable HTTP transport used by [`Client`](crate::Client).
103#[async_trait]
104pub trait HttpBackend: Send + Sync {
105    /// Executes one HTTP request and returns the fully buffered response.
106    async fn execute(&self, request: HttpRequest) -> Result<HttpResponse>;
107
108    /// Executes one HTTP request and returns a streaming response body.
109    async fn execute_stream(&self, request: HttpRequest) -> Result<HttpStreamingResponse>;
110}