/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tonic-0.14.5/src/metadata/key.rs
Line | Count | Source |
1 | | use bytes::Bytes; |
2 | | use http::header::HeaderName; |
3 | | use std::borrow::Borrow; |
4 | | use std::error::Error; |
5 | | use std::fmt; |
6 | | use std::marker::PhantomData; |
7 | | use std::str::FromStr; |
8 | | |
9 | | use super::encoding::{Ascii, Binary, ValueEncoding}; |
10 | | |
11 | | /// Represents a custom metadata field name. |
12 | | /// |
13 | | /// `MetadataKey` is used as the [`MetadataMap`] key. |
14 | | /// |
15 | | /// [`HeaderMap`]: struct.HeaderMap.html |
16 | | /// [`MetadataMap`]: struct.MetadataMap.html |
17 | | #[derive(Clone, Eq, PartialEq, Hash)] |
18 | | #[repr(transparent)] |
19 | | pub struct MetadataKey<VE: ValueEncoding> { |
20 | | // Note: There are unsafe transmutes that assume that the memory layout |
21 | | // of MetadataValue is identical to HeaderName |
22 | | pub(crate) inner: http::header::HeaderName, |
23 | | phantom: PhantomData<VE>, |
24 | | } |
25 | | |
26 | | /// A possible error when converting a `MetadataKey` from another type. |
27 | | #[derive(Debug, Default)] |
28 | | pub struct InvalidMetadataKey { |
29 | | _priv: (), |
30 | | } |
31 | | |
32 | | /// An ascii metadata key. |
33 | | pub type AsciiMetadataKey = MetadataKey<Ascii>; |
34 | | /// A binary metadata key. |
35 | | pub type BinaryMetadataKey = MetadataKey<Binary>; |
36 | | |
37 | | impl<VE: ValueEncoding> MetadataKey<VE> { |
38 | | /// Converts a slice of bytes to a `MetadataKey`. |
39 | | /// |
40 | | /// This function normalizes the input. |
41 | 0 | pub fn from_bytes(src: &[u8]) -> Result<Self, InvalidMetadataKey> { |
42 | 0 | match HeaderName::from_bytes(src) { |
43 | 0 | Ok(name) => { |
44 | 0 | if !VE::is_valid_key(name.as_str()) { |
45 | 0 | return Err(InvalidMetadataKey::new()); |
46 | 0 | } |
47 | | |
48 | 0 | Ok(MetadataKey { |
49 | 0 | inner: name, |
50 | 0 | phantom: PhantomData, |
51 | 0 | }) |
52 | | } |
53 | 0 | Err(_) => Err(InvalidMetadataKey::new()), |
54 | | } |
55 | 0 | } |
56 | | |
57 | | /// Converts a static string to a `MetadataKey`. |
58 | | /// |
59 | | /// This function panics when the static string is a invalid metadata key. |
60 | | /// |
61 | | /// This function requires the static string to only contain lowercase |
62 | | /// characters, numerals and symbols, as per the HTTP/2.0 specification |
63 | | /// and header names internal representation within this library. |
64 | | /// |
65 | | /// |
66 | | /// # Examples |
67 | | /// |
68 | | /// ``` |
69 | | /// # use tonic::metadata::*; |
70 | | /// // Parsing a metadata key |
71 | | /// let CUSTOM_KEY: &'static str = "custom-key"; |
72 | | /// |
73 | | /// let a = AsciiMetadataKey::from_bytes(b"custom-key").unwrap(); |
74 | | /// let b = AsciiMetadataKey::from_static(CUSTOM_KEY); |
75 | | /// assert_eq!(a, b); |
76 | | /// ``` |
77 | | /// |
78 | | /// ```should_panic |
79 | | /// # use tonic::metadata::*; |
80 | | /// // Parsing a metadata key that contains invalid symbols(s): |
81 | | /// AsciiMetadataKey::from_static("content{}{}length"); // This line panics! |
82 | | /// ``` |
83 | | /// |
84 | | /// ```should_panic |
85 | | /// # use tonic::metadata::*; |
86 | | /// // Parsing a metadata key that contains invalid uppercase characters. |
87 | | /// let a = AsciiMetadataKey::from_static("foobar"); |
88 | | /// let b = AsciiMetadataKey::from_static("FOOBAR"); // This line panics! |
89 | | /// ``` |
90 | | /// |
91 | | /// ```should_panic |
92 | | /// # use tonic::metadata::*; |
93 | | /// // Parsing a -bin metadata key as an Ascii key. |
94 | | /// let b = AsciiMetadataKey::from_static("hello-bin"); // This line panics! |
95 | | /// ``` |
96 | | /// |
97 | | /// ```should_panic |
98 | | /// # use tonic::metadata::*; |
99 | | /// // Parsing a non-bin metadata key as an Binary key. |
100 | | /// let b = BinaryMetadataKey::from_static("hello"); // This line panics! |
101 | | /// ``` |
102 | 0 | pub fn from_static(src: &'static str) -> Self { |
103 | 0 | let name = HeaderName::from_static(src); |
104 | 0 | if !VE::is_valid_key(name.as_str()) { |
105 | 0 | panic!("invalid metadata key") |
106 | 0 | } |
107 | | |
108 | 0 | MetadataKey { |
109 | 0 | inner: name, |
110 | 0 | phantom: PhantomData, |
111 | 0 | } |
112 | 0 | } |
113 | | |
114 | | /// Returns a `str` representation of the metadata key. |
115 | | /// |
116 | | /// The returned string will always be lower case. |
117 | | #[inline] |
118 | 0 | pub fn as_str(&self) -> &str { |
119 | 0 | self.inner.as_str() |
120 | 0 | } |
121 | | |
122 | | /// Converts a HeaderName reference to a MetadataKey. This method assumes |
123 | | /// that the caller has made sure that the header name has the correct |
124 | | /// "-bin" or non-"-bin" suffix, it does not validate its input. |
125 | | #[inline] |
126 | 0 | pub(crate) fn unchecked_from_header_name_ref(header_name: &HeaderName) -> &Self { |
127 | 0 | unsafe { &*(header_name as *const HeaderName as *const Self) } |
128 | 0 | } Unexecuted instantiation: <tonic::metadata::key::MetadataKey<tonic::metadata::encoding::Ascii>>::unchecked_from_header_name_ref Unexecuted instantiation: <tonic::metadata::key::MetadataKey<tonic::metadata::encoding::Binary>>::unchecked_from_header_name_ref |
129 | | |
130 | | /// Converts a HeaderName reference to a MetadataKey. This method assumes |
131 | | /// that the caller has made sure that the header name has the correct |
132 | | /// "-bin" or non-"-bin" suffix, it does not validate its input. |
133 | | #[inline] |
134 | 0 | pub(crate) fn unchecked_from_header_name(name: HeaderName) -> Self { |
135 | 0 | MetadataKey { |
136 | 0 | inner: name, |
137 | 0 | phantom: PhantomData, |
138 | 0 | } |
139 | 0 | } |
140 | | } |
141 | | |
142 | | impl<VE: ValueEncoding> FromStr for MetadataKey<VE> { |
143 | | type Err = InvalidMetadataKey; |
144 | | |
145 | 0 | fn from_str(s: &str) -> Result<Self, InvalidMetadataKey> { |
146 | 0 | MetadataKey::from_bytes(s.as_bytes()).map_err(|_| InvalidMetadataKey::new()) |
147 | 0 | } |
148 | | } |
149 | | |
150 | | impl<VE: ValueEncoding> AsRef<str> for MetadataKey<VE> { |
151 | 0 | fn as_ref(&self) -> &str { |
152 | 0 | self.as_str() |
153 | 0 | } |
154 | | } |
155 | | |
156 | | impl<VE: ValueEncoding> AsRef<[u8]> for MetadataKey<VE> { |
157 | 0 | fn as_ref(&self) -> &[u8] { |
158 | 0 | self.as_str().as_bytes() |
159 | 0 | } |
160 | | } |
161 | | |
162 | | impl<VE: ValueEncoding> Borrow<str> for MetadataKey<VE> { |
163 | 0 | fn borrow(&self) -> &str { |
164 | 0 | self.as_str() |
165 | 0 | } |
166 | | } |
167 | | |
168 | | impl<VE: ValueEncoding> fmt::Debug for MetadataKey<VE> { |
169 | 0 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
170 | 0 | fmt::Debug::fmt(self.as_str(), fmt) |
171 | 0 | } |
172 | | } |
173 | | |
174 | | impl<VE: ValueEncoding> fmt::Display for MetadataKey<VE> { |
175 | 0 | fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { |
176 | 0 | fmt::Display::fmt(self.as_str(), fmt) |
177 | 0 | } |
178 | | } |
179 | | |
180 | | impl InvalidMetadataKey { |
181 | | #[doc(hidden)] |
182 | 0 | pub fn new() -> InvalidMetadataKey { |
183 | 0 | Self::default() |
184 | 0 | } |
185 | | } |
186 | | |
187 | | impl<'a, VE: ValueEncoding> From<&'a MetadataKey<VE>> for MetadataKey<VE> { |
188 | 0 | fn from(src: &'a MetadataKey<VE>) -> MetadataKey<VE> { |
189 | 0 | src.clone() |
190 | 0 | } |
191 | | } |
192 | | |
193 | | impl<VE: ValueEncoding> From<MetadataKey<VE>> for Bytes { |
194 | | #[inline] |
195 | 0 | fn from(name: MetadataKey<VE>) -> Bytes { |
196 | 0 | Bytes::copy_from_slice(name.inner.as_ref()) |
197 | 0 | } |
198 | | } |
199 | | |
200 | | impl<'a, VE: ValueEncoding> PartialEq<&'a MetadataKey<VE>> for MetadataKey<VE> { |
201 | | #[inline] |
202 | 0 | fn eq(&self, other: &&'a MetadataKey<VE>) -> bool { |
203 | 0 | *self == **other |
204 | 0 | } |
205 | | } |
206 | | |
207 | | impl<VE: ValueEncoding> PartialEq<MetadataKey<VE>> for &MetadataKey<VE> { |
208 | | #[inline] |
209 | 0 | fn eq(&self, other: &MetadataKey<VE>) -> bool { |
210 | 0 | *other == *self |
211 | 0 | } |
212 | | } |
213 | | |
214 | | impl<VE: ValueEncoding> PartialEq<str> for MetadataKey<VE> { |
215 | | /// Performs a case-insensitive comparison of the string against the header |
216 | | /// name |
217 | | /// |
218 | | /// # Examples |
219 | | /// |
220 | | /// ``` |
221 | | /// # use tonic::metadata::*; |
222 | | /// let content_length = AsciiMetadataKey::from_static("content-length"); |
223 | | /// |
224 | | /// assert_eq!(content_length, "content-length"); |
225 | | /// assert_eq!(content_length, "Content-Length"); |
226 | | /// assert_ne!(content_length, "content length"); |
227 | | /// ``` |
228 | | #[inline] |
229 | 0 | fn eq(&self, other: &str) -> bool { |
230 | 0 | self.inner.eq(other) |
231 | 0 | } |
232 | | } |
233 | | |
234 | | impl<VE: ValueEncoding> PartialEq<MetadataKey<VE>> for str { |
235 | | /// Performs a case-insensitive comparison of the string against the header |
236 | | /// name |
237 | | /// |
238 | | /// # Examples |
239 | | /// |
240 | | /// ``` |
241 | | /// # use tonic::metadata::*; |
242 | | /// let content_length = AsciiMetadataKey::from_static("content-length"); |
243 | | /// |
244 | | /// assert_eq!(content_length, "content-length"); |
245 | | /// assert_eq!(content_length, "Content-Length"); |
246 | | /// assert_ne!(content_length, "content length"); |
247 | | /// ``` |
248 | | #[inline] |
249 | 0 | fn eq(&self, other: &MetadataKey<VE>) -> bool { |
250 | 0 | other.inner == *self |
251 | 0 | } |
252 | | } |
253 | | |
254 | | impl<'a, VE: ValueEncoding> PartialEq<&'a str> for MetadataKey<VE> { |
255 | | /// Performs a case-insensitive comparison of the string against the header |
256 | | /// name |
257 | | #[inline] |
258 | 0 | fn eq(&self, other: &&'a str) -> bool { |
259 | 0 | *self == **other |
260 | 0 | } |
261 | | } |
262 | | |
263 | | impl<VE: ValueEncoding> PartialEq<MetadataKey<VE>> for &str { |
264 | | /// Performs a case-insensitive comparison of the string against the header |
265 | | /// name |
266 | | #[inline] |
267 | 0 | fn eq(&self, other: &MetadataKey<VE>) -> bool { |
268 | 0 | *other == *self |
269 | 0 | } |
270 | | } |
271 | | |
272 | | impl fmt::Display for InvalidMetadataKey { |
273 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
274 | 0 | f.write_str("invalid gRPC metadata key name") |
275 | 0 | } |
276 | | } |
277 | | |
278 | | impl Error for InvalidMetadataKey {} |
279 | | |
280 | | #[cfg(test)] |
281 | | mod tests { |
282 | | use super::{AsciiMetadataKey, BinaryMetadataKey}; |
283 | | |
284 | | #[test] |
285 | | fn test_from_bytes_binary() { |
286 | | assert!(BinaryMetadataKey::from_bytes(b"").is_err()); |
287 | | assert!(BinaryMetadataKey::from_bytes(b"\xFF").is_err()); |
288 | | assert!(BinaryMetadataKey::from_bytes(b"abc").is_err()); |
289 | | assert_eq!( |
290 | | BinaryMetadataKey::from_bytes(b"abc-bin").unwrap().as_str(), |
291 | | "abc-bin" |
292 | | ); |
293 | | } |
294 | | |
295 | | #[test] |
296 | | fn test_from_bytes_ascii() { |
297 | | assert!(AsciiMetadataKey::from_bytes(b"").is_err()); |
298 | | assert!(AsciiMetadataKey::from_bytes(b"\xFF").is_err()); |
299 | | assert_eq!( |
300 | | AsciiMetadataKey::from_bytes(b"abc").unwrap().as_str(), |
301 | | "abc" |
302 | | ); |
303 | | assert!(AsciiMetadataKey::from_bytes(b"abc-bin").is_err()); |
304 | | } |
305 | | } |