cat_gateway/service/common/types/headers/
retry_after.rs

1//! Retry After header type
2//!
3//! This is an active header which expects to be provided in a response.
4
5use std::{borrow::Cow, fmt::Display};
6
7use chrono::{DateTime, Utc};
8use poem::http::HeaderValue;
9use poem_openapi::{
10    registry::{MetaSchema, MetaSchemaRef},
11    types::{ToHeader, Type},
12};
13use serde_json::Value;
14
15/// Parameter which describes the possible choices for a Retry-After header field.
16#[derive(Debug)]
17#[allow(dead_code)] // Its OK if all these variants are not used.
18pub(crate) enum RetryAfterHeader {
19    /// Http Date
20    Date(DateTime<Utc>),
21    /// Interval in seconds.
22    Seconds(u64),
23}
24
25/// Parameter which lets us set the retry header, or use some default.
26/// Needed, because its valid to exclude the retry header specifically.
27/// This is also due to the way Poem handles optional headers.
28#[derive(Debug)]
29#[allow(dead_code)] // Its OK if all these variants are not used.
30pub(crate) enum RetryAfterOption {
31    /// Use a default Retry After header value
32    Default,
33    /// Don't include the Retry After header value in the response.
34    None,
35    /// Use a specific Retry After header value
36    Some(RetryAfterHeader),
37}
38
39impl Display for RetryAfterHeader {
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41        match self {
42            RetryAfterHeader::Date(date_time) => {
43                let http_date = date_time.format("%a, %d %b %Y %T GMT").to_string();
44                write!(f, "{http_date}")
45            },
46            RetryAfterHeader::Seconds(secs) => write!(f, "{secs}"),
47        }
48    }
49}
50
51impl Default for RetryAfterHeader {
52    fn default() -> Self {
53        Self::Seconds(300)
54    }
55}
56
57impl Type for RetryAfterHeader {
58    type RawElementValueType = Self;
59    type RawValueType = Self;
60
61    const IS_REQUIRED: bool = true;
62
63    fn name() -> Cow<'static, str> {
64        "string(http-date || integer)".into()
65    }
66
67    fn schema_ref() -> MetaSchemaRef {
68        MetaSchemaRef::Inline(Box::new(MetaSchema::new_with_format(
69            "string",
70            "http-date || integer",
71        )))
72        .merge(MetaSchema {
73            title: Some("Retry-After Header".to_owned()),
74            description: Some(
75                "Http Date or Interval in seconds.
76Valid formats:
77
78* `Retry-After: <http-date>`
79* `Retry-After: <delay-seconds>`
80
81See: <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After>",
82            ),
83            example: Some(Value::String("300".to_string())),
84            ..poem_openapi::registry::MetaSchema::ANY
85        })
86    }
87
88    fn as_raw_value(&self) -> Option<&Self::RawValueType> {
89        Some(self)
90    }
91
92    fn raw_element_iter<'a>(
93        &'a self,
94    ) -> Box<dyn Iterator<Item = &'a Self::RawElementValueType> + 'a> {
95        Box::new(self.as_raw_value().into_iter())
96    }
97}
98
99impl ToHeader for RetryAfterHeader {
100    fn to_header(&self) -> Option<HeaderValue> {
101        HeaderValue::from_str(&self.to_string()).ok()
102    }
103}