Skip to main content

sova_core/
limits.rs

1//! Route-scoped limits: [`MaxBody`], [`RequestTimeout`], [`Deadline`].
2
3use crate::human::{parse_bytes, parse_duration};
4use crate::route_value::RouteValue;
5use std::borrow::Cow;
6use std::time::{Duration, Instant};
7
8/// Max request body size for a route / router / app scope.
9#[derive(Debug, Clone, Copy)]
10pub struct MaxBody(pub usize);
11
12impl MaxBody {
13    pub fn bytes(n: usize) -> Self {
14        Self(n)
15    }
16
17    pub fn kib(n: usize) -> Self {
18        Self(n.saturating_mul(1024))
19    }
20
21    pub fn mib(n: usize) -> Self {
22        Self(n.saturating_mul(1024 * 1024))
23    }
24
25    pub fn parse(s: &str) -> Result<Self, String> {
26        Ok(Self(parse_bytes(s)?))
27    }
28}
29
30impl RouteValue for MaxBody {
31    fn label(&self) -> Cow<'static, str> {
32        Cow::Owned(format!("MaxBody({} bytes)", self.0))
33    }
34}
35
36/// Per-route request timeout (inner; app-level timeout in serve still applies).
37#[derive(Debug, Clone, Copy)]
38pub struct RequestTimeout(pub Duration);
39
40impl RequestTimeout {
41    pub fn from_secs(secs: u64) -> Self {
42        Self(Duration::from_secs(secs))
43    }
44
45    pub fn parse(s: &str) -> Result<Self, String> {
46        Ok(Self(parse_duration(s)?))
47    }
48}
49
50impl RouteValue for RequestTimeout {
51    fn label(&self) -> Cow<'static, str> {
52        Cow::Owned(format!("RequestTimeout({:?})", self.0))
53    }
54}
55
56/// Absolute instant when the request budget expires (app and/or route timeout).
57///
58/// Set by the server / router so outbound clients can use [`Self::remaining`].
59#[derive(Debug, Clone, Copy)]
60pub struct Deadline(pub Instant);
61
62impl Deadline {
63    pub fn at(instant: Instant) -> Self {
64        Self(instant)
65    }
66
67    pub fn after(dur: Duration) -> Self {
68        Self(Instant::now() + dur)
69    }
70
71    /// Time left until the deadline; `Duration::ZERO` if already past.
72    pub fn remaining(&self) -> Duration {
73        self.0.saturating_duration_since(Instant::now())
74    }
75}
76
77/// Keep the earlier (stricter) deadline on `req`.
78pub fn tighten_deadline(req: &mut crate::Request, until: Instant) {
79    match req.get::<Deadline>() {
80        Some(d) if d.0 <= until => {}
81        _ => req.set(Deadline(until)),
82    }
83}