Skip to main content

alibi_core/middleware/body_limit/
mod.rs

1use super::Middleware;
2use crate::error::AuthResult;
3use crate::types::{AuthRequest, AuthResponse};
4use async_trait::async_trait;
5
6/// Configuration for body size limit middleware.
7#[derive(Debug, Clone)]
8pub struct BodyLimitConfig {
9    /// Maximum body size in bytes. Defaults to 1 MB.
10    pub max_bytes: usize,
11
12    /// Whether the middleware is enabled.
13    pub enabled: bool,
14}
15
16impl Default for BodyLimitConfig {
17    fn default() -> Self {
18        Self {
19            max_bytes: 1_048_576, // 1 MB
20            enabled: true,
21        }
22    }
23}
24
25impl BodyLimitConfig {
26    #[must_use]
27    pub fn new() -> Self {
28        Self::default()
29    }
30
31    #[must_use]
32    pub const fn max_bytes(mut self, max: usize) -> Self {
33        self.max_bytes = max;
34        self
35    }
36
37    #[must_use]
38    pub const fn enabled(mut self, enabled: bool) -> Self {
39        self.enabled = enabled;
40        self
41    }
42}
43
44/// Body size limit middleware.
45///
46/// Rejects requests whose body exceeds the configured maximum size.
47#[derive(Debug)]
48pub struct BodyLimitMiddleware {
49    config: BodyLimitConfig,
50}
51
52impl BodyLimitMiddleware {
53    #[must_use]
54    pub const fn new(config: BodyLimitConfig) -> Self {
55        Self { config }
56    }
57}
58
59#[async_trait]
60impl Middleware for BodyLimitMiddleware {
61    fn name(&self) -> &'static str {
62        "body-limit"
63    }
64
65    async fn before_request(&self, req: &AuthRequest) -> AuthResult<Option<AuthResponse>> {
66        if !self.config.enabled {
67            return Ok(None);
68        }
69
70        if let Some(body) = &req.body
71            && body.len() > self.config.max_bytes
72        {
73            return Ok(Some(AuthResponse::json(
74                413,
75                &crate::types::CodeMessageResponse {
76                    code: "BODY_TOO_LARGE",
77                    message: format!(
78                        "Request body exceeds maximum size of {} bytes",
79                        self.config.max_bytes
80                    ),
81                },
82            )?));
83        }
84
85        Ok(None)
86    }
87}
88
89// LCOV_EXCL_START
90#[cfg(test)]
91mod tests {
92    use super::*;
93    use crate::types::HttpMethod;
94    use std::collections::HashMap;
95
96    fn make_request_with_body(body_size: usize) -> AuthRequest {
97        AuthRequest::from_parts(
98            HttpMethod::Post,
99            "/sign-up/email".to_owned(),
100            HashMap::new(),
101            Some(vec![0u8; body_size]),
102            HashMap::new(),
103        )
104    }
105
106    // Rust-specific surface: Rust middleware implementations are library-specific behavior with no direct TS analogue.
107    #[tokio::test]
108    async fn test_body_limit_allows_within_limit() {
109        let mw = BodyLimitMiddleware::new(BodyLimitConfig::new().max_bytes(1024));
110        let req = make_request_with_body(512);
111        assert!(mw.before_request(&req).await.unwrap().is_none());
112    }
113
114    // Rust-specific surface: Rust middleware implementations are library-specific behavior with no direct TS analogue.
115    #[tokio::test]
116    async fn test_body_limit_allows_exact_limit() {
117        let mw = BodyLimitMiddleware::new(BodyLimitConfig::new().max_bytes(1024));
118        let req = make_request_with_body(1024);
119        assert!(mw.before_request(&req).await.unwrap().is_none());
120    }
121
122    // Rust-specific surface: Rust middleware implementations are library-specific behavior with no direct TS analogue.
123    #[tokio::test]
124    async fn test_body_limit_rejects_over_limit() {
125        let mw = BodyLimitMiddleware::new(BodyLimitConfig::new().max_bytes(1024));
126        let req = make_request_with_body(2048);
127        let resp = mw.before_request(&req).await.unwrap();
128        assert!(resp.is_some());
129        assert_eq!(resp.unwrap().status, 413);
130    }
131
132    // Rust-specific surface: Rust middleware implementations are library-specific behavior with no direct TS analogue.
133    #[tokio::test]
134    async fn test_body_limit_allows_no_body() {
135        let mw = BodyLimitMiddleware::new(BodyLimitConfig::new().max_bytes(1024));
136        let req = AuthRequest::from_parts(
137            HttpMethod::Get,
138            "/get-session".to_owned(),
139            HashMap::new(),
140            None,
141            HashMap::new(),
142        );
143        assert!(mw.before_request(&req).await.unwrap().is_none());
144    }
145
146    // Rust-specific surface: Rust middleware implementations are library-specific behavior with no direct TS analogue.
147    #[tokio::test]
148    async fn test_body_limit_disabled() {
149        let config = BodyLimitConfig::new().max_bytes(10).enabled(false);
150        let mw = BodyLimitMiddleware::new(config);
151        let req = make_request_with_body(1000);
152        assert!(mw.before_request(&req).await.unwrap().is_none());
153    }
154}
155// LCOV_EXCL_STOP