mpl_proxy/
middleware.rs

1//! MPL middleware for request/response processing
2//!
3//! # Architecture Note
4//!
5//! MPL validation is implemented in the request handlers (`proxy.rs`) rather than
6//! as Tower middleware. This is the standard pattern for axum when body inspection
7//! is required, because:
8//!
9//! 1. Request bodies can only be consumed once - middleware would need to buffer
10//!    the entire body, parse it, then reconstruct it for downstream handlers
11//! 2. Validation logic is tightly coupled with routing (different paths may have
12//!    different validation requirements)
13//! 3. Response enrichment (adding MPL headers) is simpler in handlers
14//!
15//! The `proxy_handler` in `handlers.rs` calls `ProxyState::forward_request()` which:
16//! - Parses MPL envelopes from body or X-MPL-SType header
17//! - Validates against registered schemas
18//! - Evaluates QoM profiles
19//! - Verifies semantic hashes
20//! - Returns 400 in strict mode for failures
21//! - Adds X-MPL-QoM-Result headers to responses
22//!
23//! This middleware layer is available for future use cases like:
24//! - WebSocket connection upgrade interception
25//! - Request logging/tracing
26//! - Rate limiting based on SType
27
28use std::sync::Arc;
29use std::task::{Context, Poll};
30
31use axum::body::Body;
32use axum::http::{Request, Response};
33use tower::{Layer, Service};
34
35use crate::proxy::ProxyState;
36
37/// MPL middleware layer for future extensibility
38#[derive(Clone)]
39pub struct MplMiddleware {
40    #[allow(dead_code)]
41    state: Arc<ProxyState>,
42}
43
44impl MplMiddleware {
45    pub fn new(state: Arc<ProxyState>) -> Self {
46        Self { state }
47    }
48}
49
50impl<S> Layer<S> for MplMiddleware {
51    type Service = MplMiddlewareService<S>;
52
53    fn layer(&self, inner: S) -> Self::Service {
54        MplMiddlewareService {
55            inner,
56            #[allow(dead_code)]
57            state: self.state.clone(),
58        }
59    }
60}
61
62/// MPL middleware service
63#[derive(Clone)]
64pub struct MplMiddlewareService<S> {
65    inner: S,
66    #[allow(dead_code)]
67    state: Arc<ProxyState>,
68}
69
70impl<S> Service<Request<Body>> for MplMiddlewareService<S>
71where
72    S: Service<Request<Body>, Response = Response<Body>> + Clone + Send + 'static,
73    S::Future: Send,
74{
75    type Response = S::Response;
76    type Error = S::Error;
77    type Future = S::Future;
78
79    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
80        self.inner.poll_ready(cx)
81    }
82
83    fn call(&mut self, request: Request<Body>) -> Self::Future {
84        // Pass through - validation is handled in request handlers
85        // See module-level documentation for architecture rationale
86        self.inner.call(request)
87    }
88}