alibi_core/middleware/body_limit/
mod.rs1use super::Middleware;
2use crate::error::AuthResult;
3use crate::types::{AuthRequest, AuthResponse};
4use async_trait::async_trait;
5
6#[derive(Debug, Clone)]
8pub struct BodyLimitConfig {
9 pub max_bytes: usize,
11
12 pub enabled: bool,
14}
15
16impl Default for BodyLimitConfig {
17 fn default() -> Self {
18 Self {
19 max_bytes: 1_048_576, 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#[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#[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 #[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 #[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 #[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 #[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 #[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