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}