spin_factor_outbound_http/
intercept.rs

1use http::{Request, Response};
2use http_body_util::{BodyExt, Full};
3use spin_world::async_trait;
4use wasmtime_wasi_http::{body::HyperOutgoingBody, HttpResult};
5
6pub type HyperBody = HyperOutgoingBody;
7
8/// An outbound HTTP request interceptor to be used with
9/// [`super::InstanceState::set_request_interceptor`].
10#[async_trait]
11pub trait OutboundHttpInterceptor: Send + Sync {
12    /// Intercept an outgoing HTTP request.
13    ///
14    /// If this method returns [`InterceptOutcome::Continue`], the (possibly
15    /// updated) request will be passed on to the default outgoing request
16    /// handler.
17    ///
18    /// If this method returns [`InterceptOutcome::Complete`], the inner result
19    /// will be returned as the result of the request, bypassing the default
20    /// handler. The `request` will also be dropped immediately.
21    async fn intercept(&self, request: InterceptRequest) -> HttpResult<InterceptOutcome>;
22}
23
24/// The type returned by an [`OutboundHttpInterceptor`].
25pub enum InterceptOutcome {
26    /// The intercepted request will be passed on to the default outgoing
27    /// request handler.
28    Continue(InterceptRequest),
29    /// The given response will be returned as the result of the intercepted
30    /// request, bypassing the default handler.
31    Complete(Response<HyperBody>),
32}
33
34/// An intercepted outgoing HTTP request.
35///
36/// This is a wrapper that implements `DerefMut<Target = Request<()>>` for
37/// inspection and modification of the request envelope. If the body needs to be
38/// consumed, call [`Self::into_hyper_request`].
39pub struct InterceptRequest {
40    inner: Request<()>,
41    body: InterceptBody,
42}
43
44enum InterceptBody {
45    Hyper(HyperBody),
46    Vec(Vec<u8>),
47}
48
49impl InterceptRequest {
50    pub fn into_hyper_request(self) -> Request<HyperBody> {
51        let (parts, ()) = self.inner.into_parts();
52        Request::from_parts(parts, self.body.into())
53    }
54
55    pub(crate) fn into_vec_request(self) -> Option<Request<Vec<u8>>> {
56        let InterceptBody::Vec(bytes) = self.body else {
57            return None;
58        };
59        let (parts, ()) = self.inner.into_parts();
60        Some(Request::from_parts(parts, bytes))
61    }
62}
63
64impl std::ops::Deref for InterceptRequest {
65    type Target = Request<()>;
66
67    fn deref(&self) -> &Self::Target {
68        &self.inner
69    }
70}
71
72impl std::ops::DerefMut for InterceptRequest {
73    fn deref_mut(&mut self) -> &mut Self::Target {
74        &mut self.inner
75    }
76}
77
78impl From<Request<HyperBody>> for InterceptRequest {
79    fn from(req: Request<HyperBody>) -> Self {
80        let (parts, body) = req.into_parts();
81        Self {
82            inner: Request::from_parts(parts, ()),
83            body: InterceptBody::Hyper(body),
84        }
85    }
86}
87
88impl From<Request<Vec<u8>>> for InterceptRequest {
89    fn from(req: Request<Vec<u8>>) -> Self {
90        let (parts, body) = req.into_parts();
91        Self {
92            inner: Request::from_parts(parts, ()),
93            body: InterceptBody::Vec(body),
94        }
95    }
96}
97
98impl From<InterceptBody> for HyperBody {
99    fn from(body: InterceptBody) -> Self {
100        match body {
101            InterceptBody::Hyper(body) => body,
102            InterceptBody::Vec(bytes) => {
103                Full::new(bytes.into()).map_err(|err| match err {}).boxed()
104            }
105        }
106    }
107}