Skip to main content

r402_http/server/
hooks.rs

1//! HTTP-layer lifecycle hooks (`GateHooks`).
2
3use std::future::Future;
4use std::pin::Pin;
5
6use axum_core::body::Body;
7use http::{Request, StatusCode};
8
9/// Outcome of [`GateHooks::on_protected_request`].
10#[derive(Debug)]
11#[non_exhaustive]
12pub enum ProtectedRequestOutcome {
13    /// Continue with the x402 payment check.
14    Continue,
15    /// Skip payment and forward the request.
16    GrantAccess,
17    /// Abort with a custom status and optional `text/plain` body.
18    Abort {
19        /// HTTP status to return.
20        status: StatusCode,
21        /// Optional response body.
22        body: Option<String>,
23    },
24}
25
26/// HTTP-layer hooks. Defaults are no-ops.
27pub trait GateHooks: Send + Sync {
28    /// Fires on every protected request, before the payment check.
29    fn on_protected_request<'a>(
30        &'a self,
31        _req: &'a Request<Body>,
32    ) -> impl Future<Output = ProtectedRequestOutcome> + Send + 'a {
33        async { ProtectedRequestOutcome::Continue }
34    }
35
36    /// Fires after a payment is verified, before the inner handler.
37    fn on_payment_verified<'a>(
38        &'a self,
39        _req: &'a mut Request<Body>,
40    ) -> impl Future<Output = ()> + Send + 'a {
41        async {}
42    }
43}
44
45/// Object-safe erasure of [`GateHooks`].
46pub trait DynGateHooks: Send + Sync {
47    /// See [`GateHooks::on_protected_request`].
48    fn on_protected_request<'a>(
49        &'a self,
50        req: &'a Request<Body>,
51    ) -> Pin<Box<dyn Future<Output = ProtectedRequestOutcome> + Send + 'a>>;
52
53    /// See [`GateHooks::on_payment_verified`].
54    fn on_payment_verified<'a>(
55        &'a self,
56        req: &'a mut Request<Body>,
57    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
58}
59
60impl<T> DynGateHooks for T
61where
62    T: GateHooks + ?Sized,
63{
64    fn on_protected_request<'a>(
65        &'a self,
66        req: &'a Request<Body>,
67    ) -> Pin<Box<dyn Future<Output = ProtectedRequestOutcome> + Send + 'a>> {
68        Box::pin(<T as GateHooks>::on_protected_request(self, req))
69    }
70
71    fn on_payment_verified<'a>(
72        &'a self,
73        req: &'a mut Request<Body>,
74    ) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
75        Box::pin(<T as GateHooks>::on_payment_verified(self, req))
76    }
77}