Skip to main content

tonic/metadata/
encoding.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 base64::Engine as _;
26use bytes::Bytes;
27use http::header::HeaderValue;
28use std::error::Error;
29use std::fmt;
30use std::hash::Hash;
31
32/// A possible error when converting a `MetadataValue` from a string or byte
33/// slice.
34#[derive(Debug, Hash)]
35pub struct InvalidMetadataValue {
36    _priv: (),
37}
38
39mod value_encoding {
40    use super::InvalidMetadataValueBytes;
41    use bytes::Bytes;
42    use http::header::HeaderValue;
43    use std::fmt;
44
45    pub trait Sealed {
46        #[doc(hidden)]
47        fn is_empty(value: &[u8]) -> bool;
48
49        #[doc(hidden)]
50        fn from_bytes(value: &[u8]) -> Result<HeaderValue, InvalidMetadataValueBytes>;
51
52        #[doc(hidden)]
53        fn from_shared(value: Bytes) -> Result<HeaderValue, InvalidMetadataValueBytes>;
54
55        #[doc(hidden)]
56        fn from_static(value: &'static str) -> HeaderValue;
57
58        #[doc(hidden)]
59        fn decode(value: &[u8]) -> Result<Bytes, InvalidMetadataValueBytes>;
60
61        #[doc(hidden)]
62        fn equals(a: &HeaderValue, b: &[u8]) -> bool;
63
64        #[doc(hidden)]
65        fn values_equal(a: &HeaderValue, b: &HeaderValue) -> bool;
66
67        #[doc(hidden)]
68        fn fmt(value: &HeaderValue, f: &mut fmt::Formatter<'_>) -> fmt::Result;
69    }
70}
71
72pub trait ValueEncoding: Clone + Eq + PartialEq + Hash + self::value_encoding::Sealed {
73    /// Returns true if the provided key is valid for this ValueEncoding type.
74    /// For example, `Ascii::is_valid_key("a") == true`,
75    /// `Ascii::is_valid_key("a-bin") == false`.
76    fn is_valid_key(key: &str) -> bool;
77}
78
79/// gRPC metadata values can be either ASCII strings or binary. Note that only
80/// visible ASCII characters (32-127) are permitted.
81/// This type should never be instantiated -- in fact, it's impossible
82/// to, because there's no variants to instantiate. Instead, it's just used as
83/// a type parameter for [`MetadataKey`] and [`MetadataValue`].
84///
85/// [`MetadataKey`]: struct.MetadataKey.html
86/// [`MetadataValue`]: struct.MetadataValue.html
87#[derive(Clone, Debug, Eq, PartialEq, Hash)]
88#[non_exhaustive]
89pub enum Ascii {}
90
91/// gRPC metadata values can be either ASCII strings or binary.
92/// This type should never be instantiated -- in fact, it's impossible
93/// to, because there's no variants to instantiate. Instead, it's just used as
94/// a type parameter for [`MetadataKey`] and [`MetadataValue`].
95///
96/// [`MetadataKey`]: struct.MetadataKey.html
97/// [`MetadataValue`]: struct.MetadataValue.html
98#[derive(Clone, Debug, Eq, PartialEq, Hash)]
99#[non_exhaustive]
100pub enum Binary {}
101
102// ===== impl ValueEncoding =====
103
104impl self::value_encoding::Sealed for Ascii {
105    fn is_empty(value: &[u8]) -> bool {
106        value.is_empty()
107    }
108
109    fn from_bytes(value: &[u8]) -> Result<HeaderValue, InvalidMetadataValueBytes> {
110        HeaderValue::from_bytes(value).map_err(|_| InvalidMetadataValueBytes::new())
111    }
112
113    fn from_shared(value: Bytes) -> Result<HeaderValue, InvalidMetadataValueBytes> {
114        HeaderValue::from_maybe_shared(value).map_err(|_| InvalidMetadataValueBytes::new())
115    }
116
117    fn from_static(value: &'static str) -> HeaderValue {
118        HeaderValue::from_static(value)
119    }
120
121    fn decode(value: &[u8]) -> Result<Bytes, InvalidMetadataValueBytes> {
122        Ok(Bytes::copy_from_slice(value))
123    }
124
125    fn equals(a: &HeaderValue, b: &[u8]) -> bool {
126        a.as_bytes() == b
127    }
128
129    fn values_equal(a: &HeaderValue, b: &HeaderValue) -> bool {
130        a == b
131    }
132
133    fn fmt(value: &HeaderValue, f: &mut fmt::Formatter<'_>) -> fmt::Result {
134        fmt::Debug::fmt(value, f)
135    }
136}
137
138impl ValueEncoding for Ascii {
139    fn is_valid_key(key: &str) -> bool {
140        !Binary::is_valid_key(key)
141    }
142}
143
144impl self::value_encoding::Sealed for Binary {
145    fn is_empty(value: &[u8]) -> bool {
146        for c in value {
147            if *c != b'=' {
148                return false;
149            }
150        }
151        true
152    }
153
154    fn from_bytes(value: &[u8]) -> Result<HeaderValue, InvalidMetadataValueBytes> {
155        let encoded_value: String = crate::util::base64::STANDARD_NO_PAD.encode(value);
156        HeaderValue::from_maybe_shared(Bytes::from(encoded_value))
157            .map_err(|_| InvalidMetadataValueBytes::new())
158    }
159
160    fn from_shared(value: Bytes) -> Result<HeaderValue, InvalidMetadataValueBytes> {
161        Self::from_bytes(value.as_ref())
162    }
163
164    fn from_static(value: &'static str) -> HeaderValue {
165        if crate::util::base64::STANDARD.decode(value).is_err() {
166            panic!("Invalid base64 passed to from_static: {value}");
167        }
168        unsafe {
169            // Because this is valid base64 this must be a valid HTTP header value,
170            // no need to check again by calling from_shared.
171            HeaderValue::from_maybe_shared_unchecked(Bytes::from_static(value.as_ref()))
172        }
173    }
174
175    fn decode(value: &[u8]) -> Result<Bytes, InvalidMetadataValueBytes> {
176        crate::util::base64::STANDARD
177            .decode(value)
178            .map(|bytes_vec| bytes_vec.into())
179            .map_err(|_| InvalidMetadataValueBytes::new())
180    }
181
182    fn equals(a: &HeaderValue, b: &[u8]) -> bool {
183        if let Ok(decoded) = crate::util::base64::STANDARD.decode(a.as_bytes()) {
184            decoded == b
185        } else {
186            a.as_bytes() == b
187        }
188    }
189
190    fn values_equal(a: &HeaderValue, b: &HeaderValue) -> bool {
191        match (Self::decode(a.as_bytes()), Self::decode(b.as_bytes())) {
192            (Ok(a), Ok(b)) => a == b,
193            (Err(_), Err(_)) => true,
194            _ => false,
195        }
196    }
197
198    fn fmt(value: &HeaderValue, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199        if let Ok(decoded) = Self::decode(value.as_bytes()) {
200            write!(f, "{decoded:?}")
201        } else {
202            write!(f, "b[invalid]{value:?}")
203        }
204    }
205}
206
207impl ValueEncoding for Binary {
208    fn is_valid_key(key: &str) -> bool {
209        key.ends_with("-bin")
210    }
211}
212
213// ===== impl InvalidMetadataValue =====
214
215impl InvalidMetadataValue {
216    pub(crate) fn new() -> Self {
217        InvalidMetadataValue { _priv: () }
218    }
219}
220
221impl fmt::Display for InvalidMetadataValue {
222    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
223        f.write_str("failed to parse metadata value")
224    }
225}
226
227impl Error for InvalidMetadataValue {}
228
229/// A possible error when converting a `MetadataValue` from a string or byte
230/// slice.
231#[derive(Debug, Hash)]
232pub struct InvalidMetadataValueBytes(InvalidMetadataValue);
233
234// ===== impl InvalidMetadataValueBytes =====
235
236impl InvalidMetadataValueBytes {
237    pub(crate) fn new() -> Self {
238        InvalidMetadataValueBytes(InvalidMetadataValue::new())
239    }
240}
241
242impl fmt::Display for InvalidMetadataValueBytes {
243    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
244        self.0.fmt(f)
245    }
246}
247
248impl Error for InvalidMetadataValueBytes {}