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}