srv_rs/
record.rs

1//! SRV records.
2
3use http::uri::{PathAndQuery, Scheme, Uri};
4use rand::Rng;
5use std::{cmp::Reverse, convert::TryInto, fmt::Display};
6
7/// Representation of types that contain the fields of a SRV record.
8pub trait SrvRecord {
9    /// Type representing the SRV record's target. Must implement `Display` so
10    /// it can be used to create a `Uri`.
11    type Target: Display + ?Sized;
12
13    /// Gets a SRV record's target.
14    fn target(&self) -> &Self::Target;
15
16    /// Gets a SRV record's port.
17    fn port(&self) -> u16;
18
19    /// Gets a SRV record's priority.
20    fn priority(&self) -> u16;
21
22    /// Gets a SRV record's weight.
23    fn weight(&self) -> u16;
24
25    /// Parses a SRV record into a URI with a given scheme (e.g. https) and
26    /// `path_and_query` (used as a suffix in the URI).
27    ///
28    /// ```
29    /// # fn srv_record_parse() -> Result<(), http::Error> {
30    /// use srv_rs::{resolver::libresolv::LibResolvSrvRecord, SrvRecord};
31    /// let record = LibResolvSrvRecord {
32    ///     priority: 1,
33    ///     weight: 100,
34    ///     port: 8211,
35    ///     target: String::from("srv-client-rust.deshaw.org"),
36    /// };
37    /// assert_eq!(
38    ///     &record.parse("https", "/")?.to_string(),
39    ///     "https://srv-client-rust.deshaw.org:8211/"
40    /// );
41    /// assert_eq!(
42    ///     &record.parse("http", "/bar")?.to_string(),
43    ///     "http://srv-client-rust.deshaw.org:8211/bar"
44    /// );
45    /// # Ok(())
46    /// # }
47    /// ```
48    fn parse(
49        &self,
50        scheme: impl TryInto<Scheme, Error = impl Into<http::Error>>,
51        path_and_query: impl TryInto<PathAndQuery, Error = impl Into<http::Error>>,
52    ) -> Result<Uri, http::Error> {
53        let scheme: Scheme = scheme.try_into().map_err(Into::into)?;
54        let path_and_query: PathAndQuery = path_and_query.try_into().map_err(Into::into)?;
55        Uri::builder()
56            .scheme(scheme)
57            .path_and_query(path_and_query)
58            .authority(format!("{}:{}", self.target(), self.port()).as_str())
59            .build()
60    }
61
62    /// Generates a key to sort a SRV record by priority and weight per RFC 2782.
63    fn sort_key(&self, rng: impl Rng) -> (u16, Reverse<u32>) {
64        sort_key(self.priority(), self.weight(), rng)
65    }
66}
67
68/// Generates a key to sort a SRV record by priority and weight per RFC 2782.
69pub(crate) fn sort_key(priority: u16, weight: u16, mut rng: impl Rng) -> (u16, Reverse<u32>) {
70    // Sort ascending by priority, then descending (hence `Reverse`) by randomized weight
71    let rand = rng.gen::<u16>() as u32;
72    (priority, Reverse(weight as u32 * rand))
73}