Coverage Report

Created: 2026-07-13 08:11

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}