Skip to main content

rama_http/layer/dns/dns_resolve/
mod.rs

1//! This module contains the [`DnsResolveModeLayer`] and [`DnsResolveMode`] types.
2//!
3//! These types can be used to opt-in for eager DNS resolution,
4//! which will resolve domain names to IP addresses even when not needed.
5//! For example resolving them to make a connection to a target server over a proxy
6//! by IP address instead of domain name.
7
8use crate::HeaderValue;
9use rama_core::error::BoxErrorExt as _;
10use rama_core::error::{BoxError, ErrorContext as _, ErrorExt};
11use rama_core::extensions::Extension;
12use rama_core::username::{ComposeError, Composer, UsernameLabelWriter};
13use rama_utils::macros::match_ignore_ascii_case_str;
14use std::fmt;
15
16mod service;
17#[doc(inline)]
18pub use service::DnsResolveModeService;
19
20mod layer;
21#[doc(inline)]
22pub use layer::DnsResolveModeLayer;
23
24mod username_parser;
25#[doc(inline)]
26pub use username_parser::DnsResolveModeUsernameParser;
27
28#[derive(Debug, Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Extension)]
29#[extension(tags(http, dns))]
30/// A vanity [`Extensions`] type for others to easily check if eager DNS resolution is enabled.
31///
32/// [`Extensions`]: rama_core::extensions::Extensions
33pub struct DnsResolveMode(ResolveMode);
34
35impl fmt::Display for DnsResolveMode {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        match self.0 {
38            ResolveMode::Eager => write!(f, "eager"),
39            ResolveMode::Lazy => write!(f, "lazy"),
40        }
41    }
42}
43
44impl DnsResolveMode {
45    /// Creates a new "eager" resolve mod
46    #[must_use]
47    pub const fn eager() -> Self {
48        Self(ResolveMode::Eager)
49    }
50
51    /// Creates a new "lazy" resolve mode
52    #[must_use]
53    pub const fn lazy() -> Self {
54        Self(ResolveMode::Lazy)
55    }
56
57    /// Returns `true` if the [`DnsResolveMode`] is "eager".
58    #[must_use]
59    pub fn is_eager(&self) -> bool {
60        match self.0 {
61            ResolveMode::Eager => true,
62            ResolveMode::Lazy => false,
63        }
64    }
65
66    /// Returns `true` if the [`DnsResolveMode`] is "lazy".
67    #[must_use]
68    pub fn is_lazy(&self) -> bool {
69        match self.0 {
70            ResolveMode::Eager => false,
71            ResolveMode::Lazy => true,
72        }
73    }
74}
75
76impl std::str::FromStr for DnsResolveMode {
77    type Err = BoxError;
78
79    #[inline(always)]
80    fn from_str(value: &str) -> Result<Self, Self::Err> {
81        Self::try_from(value)
82    }
83}
84
85impl TryFrom<&str> for DnsResolveMode {
86    type Error = BoxError;
87
88    fn try_from(value: &str) -> Result<Self, Self::Error> {
89        match_ignore_ascii_case_str! {
90            match (value) {
91                "eager" => Ok(Self::eager()),
92                "lazy" => Ok(Self::lazy()),
93                _ => Err(BoxError::from_static_str("Invalid DNS resolve mode: unknown str")),
94            }
95        }
96    }
97}
98
99impl TryFrom<&HeaderValue> for DnsResolveMode {
100    type Error = BoxError;
101
102    fn try_from(value: &HeaderValue) -> Result<Self, Self::Error> {
103        match value.to_str() {
104            Ok(value) => Self::try_from(value).into_box_error(),
105            Err(err) => Err(err.context("Invalid DNS resolve mode")),
106        }
107    }
108}
109
110#[derive(Debug, Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
111enum ResolveMode {
112    Eager,
113    #[default]
114    Lazy,
115}
116
117impl<const SEPARATOR: char> UsernameLabelWriter<SEPARATOR> for DnsResolveMode {
118    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
119        composer.write_label("dns")?;
120        match self.0 {
121            ResolveMode::Eager => composer.write_label("eager"),
122            ResolveMode::Lazy => composer.write_label("lazy"),
123        }
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130    use rama_core::extensions::Extensions;
131    use rama_core::username::{compose_username, parse_username};
132
133    #[test]
134    fn parse_username_label_compose_parse_dns_resolve_mode() {
135        let test_cases = [DnsResolveMode::eager(), DnsResolveMode::lazy()];
136        for test_case in test_cases {
137            let fmt_username = compose_username("john".to_owned(), test_case).unwrap();
138            let ext = Extensions::new();
139            let username =
140                parse_username(&ext, DnsResolveModeUsernameParser::default(), fmt_username)
141                    .unwrap();
142            assert_eq!("john", username);
143            let result = ext.get_ref::<DnsResolveMode>().unwrap();
144            assert_eq!(test_case, *result);
145        }
146    }
147}