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::{ErrorExt, OpaqueError};
10use rama_core::username::{ComposeError, Composer, UsernameLabelWriter};
11use rama_utils::macros::match_ignore_ascii_case_str;
12use std::fmt;
13
14mod service;
15#[doc(inline)]
16pub use service::DnsResolveModeService;
17
18mod layer;
19#[doc(inline)]
20pub use layer::DnsResolveModeLayer;
21
22mod username_parser;
23#[doc(inline)]
24pub use username_parser::DnsResolveModeUsernameParser;
25
26#[derive(Debug, Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
27/// A vanity [`Extensions`] type for others to easily check if eager DNS resolution is enabled.
28///
29/// [`Extensions`]: rama_core::context::Extensions
30pub struct DnsResolveMode(ResolveMode);
31
32impl fmt::Display for DnsResolveMode {
33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
34        match self.0 {
35            ResolveMode::Eager => write!(f, "eager"),
36            ResolveMode::Lazy => write!(f, "lazy"),
37        }
38    }
39}
40
41impl DnsResolveMode {
42    /// Creates a new "eager" resolve mod
43    pub const fn eager() -> Self {
44        Self(ResolveMode::Eager)
45    }
46
47    /// Creates a new "lazy" resolve mode
48    pub const fn lazy() -> Self {
49        Self(ResolveMode::Lazy)
50    }
51
52    /// Returns `true` if the [`DnsResolveMode`] is "eager".
53    pub fn is_eager(&self) -> bool {
54        match self.0 {
55            ResolveMode::Eager => true,
56            ResolveMode::Lazy => false,
57        }
58    }
59
60    /// Returns `true` if the [`DnsResolveMode`] is "lazy".
61    pub fn is_lazy(&self) -> bool {
62        match self.0 {
63            ResolveMode::Eager => false,
64            ResolveMode::Lazy => true,
65        }
66    }
67}
68
69impl std::str::FromStr for DnsResolveMode {
70    type Err = OpaqueError;
71
72    fn from_str(value: &str) -> Result<Self, Self::Err> {
73        Self::try_from(value)
74    }
75}
76
77impl TryFrom<&str> for DnsResolveMode {
78    type Error = OpaqueError;
79
80    fn try_from(value: &str) -> Result<Self, Self::Error> {
81        match_ignore_ascii_case_str! {
82            match (value) {
83                "eager" => Ok(DnsResolveMode::eager()),
84                "lazy" => Ok(DnsResolveMode::lazy()),
85                _ => Err(OpaqueError::from_display("Invalid DNS resolve mode: unknown str")),
86            }
87        }
88    }
89}
90
91impl TryFrom<&HeaderValue> for DnsResolveMode {
92    type Error = OpaqueError;
93
94    fn try_from(value: &HeaderValue) -> Result<Self, Self::Error> {
95        match value.to_str() {
96            Ok(value) => Self::try_from(value),
97            Err(err) => Err(err.context("Invalid DNS resolve mode")),
98        }
99    }
100}
101
102#[derive(Debug, Clone, Default, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
103enum ResolveMode {
104    Eager,
105    #[default]
106    Lazy,
107}
108
109impl<const SEPARATOR: char> UsernameLabelWriter<SEPARATOR> for DnsResolveMode {
110    fn write_labels(&self, composer: &mut Composer<SEPARATOR>) -> Result<(), ComposeError> {
111        composer.write_label("dns")?;
112        match self.0 {
113            ResolveMode::Eager => composer.write_label("eager"),
114            ResolveMode::Lazy => composer.write_label("lazy"),
115        }
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::*;
122    use rama_core::context::Extensions;
123    use rama_core::username::{compose_username, parse_username};
124
125    #[test]
126    fn parse_username_label_compose_parse_dns_resolve_mode() {
127        let test_cases = [DnsResolveMode::eager(), DnsResolveMode::lazy()];
128        for test_case in test_cases {
129            let fmt_username = compose_username("john".to_owned(), test_case).unwrap();
130            let mut ext = Extensions::new();
131            let username = parse_username(
132                &mut ext,
133                DnsResolveModeUsernameParser::default(),
134                fmt_username,
135            )
136            .unwrap();
137            assert_eq!("john", username);
138            let result = ext.get::<DnsResolveMode>().unwrap();
139            assert_eq!(test_case, *result);
140        }
141    }
142}