1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 // TLS Application-Layer Protocol Negotiation (ALPN) Protocol is defined in
15 // [`RFC7301`]. `AlpnProtocol` contains some protocols used in HTTP, which
16 // registered in [`IANA`].
17 //
18 // [`RFC7301`]: https://www.rfc-editor.org/rfc/rfc7301.html#section-3
19 // [`IANA`]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
20 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
21 pub(crate) struct AlpnProtocol(Inner);
22 
23 #[derive(Clone, Copy, Debug, Eq, PartialEq)]
24 enum Inner {
25     HTTP09,
26     HTTP10,
27     HTTP11,
28     SPDY1,
29     SPDY2,
30     SPDY3,
31     H2,
32     H2C,
33     H3,
34 }
35 
36 impl AlpnProtocol {
37     /// `HTTP/0.9` in [`IANA Registration`].
38     ///
39     /// [`IANA Registration`]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
40     pub(crate) const HTTP09: Self = Self(Inner::HTTP09);
41 
42     /// `HTTP/1.0` in [`IANA Registration`].
43     ///
44     /// [`IANA Registration`]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
45     pub(crate) const HTTP10: Self = Self(Inner::HTTP10);
46 
47     /// `HTTP/1.1` in [`IANA Registration`].
48     ///
49     /// [`IANA Registration`]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
50     pub(crate) const HTTP11: Self = Self(Inner::HTTP11);
51 
52     /// `SPDY/1` in [`IANA Registration`].
53     ///
54     /// [`IANA Registration`]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
55     pub(crate) const SPDY1: Self = Self(Inner::SPDY1);
56 
57     /// `SPDY/2` in [`IANA Registration`].
58     ///
59     /// [`IANA Registration`]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
60     pub(crate) const SPDY2: Self = Self(Inner::SPDY2);
61 
62     /// `SPDY/3` in [`IANA Registration`].
63     ///
64     /// [`IANA Registration`]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
65     pub(crate) const SPDY3: Self = Self(Inner::SPDY3);
66 
67     /// `HTTP/2 over TLS` in [`IANA Registration`].
68     ///
69     /// [`IANA Registration`]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
70     pub(crate) const H2: Self = Self(Inner::H2);
71 
72     /// `HTTP/2 over TCP` in [`IANA Registration`].
73     ///
74     /// [`IANA Registration`]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
75     pub(crate) const H2C: Self = Self(Inner::H2C);
76 
77     /// `HTTP/3` in [`IANA Registration`].
78     ///
79     /// [`IANA Registration`]: https://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml#alpn-protocol-ids
80     pub(crate) const H3: Self = Self(Inner::H3);
81 
82     /// Gets ALPN “wire format”, which consists protocol name prefixed by its
83     /// byte length.
wire_format_bytes(&self) -> &[u8]84     pub(crate) fn wire_format_bytes(&self) -> &[u8] {
85         match *self {
86             AlpnProtocol::HTTP09 => b"\x08http/0.9",
87             AlpnProtocol::HTTP10 => b"\x08http/1.0",
88             AlpnProtocol::HTTP11 => b"\x08http/1.1",
89             AlpnProtocol::SPDY1 => b"\x06spdy/1",
90             AlpnProtocol::SPDY2 => b"\x06spdy/2",
91             AlpnProtocol::SPDY3 => b"\x06spdy/3",
92             AlpnProtocol::H2 => b"\x02h2",
93             AlpnProtocol::H2C => b"\x03h2c",
94             AlpnProtocol::H3 => b"\x02h3",
95         }
96     }
97 }
98 
99 /// `AlpnProtocolList` consists of a sequence of supported protocol names
100 /// prefixed by their byte length.
101 #[derive(Debug, Default)]
102 pub(crate) struct AlpnProtocolList(Vec<u8>);
103 
104 impl AlpnProtocolList {
105     /// Creates a new `AlpnProtocolList`.
new() -> Self106     pub(crate) fn new() -> Self {
107         AlpnProtocolList(vec![])
108     }
109 
extend_from_slice(&mut self, other: &[u8])110     fn extend_from_slice(&mut self, other: &[u8]) {
111         self.0.extend_from_slice(other);
112     }
113 
114     /// Adds an `AlpnProtocol`.
extend(mut self, protocol: AlpnProtocol) -> Self115     pub(crate) fn extend(mut self, protocol: AlpnProtocol) -> Self {
116         self.extend_from_slice(protocol.wire_format_bytes());
117         self
118     }
119 
120     /// Gets `&[u8]` of ALPN “wire format”, which consists of a sequence of
121     /// supported protocol names prefixed by their byte length.
as_slice(&self) -> &[u8]122     pub(crate) fn as_slice(&self) -> &[u8] {
123         self.0.as_slice()
124     }
125 }
126 
127 #[cfg(test)]
128 mod ut_alpn {
129     use crate::util::{AlpnProtocol, AlpnProtocolList};
130 
131     /// UT test cases for `AlpnProtocol::wire_format_bytes`.
132     ///
133     /// # Brief
134     /// 1. Creates a `AlpnProtocol`.
135     /// 2. Gets `&[u8]` by AlpnProtocol::wire_format_bytes.
136     /// 3. Checks whether the result is correct.
137     #[test]
ut_alpn_as_use_bytes()138     fn ut_alpn_as_use_bytes() {
139         assert_eq!(AlpnProtocol::HTTP09.wire_format_bytes(), b"\x08http/0.9");
140         assert_eq!(AlpnProtocol::HTTP10.wire_format_bytes(), b"\x08http/1.0");
141         assert_eq!(AlpnProtocol::HTTP11.wire_format_bytes(), b"\x08http/1.1");
142         assert_eq!(AlpnProtocol::SPDY1.wire_format_bytes(), b"\x06spdy/1");
143         assert_eq!(AlpnProtocol::SPDY2.wire_format_bytes(), b"\x06spdy/2");
144         assert_eq!(AlpnProtocol::SPDY3.wire_format_bytes(), b"\x06spdy/3");
145         assert_eq!(AlpnProtocol::H2.wire_format_bytes(), b"\x02h2");
146         assert_eq!(AlpnProtocol::H2C.wire_format_bytes(), b"\x03h2c");
147         assert_eq!(AlpnProtocol::H3.wire_format_bytes(), b"\x02h3");
148     }
149 
150     /// UT test cases for `AlpnProtocol::clone`.
151     ///
152     /// # Brief
153     /// 1. Creates a `AlpnProtocol`.
154     /// 2. Compares the cloned values.
155     /// 3. Checks whether the result is correct.
156     #[test]
ut_alpn_clone()157     fn ut_alpn_clone() {
158         assert_eq!(AlpnProtocol::HTTP09, AlpnProtocol::HTTP09.clone());
159     }
160 
161     /// UT test cases for `AlpnProtocolList::new`.
162     ///
163     /// # Brief
164     /// 1. Creates a `AlpnProtocolList` by `AlpnProtocolList::new`.
165     /// 2. Checks whether the result is correct.
166     #[test]
ut_alpn_list_new()167     fn ut_alpn_list_new() {
168         assert_eq!(AlpnProtocolList::new().as_slice(), b"");
169     }
170 
171     /// UT test cases for `AlpnProtocolList::default`.
172     ///
173     /// # Brief
174     /// 1. Creates a `AlpnProtocolList` by `AlpnProtocolList::default`.
175     /// 2. Checks whether the result is correct.
176     #[test]
ut_alpn_list_default()177     fn ut_alpn_list_default() {
178         assert_eq!(AlpnProtocolList::default().as_slice(), b"");
179     }
180 
181     /// UT test cases for `AlpnProtocolList::add`.
182     ///
183     /// # Brief
184     /// 1. Creates a `AlpnProtocolList` by `AlpnProtocolList::new`.
185     /// 2. Adds several `AlpnProtocol`s.
186     /// 3. Checks whether the result is correct.
187     #[test]
ut_alpn_list_add()188     fn ut_alpn_list_add() {
189         assert_eq!(
190             AlpnProtocolList::new()
191                 .extend(AlpnProtocol::SPDY1)
192                 .extend(AlpnProtocol::HTTP11)
193                 .as_slice(),
194             b"\x06spdy/1\x08http/1.1"
195         );
196     }
197 
198     /// UT test cases for `AlpnProtocolList::as_slice`.
199     ///
200     /// # Brief
201     /// 1. Creates a `AlpnProtocolList` and adds several `AlpnProtocol`s.
202     /// 2. Gets slice by `AlpnProtocolList::as_slice`.
203     /// 3. Checks whether the result is correct.
204     #[test]
ut_alpn_list_as_slice()205     fn ut_alpn_list_as_slice() {
206         assert_eq!(
207             AlpnProtocolList::new()
208                 .extend(AlpnProtocol::HTTP09)
209                 .as_slice(),
210             b"\x08http/0.9"
211         );
212     }
213 }
214