| 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<HeaderValue, InvalidMetadataValueBytes>; |
| |
| #[doc(hidden)] |
| fn from_shared(value: Bytes) -> Result<HeaderValue, InvalidMetadataValueBytes>; |
| |
| #[doc(hidden)] |
| fn from_static(value: &'static str) -> HeaderValue; |
| |
| #[doc(hidden)] |
| fn decode(value: &[u8]) -> Result<Bytes, InvalidMetadataValueBytes>; |
| |
| #[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, InvalidMetadataValueBytes> { |
| HeaderValue::from_bytes(value).map_err(|_| InvalidMetadataValueBytes::new()) |
| } |
| |
| fn from_shared(value: Bytes) -> Result<HeaderValue, InvalidMetadataValueBytes> { |
| 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<Bytes, InvalidMetadataValueBytes> { |
| 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<HeaderValue, InvalidMetadataValueBytes> { |
| 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<HeaderValue, InvalidMetadataValueBytes> { |
| 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<Bytes, InvalidMetadataValueBytes> { |
| 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 {} |