spin_factor_outbound_http/
intercept.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use http::{Request, Response};
use http_body_util::{BodyExt, Full};
use spin_world::async_trait;
use wasmtime_wasi_http::{body::HyperOutgoingBody, HttpResult};

pub type HyperBody = HyperOutgoingBody;

/// An outbound HTTP request interceptor to be used with
/// [`super::InstanceState::set_request_interceptor`].
#[async_trait]
pub trait OutboundHttpInterceptor: Send + Sync {
    /// Intercept an outgoing HTTP request.
    ///
    /// If this method returns [`InterceptOutcome::Continue`], the (possibly
    /// updated) request will be passed on to the default outgoing request
    /// handler.
    ///
    /// If this method returns [`InterceptOutcome::Complete`], the inner result
    /// will be returned as the result of the request, bypassing the default
    /// handler. The `request` will also be dropped immediately.
    async fn intercept(&self, request: InterceptRequest) -> HttpResult<InterceptOutcome>;
}

/// The type returned by an [`OutboundHttpInterceptor`].
pub enum InterceptOutcome {
    /// The intercepted request will be passed on to the default outgoing
    /// request handler.
    Continue(InterceptRequest),
    /// The given response will be returned as the result of the intercepted
    /// request, bypassing the default handler.
    Complete(Response<HyperBody>),
}

/// An intercepted outgoing HTTP request.
///
/// This is a wrapper that implements `DerefMut<Target = Request<()>>` for
/// inspection and modification of the request envelope. If the body needs to be
/// consumed, call [`Self::into_hyper_request`].
pub struct InterceptRequest {
    inner: Request<()>,
    body: InterceptBody,
}

enum InterceptBody {
    Hyper(HyperBody),
    Vec(Vec<u8>),
}

impl InterceptRequest {
    pub fn into_hyper_request(self) -> Request<HyperBody> {
        let (parts, ()) = self.inner.into_parts();
        Request::from_parts(parts, self.body.into())
    }

    pub(crate) fn into_vec_request(self) -> Option<Request<Vec<u8>>> {
        let InterceptBody::Vec(bytes) = self.body else {
            return None;
        };
        let (parts, ()) = self.inner.into_parts();
        Some(Request::from_parts(parts, bytes))
    }
}

impl std::ops::Deref for InterceptRequest {
    type Target = Request<()>;

    fn deref(&self) -> &Self::Target {
        &self.inner
    }
}

impl std::ops::DerefMut for InterceptRequest {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.inner
    }
}

impl From<Request<HyperBody>> for InterceptRequest {
    fn from(req: Request<HyperBody>) -> Self {
        let (parts, body) = req.into_parts();
        Self {
            inner: Request::from_parts(parts, ()),
            body: InterceptBody::Hyper(body),
        }
    }
}

impl From<Request<Vec<u8>>> for InterceptRequest {
    fn from(req: Request<Vec<u8>>) -> Self {
        let (parts, body) = req.into_parts();
        Self {
            inner: Request::from_parts(parts, ()),
            body: InterceptBody::Vec(body),
        }
    }
}

impl From<InterceptBody> for HyperBody {
    fn from(body: InterceptBody) -> Self {
        match body {
            InterceptBody::Hyper(body) => body,
            InterceptBody::Vec(bytes) => {
                Full::new(bytes.into()).map_err(|err| match err {}).boxed()
            }
        }
    }
}