http_types/trace/server_timing/
mod.rs

1//! Metrics and descriptions for the given request-response cycle.
2//!
3//! # Examples
4//!
5//! ```
6//! # fn main() -> http_types::Result<()> {
7//! #
8//! use http_types::Response;
9//! use http_types::trace::{ServerTiming, Metric};
10//!
11//! let mut timings = ServerTiming::new();
12//! timings.push(Metric::new("server".to_owned(), None, None)?);
13//!
14//! let mut res = Response::new(200);
15//! timings.apply(&mut res);
16//!
17//! let timings = ServerTiming::from_headers(res)?.unwrap();
18//! let entry = timings.iter().next().unwrap();
19//! assert_eq!(entry.name(), "server");
20//! #
21//! # Ok(()) }
22//! ```
23
24mod metric;
25mod parse;
26
27pub use metric::Metric;
28use parse::parse_header;
29
30use std::convert::AsMut;
31use std::fmt::Write;
32use std::iter::Iterator;
33use std::option;
34use std::slice;
35
36use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, SERVER_TIMING};
37
38/// Metrics and descriptions for the given request-response cycle.
39///
40/// # Specifications
41///
42/// - [Server Timing (Working Draft)](https://w3c.github.io/server-timing/#the-server-timing-header-field)
43///
44/// # Examples
45///
46/// ```
47/// # fn main() -> http_types::Result<()> {
48/// #
49/// use http_types::Response;
50/// use http_types::trace::{ServerTiming, Metric};
51///
52/// let mut timings = ServerTiming::new();
53/// timings.push(Metric::new("server".to_owned(), None, None)?);
54///
55/// let mut res = Response::new(200);
56/// timings.apply(&mut res);
57///
58/// let timings = ServerTiming::from_headers(res)?.unwrap();
59/// let entry = timings.iter().next().unwrap();
60/// assert_eq!(entry.name(), "server");
61/// #
62/// # Ok(()) }
63/// ```
64#[derive(Debug)]
65pub struct ServerTiming {
66    timings: Vec<Metric>,
67}
68
69impl ServerTiming {
70    /// Create a new instance of `ServerTiming`.
71    pub fn new() -> Self {
72        Self { timings: vec![] }
73    }
74
75    /// Create a new instance from headers.
76    pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
77        let mut timings = vec![];
78        let headers = match headers.as_ref().get(SERVER_TIMING) {
79            Some(headers) => headers,
80            None => return Ok(None),
81        };
82
83        for value in headers {
84            parse_header(value.as_str(), &mut timings)?;
85        }
86        Ok(Some(Self { timings }))
87    }
88
89    /// Sets the `Server-Timing` header.
90    pub fn apply(&self, mut headers: impl AsMut<Headers>) {
91        headers.as_mut().insert(SERVER_TIMING, self.value());
92    }
93
94    /// Get the `HeaderName`.
95    pub fn name(&self) -> HeaderName {
96        SERVER_TIMING
97    }
98
99    /// Get the `HeaderValue`.
100    pub fn value(&self) -> HeaderValue {
101        let mut output = String::new();
102        for (n, timing) in self.timings.iter().enumerate() {
103            let timing: HeaderValue = timing.clone().into();
104            match n {
105                0 => write!(output, "{}", timing).unwrap(),
106                _ => write!(output, ", {}", timing).unwrap(),
107            };
108        }
109
110        // SAFETY: the internal string is validated to be ASCII.
111        unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
112    }
113
114    /// Push an entry into the list of entries.
115    pub fn push(&mut self, entry: Metric) {
116        self.timings.push(entry);
117    }
118
119    /// An iterator visiting all server timings.
120    pub fn iter(&self) -> Iter<'_> {
121        Iter {
122            inner: self.timings.iter(),
123        }
124    }
125
126    /// An iterator visiting all server timings.
127    pub fn iter_mut(&mut self) -> IterMut<'_> {
128        IterMut {
129            inner: self.timings.iter_mut(),
130        }
131    }
132}
133
134impl IntoIterator for ServerTiming {
135    type Item = Metric;
136    type IntoIter = IntoIter;
137
138    #[inline]
139    fn into_iter(self) -> Self::IntoIter {
140        IntoIter {
141            inner: self.timings.into_iter(),
142        }
143    }
144}
145
146impl<'a> IntoIterator for &'a ServerTiming {
147    type Item = &'a Metric;
148    type IntoIter = Iter<'a>;
149
150    // #[inline]serv
151    fn into_iter(self) -> Self::IntoIter {
152        self.iter()
153    }
154}
155
156impl<'a> IntoIterator for &'a mut ServerTiming {
157    type Item = &'a mut Metric;
158    type IntoIter = IterMut<'a>;
159
160    #[inline]
161    fn into_iter(self) -> Self::IntoIter {
162        self.iter_mut()
163    }
164}
165
166/// A borrowing iterator over entries in `ServerTiming`.
167#[derive(Debug)]
168pub struct IntoIter {
169    inner: std::vec::IntoIter<Metric>,
170}
171
172impl Iterator for IntoIter {
173    type Item = Metric;
174
175    fn next(&mut self) -> Option<Self::Item> {
176        self.inner.next()
177    }
178
179    #[inline]
180    fn size_hint(&self) -> (usize, Option<usize>) {
181        self.inner.size_hint()
182    }
183}
184
185/// A lending iterator over entries in `ServerTiming`.
186#[derive(Debug)]
187pub struct Iter<'a> {
188    inner: slice::Iter<'a, Metric>,
189}
190
191impl<'a> Iterator for Iter<'a> {
192    type Item = &'a Metric;
193
194    fn next(&mut self) -> Option<Self::Item> {
195        self.inner.next()
196    }
197
198    #[inline]
199    fn size_hint(&self) -> (usize, Option<usize>) {
200        self.inner.size_hint()
201    }
202}
203
204/// A mutable iterator over entries in `ServerTiming`.
205#[derive(Debug)]
206pub struct IterMut<'a> {
207    inner: slice::IterMut<'a, Metric>,
208}
209
210impl<'a> Iterator for IterMut<'a> {
211    type Item = &'a mut Metric;
212
213    fn next(&mut self) -> Option<Self::Item> {
214        self.inner.next()
215    }
216
217    #[inline]
218    fn size_hint(&self) -> (usize, Option<usize>) {
219        self.inner.size_hint()
220    }
221}
222
223impl ToHeaderValues for ServerTiming {
224    type Iter = option::IntoIter<HeaderValue>;
225    fn to_header_values(&self) -> crate::Result<Self::Iter> {
226        // A HeaderValue will always convert into itself.
227        Ok(self.value().to_header_values().unwrap())
228    }
229}
230
231#[cfg(test)]
232mod test {
233    use super::*;
234    use crate::headers::Headers;
235
236    #[test]
237    fn smoke() -> crate::Result<()> {
238        let mut timings = ServerTiming::new();
239        timings.push(Metric::new("server".to_owned(), None, None)?);
240
241        let mut headers = Headers::new();
242        timings.apply(&mut headers);
243
244        let timings = ServerTiming::from_headers(headers)?.unwrap();
245        let entry = timings.iter().next().unwrap();
246        assert_eq!(entry.name(), "server");
247        Ok(())
248    }
249
250    #[test]
251    fn to_header_values() -> crate::Result<()> {
252        let mut timings = ServerTiming::new();
253        timings.push(Metric::new("server".to_owned(), None, None)?);
254
255        let mut headers = Headers::new();
256        timings.apply(&mut headers);
257
258        let timings = ServerTiming::from_headers(headers)?.unwrap();
259        let entry = timings.iter().next().unwrap();
260        assert_eq!(entry.name(), "server");
261        Ok(())
262    }
263
264    #[test]
265    fn bad_request_on_parse_error() {
266        let mut headers = Headers::new();
267        headers.insert(SERVER_TIMING, "server; <nori ate your param omnom>");
268        let err = ServerTiming::from_headers(headers).unwrap_err();
269        assert_eq!(err.status(), 400);
270    }
271}