tonic/metadata/
key.rs

1use bytes::Bytes;
2use http::header::HeaderName;
3use std::borrow::Borrow;
4use std::error::Error;
5use std::fmt;
6use std::marker::PhantomData;
7use std::str::FromStr;
8
9use 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)]
19pub 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)]
28pub struct InvalidMetadataKey {
29    _priv: (),
30}
31
32/// An ascii metadata key.
33pub type AsciiMetadataKey = MetadataKey<Ascii>;
34/// A binary metadata key.
35pub type BinaryMetadataKey = MetadataKey<Binary>;
36
37impl<VE: ValueEncoding> MetadataKey<VE> {
38    /// Converts a slice of bytes to a `MetadataKey`.
39    ///
40    /// This function normalizes the input.
41    pub fn from_bytes(src: &[u8]) -> Result<Self, InvalidMetadataKey> {
42        match HeaderName::from_bytes(src) {
43            Ok(name) => {
44                if !VE::is_valid_key(name.as_str()) {
45                    return Err(InvalidMetadataKey::new());
46                }
47
48                Ok(MetadataKey {
49                    inner: name,
50                    phantom: PhantomData,
51                })
52            }
53            Err(_) => Err(InvalidMetadataKey::new()),
54        }
55    }
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    pub fn from_static(src: &'static str) -> Self {
103        let name = HeaderName::from_static(src);
104        if !VE::is_valid_key(name.as_str()) {
105            panic!("invalid metadata key")
106        }
107
108        MetadataKey {
109            inner: name,
110            phantom: PhantomData,
111        }
112    }
113
114    /// Returns a `str` representation of the metadata key.
115    ///
116    /// The returned string will always be lower case.
117    #[inline]
118    pub fn as_str(&self) -> &str {
119        self.inner.as_str()
120    }
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    pub(crate) fn unchecked_from_header_name_ref(header_name: &HeaderName) -> &Self {
127        unsafe { &*(header_name as *const HeaderName as *const Self) }
128    }
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    pub(crate) fn unchecked_from_header_name(name: HeaderName) -> Self {
135        MetadataKey {
136            inner: name,
137            phantom: PhantomData,
138        }
139    }
140}
141
142impl<VE: ValueEncoding> FromStr for MetadataKey<VE> {
143    type Err = InvalidMetadataKey;
144
145    fn from_str(s: &str) -> Result<Self, InvalidMetadataKey> {
146        MetadataKey::from_bytes(s.as_bytes()).map_err(|_| InvalidMetadataKey::new())
147    }
148}
149
150impl<VE: ValueEncoding> AsRef<str> for MetadataKey<VE> {
151    fn as_ref(&self) -> &str {
152        self.as_str()
153    }
154}
155
156impl<VE: ValueEncoding> AsRef<[u8]> for MetadataKey<VE> {
157    fn as_ref(&self) -> &[u8] {
158        self.as_str().as_bytes()
159    }
160}
161
162impl<VE: ValueEncoding> Borrow<str> for MetadataKey<VE> {
163    fn borrow(&self) -> &str {
164        self.as_str()
165    }
166}
167
168impl<VE: ValueEncoding> fmt::Debug for MetadataKey<VE> {
169    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
170        fmt::Debug::fmt(self.as_str(), fmt)
171    }
172}
173
174impl<VE: ValueEncoding> fmt::Display for MetadataKey<VE> {
175    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
176        fmt::Display::fmt(self.as_str(), fmt)
177    }
178}
179
180impl InvalidMetadataKey {
181    #[doc(hidden)]
182    pub fn new() -> InvalidMetadataKey {
183        Self::default()
184    }
185}
186
187impl<'a, VE: ValueEncoding> From<&'a MetadataKey<VE>> for MetadataKey<VE> {
188    fn from(src: &'a MetadataKey<VE>) -> MetadataKey<VE> {
189        src.clone()
190    }
191}
192
193impl<VE: ValueEncoding> From<MetadataKey<VE>> for Bytes {
194    #[inline]
195    fn from(name: MetadataKey<VE>) -> Bytes {
196        Bytes::copy_from_slice(name.inner.as_ref())
197    }
198}
199
200impl<'a, VE: ValueEncoding> PartialEq<&'a MetadataKey<VE>> for MetadataKey<VE> {
201    #[inline]
202    fn eq(&self, other: &&'a MetadataKey<VE>) -> bool {
203        *self == **other
204    }
205}
206
207impl<'a, VE: ValueEncoding> PartialEq<MetadataKey<VE>> for &'a MetadataKey<VE> {
208    #[inline]
209    fn eq(&self, other: &MetadataKey<VE>) -> bool {
210        *other == *self
211    }
212}
213
214impl<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    fn eq(&self, other: &str) -> bool {
230        self.inner.eq(other)
231    }
232}
233
234impl<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    fn eq(&self, other: &MetadataKey<VE>) -> bool {
250        other.inner == *self
251    }
252}
253
254impl<'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    fn eq(&self, other: &&'a str) -> bool {
259        *self == **other
260    }
261}
262
263impl<'a, VE: ValueEncoding> PartialEq<MetadataKey<VE>> for &'a str {
264    /// Performs a case-insensitive comparison of the string against the header
265    /// name
266    #[inline]
267    fn eq(&self, other: &MetadataKey<VE>) -> bool {
268        *other == *self
269    }
270}
271
272impl fmt::Display for InvalidMetadataKey {
273    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
274        f.write_str("invalid gRPC metadata key name")
275    }
276}
277
278impl Error for InvalidMetadataKey {}
279
280#[cfg(test)]
281mod 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}