Now let's take a silly one
1use axum::extract::Request;
2use axum::middleware::Next;
3use axum::response::Response;
4use http::Version;
5use tracing::Instrument;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum NegotiatedProtocol {
9 H1,
10 H2,
11 H3,
12}
13
14impl NegotiatedProtocol {
15 pub fn as_str(self) -> &'static str {
16 match self {
17 NegotiatedProtocol::H1 => "h1",
18 NegotiatedProtocol::H2 => "h2",
19 NegotiatedProtocol::H3 => "h3",
20 }
21 }
22
23 pub fn from_version(version: Version) -> Option<Self> {
24 match version {
25 Version::HTTP_10 | Version::HTTP_11 => Some(NegotiatedProtocol::H1),
26 Version::HTTP_2 => Some(NegotiatedProtocol::H2),
27 Version::HTTP_3 => Some(NegotiatedProtocol::H3),
28 _ => None,
29 }
30 }
31}
32
33pub(crate) async fn tag(mut request: Request, next: Next) -> Response {
34 let protocol = NegotiatedProtocol::from_version(request.version());
35 if let Some(protocol) = protocol {
36 request.extensions_mut().insert(protocol);
37 }
38 let span = tracing::debug_span!(
39 "request",
40 protocol = protocol
41 .map(NegotiatedProtocol::as_str)
42 .unwrap_or("unknown")
43 );
44 next.run(request).instrument(span).await
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn each_http_version_maps_to_its_negotiated_protocol() {
53 assert_eq!(
54 NegotiatedProtocol::from_version(Version::HTTP_11),
55 Some(NegotiatedProtocol::H1)
56 );
57 assert_eq!(
58 NegotiatedProtocol::from_version(Version::HTTP_10),
59 Some(NegotiatedProtocol::H1)
60 );
61 assert_eq!(
62 NegotiatedProtocol::from_version(Version::HTTP_2),
63 Some(NegotiatedProtocol::H2)
64 );
65 assert_eq!(
66 NegotiatedProtocol::from_version(Version::HTTP_3),
67 Some(NegotiatedProtocol::H3)
68 );
69 assert_eq!(NegotiatedProtocol::from_version(Version::HTTP_09), None);
70 }
71
72 #[test]
73 fn the_label_is_stable_for_logging_and_metrics() {
74 assert_eq!(NegotiatedProtocol::H1.as_str(), "h1");
75 assert_eq!(NegotiatedProtocol::H2.as_str(), "h2");
76 assert_eq!(NegotiatedProtocol::H3.as_str(), "h3");
77 }
78}