use base64::Engine as _; use bytes::Bytes; use http::header::HeaderValue; use std::error::Error; use std::fmt; use std::hash::Hash; /// A possible error when converting a `MetadataValue` from a string or byte /// slice. #[derive(Debug, Hash)] pub struct InvalidMetadataValue { _priv: (), } mod value_encoding { use super::InvalidMetadataValueBytes; use bytes::Bytes; use http::header::HeaderValue; use std::fmt; pub trait Sealed { #[doc(hidden)] fn is_empty(value: &[u8]) -> bool; #[doc(hidden)] fn from_bytes(value: &[u8]) -> Result; #[doc(hidden)] fn from_shared(value: Bytes) -> Result; #[doc(hidden)] fn from_static(value: &'static str) -> HeaderValue; #[doc(hidden)] fn decode(value: &[u8]) -> Result; #[doc(hidden)] fn equals(a: &HeaderValue, b: &[u8]) -> bool; #[doc(hidden)] fn values_equal(a: &HeaderValue, b: &HeaderValue) -> bool; #[doc(hidden)] fn fmt(value: &HeaderValue, f: &mut fmt::Formatter<'_>) -> fmt::Result; } } pub trait ValueEncoding: Clone + Eq + PartialEq + Hash + self::value_encoding::Sealed { /// Returns true if the provided key is valid for this ValueEncoding type. /// For example, `Ascii::is_valid_key("a") == true`, /// `Ascii::is_valid_key("a-bin") == false`. fn is_valid_key(key: &str) -> bool; } /// gRPC metadata values can be either ASCII strings or binary. Note that only /// visible ASCII characters (32-127) are permitted. /// This type should never be instantiated -- in fact, it's impossible /// to, because there's no variants to instantiate. Instead, it's just used as /// a type parameter for [`MetadataKey`] and [`MetadataValue`]. /// /// [`MetadataKey`]: struct.MetadataKey.html /// [`MetadataValue`]: struct.MetadataValue.html #[derive(Clone, Debug, Eq, PartialEq, Hash)] #[non_exhaustive] pub enum Ascii {} /// gRPC metadata values can be either ASCII strings or binary. /// This type should never be instantiated -- in fact, it's impossible /// to, because there's no variants to instantiate. Instead, it's just used as /// a type parameter for [`MetadataKey`] and [`MetadataValue`]. /// /// [`MetadataKey`]: struct.MetadataKey.html /// [`MetadataValue`]: struct.MetadataValue.html #[derive(Clone, Debug, Eq, PartialEq, Hash)] #[non_exhaustive] pub enum Binary {} // ===== impl ValueEncoding ===== impl self::value_encoding::Sealed for Ascii { fn is_empty(value: &[u8]) -> bool { value.is_empty() } fn from_bytes(value: &[u8]) -> Result { HeaderValue::from_bytes(value).map_err(|_| InvalidMetadataValueBytes::new()) } fn from_shared(value: Bytes) -> Result { HeaderValue::from_maybe_shared(value).map_err(|_| InvalidMetadataValueBytes::new()) } fn from_static(value: &'static str) -> HeaderValue { HeaderValue::from_static(value) } fn decode(value: &[u8]) -> Result { Ok(Bytes::copy_from_slice(value)) } fn equals(a: &HeaderValue, b: &[u8]) -> bool { a.as_bytes() == b } fn values_equal(a: &HeaderValue, b: &HeaderValue) -> bool { a == b } fn fmt(value: &HeaderValue, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(value, f) } } impl ValueEncoding for Ascii { fn is_valid_key(key: &str) -> bool { !Binary::is_valid_key(key) } } impl self::value_encoding::Sealed for Binary { fn is_empty(value: &[u8]) -> bool { for c in value { if *c != b'=' { return false; } } true } fn from_bytes(value: &[u8]) -> Result { let encoded_value: String = crate::util::base64::STANDARD_NO_PAD.encode(value); HeaderValue::from_maybe_shared(Bytes::from(encoded_value)) .map_err(|_| InvalidMetadataValueBytes::new()) } fn from_shared(value: Bytes) -> Result { Self::from_bytes(value.as_ref()) } fn from_static(value: &'static str) -> HeaderValue { if crate::util::base64::STANDARD.decode(value).is_err() { panic!("Invalid base64 passed to from_static: {}", value); } unsafe { // Because this is valid base64 this must be a valid HTTP header value, // no need to check again by calling from_shared. HeaderValue::from_maybe_shared_unchecked(Bytes::from_static(value.as_ref())) } } fn decode(value: &[u8]) -> Result { crate::util::base64::STANDARD .decode(value) .map(|bytes_vec| bytes_vec.into()) .map_err(|_| InvalidMetadataValueBytes::new()) } fn equals(a: &HeaderValue, b: &[u8]) -> bool { if let Ok(decoded) = crate::util::base64::STANDARD.decode(a.as_bytes()) { decoded == b } else { a.as_bytes() == b } } fn values_equal(a: &HeaderValue, b: &HeaderValue) -> bool { match (Self::decode(a.as_bytes()), Self::decode(b.as_bytes())) { (Ok(a), Ok(b)) => a == b, (Err(_), Err(_)) => true, _ => false, } } fn fmt(value: &HeaderValue, f: &mut fmt::Formatter<'_>) -> fmt::Result { if let Ok(decoded) = Self::decode(value.as_bytes()) { write!(f, "{:?}", decoded) } else { write!(f, "b[invalid]{:?}", value) } } } impl ValueEncoding for Binary { fn is_valid_key(key: &str) -> bool { key.ends_with("-bin") } } // ===== impl InvalidMetadataValue ===== impl InvalidMetadataValue { pub(crate) fn new() -> Self { InvalidMetadataValue { _priv: () } } } impl fmt::Display for InvalidMetadataValue { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.write_str("failed to parse metadata value") } } impl Error for InvalidMetadataValue {} /// A possible error when converting a `MetadataValue` from a string or byte /// slice. #[derive(Debug, Hash)] pub struct InvalidMetadataValueBytes(InvalidMetadataValue); // ===== impl InvalidMetadataValueBytes ===== impl InvalidMetadataValueBytes { pub(crate) fn new() -> Self { InvalidMetadataValueBytes(InvalidMetadataValue::new()) } } impl fmt::Display for InvalidMetadataValueBytes { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } impl Error for InvalidMetadataValueBytes {}