Skip to main content

tonic/metadata/
key.rs

1/*
2 *
3 * Copyright 2025 gRPC authors.
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to
7 * deal in the Software without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21 * IN THE SOFTWARE.
22 *
23 */
24
25use bytes::Bytes;
26use http::header::HeaderName;
27use std::borrow::Borrow;
28use std::error::Error;
29use std::fmt;
30use std::marker::PhantomData;
31use std::str::FromStr;
32
33use super::encoding::{Ascii, Binary, ValueEncoding};
34
35/// Represents a custom metadata field name.
36///
37/// `MetadataKey` is used as the [`MetadataMap`] key.
38///
39/// [`HeaderMap`]: struct.HeaderMap.html
40/// [`MetadataMap`]: struct.MetadataMap.html
41#[derive(Clone, Eq, PartialEq, Hash)]
42#[repr(transparent)]
43pub struct MetadataKey<VE: ValueEncoding> {
44    // Note: There are unsafe transmutes that assume that the memory layout
45    // of MetadataValue is identical to HeaderName
46    pub(crate) inner: http::header::HeaderName,
47    phantom: PhantomData<VE>,
48}
49
50/// A possible error when converting a `MetadataKey` from another type.
51#[derive(Debug, Default)]
52pub struct InvalidMetadataKey {
53    _priv: (),
54}
55
56/// An ascii metadata key.
57pub type AsciiMetadataKey = MetadataKey<Ascii>;
58/// A binary metadata key.
59pub type BinaryMetadataKey = MetadataKey<Binary>;
60
61impl<VE: ValueEncoding> MetadataKey<VE> {
62    /// Converts a slice of bytes to a `MetadataKey`.
63    ///
64    /// This function normalizes the input.
65    pub fn from_bytes(src: &[u8]) -> Result<Self, InvalidMetadataKey> {
66        match HeaderName::from_bytes(src) {
67            Ok(name) => {
68                if !VE::is_valid_key(name.as_str()) {
69                    return Err(InvalidMetadataKey::new());
70                }
71
72                Ok(MetadataKey {
73                    inner: name,
74                    phantom: PhantomData,
75                })
76            }
77            Err(_) => Err(InvalidMetadataKey::new()),
78        }
79    }
80
81    /// Converts a static string to a `MetadataKey`.
82    ///
83    /// This function panics when the static string is a invalid metadata key.
84    ///
85    /// This function requires the static string to only contain lowercase
86    /// characters, numerals and symbols, as per the HTTP/2.0 specification
87    /// and header names internal representation within this library.
88    ///
89    ///
90    /// # Examples
91    ///
92    /// ```
93    /// # use tonic::metadata::*;
94    /// // Parsing a metadata key
95    /// let CUSTOM_KEY: &'static str = "custom-key";
96    ///
97    /// let a = AsciiMetadataKey::from_bytes(b"custom-key").unwrap();
98    /// let b = AsciiMetadataKey::from_static(CUSTOM_KEY);
99    /// assert_eq!(a, b);
100    /// ```
101    ///
102    /// ```should_panic
103    /// # use tonic::metadata::*;
104    /// // Parsing a metadata key that contains invalid symbols(s):
105    /// AsciiMetadataKey::from_static("content{}{}length"); // This line panics!
106    /// ```
107    ///
108    /// ```should_panic
109    /// # use tonic::metadata::*;
110    /// // Parsing a metadata key that contains invalid uppercase characters.
111    /// let a = AsciiMetadataKey::from_static("foobar");
112    /// let b = AsciiMetadataKey::from_static("FOOBAR"); // This line panics!
113    /// ```
114    ///
115    /// ```should_panic
116    /// # use tonic::metadata::*;
117    /// // Parsing a -bin metadata key as an Ascii key.
118    /// let b = AsciiMetadataKey::from_static("hello-bin"); // This line panics!
119    /// ```
120    ///
121    /// ```should_panic
122    /// # use tonic::metadata::*;
123    /// // Parsing a non-bin metadata key as an Binary key.
124    /// let b = BinaryMetadataKey::from_static("hello"); // This line panics!
125    /// ```
126    pub fn from_static(src: &'static str) -> Self {
127        let name = HeaderName::from_static(src);
128        if !VE::is_valid_key(name.as_str()) {
129            panic!("invalid metadata key")
130        }
131
132        MetadataKey {
133            inner: name,
134            phantom: PhantomData,
135        }
136    }
137
138    /// Returns a `str` representation of the metadata key.
139    ///
140    /// The returned string will always be lower case.
141    #[inline]
142    pub fn as_str(&self) -> &str {
143        self.inner.as_str()
144    }
145
146    /// Converts a HeaderName reference to a MetadataKey. This method assumes
147    /// that the caller has made sure that the header name has the correct
148    /// "-bin" or non-"-bin" suffix, it does not validate its input.
149    #[inline]
150    pub(crate) fn unchecked_from_header_name_ref(header_name: &HeaderName) -> &Self {
151        unsafe { &*(header_name as *const HeaderName as *const Self) }
152    }
153
154    /// Converts a HeaderName reference to a MetadataKey. This method assumes
155    /// that the caller has made sure that the header name has the correct
156    /// "-bin" or non-"-bin" suffix, it does not validate its input.
157    #[inline]
158    pub(crate) fn unchecked_from_header_name(name: HeaderName) -> Self {
159        MetadataKey {
160            inner: name,
161            phantom: PhantomData,
162        }
163    }
164}
165
166impl<VE: ValueEncoding> FromStr for MetadataKey<VE> {
167    type Err = InvalidMetadataKey;
168
169    fn from_str(s: &str) -> Result<Self, InvalidMetadataKey> {
170        MetadataKey::from_bytes(s.as_bytes()).map_err(|_| InvalidMetadataKey::new())
171    }
172}
173
174impl<VE: ValueEncoding> AsRef<str> for MetadataKey<VE> {
175    fn as_ref(&self) -> &str {
176        self.as_str()
177    }
178}
179
180impl<VE: ValueEncoding> AsRef<[u8]> for MetadataKey<VE> {
181    fn as_ref(&self) -> &[u8] {
182        self.as_str().as_bytes()
183    }
184}
185
186impl<VE: ValueEncoding> Borrow<str> for MetadataKey<VE> {
187    fn borrow(&self) -> &str {
188        self.as_str()
189    }
190}
191
192impl<VE: ValueEncoding> fmt::Debug for MetadataKey<VE> {
193    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
194        fmt::Debug::fmt(self.as_str(), fmt)
195    }
196}
197
198impl<VE: ValueEncoding> fmt::Display for MetadataKey<VE> {
199    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
200        fmt::Display::fmt(self.as_str(), fmt)
201    }
202}
203
204impl InvalidMetadataKey {
205    #[doc(hidden)]
206    pub fn new() -> InvalidMetadataKey {
207        Self::default()
208    }
209}
210
211impl<'a, VE: ValueEncoding> From<&'a MetadataKey<VE>> for MetadataKey<VE> {
212    fn from(src: &'a MetadataKey<VE>) -> MetadataKey<VE> {
213        src.clone()
214    }
215}
216
217impl<VE: ValueEncoding> From<MetadataKey<VE>> for Bytes {
218    #[inline]
219    fn from(name: MetadataKey<VE>) -> Bytes {
220        Bytes::copy_from_slice(name.inner.as_ref())
221    }
222}
223
224impl<'a, VE: ValueEncoding> PartialEq<&'a MetadataKey<VE>> for MetadataKey<VE> {
225    #[inline]
226    fn eq(&self, other: &&'a MetadataKey<VE>) -> bool {
227        *self == **other
228    }
229}
230
231impl<VE: ValueEncoding> PartialEq<MetadataKey<VE>> for &MetadataKey<VE> {
232    #[inline]
233    fn eq(&self, other: &MetadataKey<VE>) -> bool {
234        *other == *self
235    }
236}
237
238impl<VE: ValueEncoding> PartialEq<str> for MetadataKey<VE> {
239    /// Performs a case-insensitive comparison of the string against the header
240    /// name
241    ///
242    /// # Examples
243    ///
244    /// ```
245    /// # use tonic::metadata::*;
246    /// let content_length = AsciiMetadataKey::from_static("content-length");
247    ///
248    /// assert_eq!(content_length, "content-length");
249    /// assert_eq!(content_length, "Content-Length");
250    /// assert_ne!(content_length, "content length");
251    /// ```
252    #[inline]
253    fn eq(&self, other: &str) -> bool {
254        self.inner.eq(other)
255    }
256}
257
258impl<VE: ValueEncoding> PartialEq<MetadataKey<VE>> for str {
259    /// Performs a case-insensitive comparison of the string against the header
260    /// name
261    ///
262    /// # Examples
263    ///
264    /// ```
265    /// # use tonic::metadata::*;
266    /// let content_length = AsciiMetadataKey::from_static("content-length");
267    ///
268    /// assert_eq!(content_length, "content-length");
269    /// assert_eq!(content_length, "Content-Length");
270    /// assert_ne!(content_length, "content length");
271    /// ```
272    #[inline]
273    fn eq(&self, other: &MetadataKey<VE>) -> bool {
274        other.inner == *self
275    }
276}
277
278impl<'a, VE: ValueEncoding> PartialEq<&'a str> for MetadataKey<VE> {
279    /// Performs a case-insensitive comparison of the string against the header
280    /// name
281    #[inline]
282    fn eq(&self, other: &&'a str) -> bool {
283        *self == **other
284    }
285}
286
287impl<VE: ValueEncoding> PartialEq<MetadataKey<VE>> for &str {
288    /// Performs a case-insensitive comparison of the string against the header
289    /// name
290    #[inline]
291    fn eq(&self, other: &MetadataKey<VE>) -> bool {
292        *other == *self
293    }
294}
295
296impl fmt::Display for InvalidMetadataKey {
297    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298        f.write_str("invalid gRPC metadata key name")
299    }
300}
301
302impl Error for InvalidMetadataKey {}
303
304#[cfg(test)]
305mod tests {
306    use super::{AsciiMetadataKey, BinaryMetadataKey};
307
308    #[test]
309    fn test_from_bytes_binary() {
310        assert!(BinaryMetadataKey::from_bytes(b"").is_err());
311        assert!(BinaryMetadataKey::from_bytes(b"\xFF").is_err());
312        assert!(BinaryMetadataKey::from_bytes(b"abc").is_err());
313        assert_eq!(
314            BinaryMetadataKey::from_bytes(b"abc-bin").unwrap().as_str(),
315            "abc-bin"
316        );
317    }
318
319    #[test]
320    fn test_from_bytes_ascii() {
321        assert!(AsciiMetadataKey::from_bytes(b"").is_err());
322        assert!(AsciiMetadataKey::from_bytes(b"\xFF").is_err());
323        assert_eq!(
324            AsciiMetadataKey::from_bytes(b"abc").unwrap().as_str(),
325            "abc"
326        );
327        assert!(AsciiMetadataKey::from_bytes(b"abc-bin").is_err());
328    }
329}