Skip to main content

gproxy_channel_api/
prepared.rs

1//! The output of [`Channel::prepare`](crate::channel::Channel::prepare).
2
3use std::future::Future;
4use std::pin::Pin;
5use std::sync::Arc;
6
7use bytes::Bytes;
8
9#[cfg(not(target_arch = "wasm32"))]
10use crate::transport::RespStream;
11use crate::transport::{ClientError, UpstreamClient};
12
13/// A channel-driven multi-step upstream exchange. The pipeline injects the
14/// resolved `(proxy, emulation)` client; the closure performs whatever sequence
15/// of calls it needs and returns the finished, buffered response. The closure
16/// owns whatever else it needs (secret, inbound body) by `move`. Each call made
17/// through the injected client is logged (§8-D) by the pipeline's capturing
18/// wrapper, so the channel never resolves a proxy/client itself or persists
19/// anything.
20#[cfg(not(target_arch = "wasm32"))]
21pub type CustomSend = Box<
22    dyn FnOnce(
23            Arc<dyn UpstreamClient>,
24        )
25            -> Pin<Box<dyn Future<Output = Result<http::Response<Bytes>, ClientError>> + Send>>
26        + Send,
27>;
28/// wasm variant: the upstream future is `?Send` (see [`UpstreamClient`]).
29#[cfg(target_arch = "wasm32")]
30pub type CustomSend = Box<
31    dyn FnOnce(
32        Arc<dyn UpstreamClient>,
33    ) -> Pin<Box<dyn Future<Output = Result<http::Response<Bytes>, ClientError>>>>,
34>;
35
36/// A channel-driven multi-step exchange that returns a STREAMING body (native
37/// only). Like [`CustomSend`] but yields `(status, headers, stream)` so a
38/// long-running exchange streams the response incrementally instead of buffering
39/// the whole thing.
40#[cfg(not(target_arch = "wasm32"))]
41pub type CustomStreamSend = Box<
42    dyn FnOnce(
43            Arc<dyn UpstreamClient>,
44        ) -> Pin<
45            Box<
46                dyn Future<
47                        Output = Result<
48                            (http::StatusCode, http::HeaderMap, RespStream),
49                            ClientError,
50                        >,
51                    > + Send,
52            >,
53        > + Send,
54>;
55
56/// The output of [`Channel::prepare`](crate::Channel::prepare): either a single direct upstream request
57/// (the common case — the pipeline sends it once), or a channel-driven
58/// multi-step exchange ([`CustomSend`]).
59///
60/// Proxy and TLS-emulation are NOT carried here — they are per-credential /
61/// global / channel-default concerns resolved by the executor
62/// not the channel's to decide; the executor injects the resolved client into a
63/// `Custom` closure.
64// `Direct` (a full `http::Request`) is the hot path — every normal request. The
65// size gap vs the boxed `Custom` closure is real, but boxing `Direct` to close
66// it would add a heap allocation to EVERY request for the sake of the rare
67// multi-step exchange; not worth it. The value is short-lived (one per attempt).
68#[allow(clippy::large_enum_variant)]
69pub enum PreparedRequest {
70    /// Normal single send. `request.uri()` MUST be absolute (scheme + authority
71    /// + path + query) — wreq cannot route a relative URI.
72    Direct(http::Request<Bytes>),
73    /// Channel-driven buffered multi-step exchange.
74    Custom(CustomSend),
75    /// Channel-driven multi-step exchange that streams its body incrementally
76    /// Native only.
77    #[cfg(not(target_arch = "wasm32"))]
78    CustomStream(CustomStreamSend),
79}
80
81impl PreparedRequest {
82    /// Wrap a built request for a normal single send.
83    pub fn new(request: http::Request<Bytes>) -> Self {
84        Self::Direct(request)
85    }
86
87    /// Wrap a channel-driven multi-step exchange closure.
88    pub fn custom(send: CustomSend) -> Self {
89        Self::Custom(send)
90    }
91
92    /// Wrap a streaming channel-driven multi-step exchange closure.
93    #[cfg(not(target_arch = "wasm32"))]
94    pub fn custom_stream(send: CustomStreamSend) -> Self {
95        Self::CustomStream(send)
96    }
97
98    /// Execute this request in a host path that requires a buffered response.
99    /// Buffered custom exchanges use the same resolved client as direct
100    /// requests. A streaming-only exchange is rejected as a recoverable host
101    /// configuration error instead of being consumed or panicking.
102    pub async fn send_buffered(
103        self,
104        client: Arc<dyn UpstreamClient>,
105    ) -> Result<http::Response<Bytes>, ClientError> {
106        match self {
107            Self::Direct(request) => client.send(request).await,
108            Self::Custom(send) => send(client).await,
109            #[cfg(not(target_arch = "wasm32"))]
110            Self::CustomStream(_) => Err(ClientError::Config(
111                "streaming custom exchange cannot run in a buffered request path".into(),
112            )),
113        }
114    }
115
116    /// Consume a direct request without executing it.
117    ///
118    /// Custom exchanges require a host-provided [`UpstreamClient`] and must use
119    /// [`send_buffered`](Self::send_buffered) or the streaming pipeline executor.
120    pub fn into_http(self) -> Result<http::Request<Bytes>, ClientError> {
121        match self {
122            Self::Direct(request) => Ok(request),
123            Self::Custom(_) => Err(ClientError::Config(
124                "custom exchange requires execution through an upstream client".into(),
125            )),
126            #[cfg(not(target_arch = "wasm32"))]
127            Self::CustomStream(_) => Err(ClientError::Config(
128                "streaming custom exchange requires the streaming pipeline executor".into(),
129            )),
130        }
131    }
132}
133
134impl std::fmt::Debug for PreparedRequest {
135    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
136        match self {
137            Self::Direct(r) => f.debug_tuple("Direct").field(r).finish(),
138            Self::Custom(_) => f.write_str("Custom(<closure>)"),
139            #[cfg(not(target_arch = "wasm32"))]
140            Self::CustomStream(_) => f.write_str("CustomStream(<closure>)"),
141        }
142    }
143}