Skip to main content

rama_net/http/
version.rs

1//! HTTP protocol version, owned by rama-net as a protocol primitive.
2//!
3//! Mirrors the shape of the `http` crate's `Version` (opaque, with the
4//! standard `HTTP_09..HTTP_3` constants) so it is a drop-in replacement for
5//! `rama_http_types::Version`, which re-exports this type.
6
7use std::{error::Error, fmt};
8
9use rama_macros::Extension;
10
11/// Represents a version of the HTTP spec.
12#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
13pub struct Version(Http);
14
15#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Extension)]
16#[extension(tags(http))]
17/// Target http version
18///
19/// This can be set manually to enforce a specific version,
20/// otherwise this will be set automatically by things such
21/// tls alpn
22pub struct TargetHttpVersion(pub Version);
23
24/// HTTP version to use only when connection negotiation does not select one.
25///
26/// Unlike [`TargetHttpVersion`], this is not an egress requirement and must not
27/// constrain TLS ALPN. It becomes the target only after the transport has been
28/// established without negotiating an HTTP version.
29#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Extension)]
30#[extension(tags(http))]
31pub struct FallbackHttpVersion(pub Version);
32
33/// HTTP version carried by the request that initiated a connection attempt.
34///
35/// This is descriptive input context, not an egress requirement. Connectors
36/// must not use it to constrain protocol negotiation as they would a
37/// [`TargetHttpVersion`].
38#[derive(Debug, Copy, Clone, Default, PartialEq, Eq, Extension)]
39#[extension(tags(http))]
40pub struct HttpRequestVersion(pub Version);
41
42#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Debug)]
43enum Http {
44    Http09,
45    Http10,
46    Http11,
47    H2,
48    H3,
49}
50
51impl Version {
52    /// `HTTP/0.9`
53    pub const HTTP_09: Self = Self(Http::Http09);
54
55    /// `HTTP/1.0`
56    pub const HTTP_10: Self = Self(Http::Http10);
57
58    /// `HTTP/1.1`
59    pub const HTTP_11: Self = Self(Http::Http11);
60
61    /// `HTTP/2.0`
62    pub const HTTP_2: Self = Self(Http::H2);
63
64    /// `HTTP/3.0`
65    pub const HTTP_3: Self = Self(Http::H3);
66
67    /// The canonical `HTTP/x.y` text for this version.
68    #[must_use]
69    pub const fn as_str(self) -> &'static str {
70        match self.0 {
71            Http::Http09 => "HTTP/0.9",
72            Http::Http10 => "HTTP/1.0",
73            Http::Http11 => "HTTP/1.1",
74            Http::H2 => "HTTP/2.0",
75            Http::H3 => "HTTP/3.0",
76        }
77    }
78}
79
80impl fmt::Display for Version {
81    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82        f.write_str(self.as_str())
83    }
84}
85
86impl std::str::FromStr for Version {
87    type Err = InvalidVersion;
88
89    /// Parse from the canonical `HTTP/x.y` text or any of its
90    /// common aliases (`1.1`, `2`, `HTTP/2`, ...).
91    fn from_str(s: &str) -> Result<Self, Self::Err> {
92        Ok(match s {
93            "HTTP/0.9" | "0.9" => Self::HTTP_09,
94            "HTTP/1.0" | "1.0" => Self::HTTP_10,
95            "HTTP/1.1" | "1.1" => Self::HTTP_11,
96            "HTTP/2" | "HTTP/2.0" | "2" | "2.0" => Self::HTTP_2,
97            "HTTP/3" | "HTTP/3.0" | "3" | "3.0" => Self::HTTP_3,
98            _ => return Err(InvalidVersion::new()),
99        })
100    }
101}
102
103impl Default for Version {
104    #[inline]
105    fn default() -> Self {
106        Self::HTTP_11
107    }
108}
109
110impl core::fmt::Debug for Version {
111    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
112        f.write_str(self.as_str())
113    }
114}
115
116/// A possible error value when converting `Version` from bytes
117/// or a related type.
118#[derive(Debug, Default)]
119#[non_exhaustive]
120pub struct InvalidVersion;
121
122impl InvalidVersion {
123    #[inline(always)]
124    pub fn new() -> Self {
125        Self
126    }
127}
128
129impl fmt::Display for InvalidVersion {
130    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131        f.write_str("invalid HTTP version")
132    }
133}
134
135impl Error for InvalidVersion {}
136
137use rama_utils::macros::serde_str::impl_serde_str;
138
139impl_serde_str!(as_str Version);
140
141// `ApplicationProtocol` (ALPN) <-> `Version` conversions live in `rama-tls`
142// (which depends on both this crate and the TLS enum vocabulary).