blob: 9253a9254dc44cea54e53a046c0d14a7eb963387 [file] [log] [blame] [edit]
#![allow(clippy::upper_case_acronyms)]
use super::encoding::{
Ascii, Binary, InvalidMetadataValue, InvalidMetadataValueBytes, ValueEncoding,
};
use super::key::MetadataKey;
use bytes::Bytes;
use http::header::HeaderValue;
use std::error::Error;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::str::FromStr;
use std::{cmp, fmt};
/// Represents a custom metadata field value.
///
/// `MetadataValue` is used as the [`MetadataMap`] value.
///
/// [`HeaderMap`]: struct.HeaderMap.html
/// [`MetadataMap`]: struct.MetadataMap.html
#[derive(Clone)]
#[repr(transparent)]
pub struct MetadataValue<VE: ValueEncoding> {
// Note: There are unsafe transmutes that assume that the memory layout
// of MetadataValue is identical to HeaderValue
pub(crate) inner: HeaderValue,
phantom: PhantomData<VE>,
}
/// A possible error when converting a `MetadataValue` to a string representation.
///
/// Metadata field values may contain opaque bytes, in which case it is not
/// possible to represent the value as a string.
#[derive(Debug)]
pub struct ToStrError {
_priv: (),
}
/// An ascii metadata value.
pub type AsciiMetadataValue = MetadataValue<Ascii>;
/// A binary metadata value.
pub type BinaryMetadataValue = MetadataValue<Binary>;
impl<VE: ValueEncoding> MetadataValue<VE> {
/// Convert a static string to a `MetadataValue`.
///
/// This function will not perform any copying, however the string is
/// checked to ensure that no invalid characters are present.
///
/// For Ascii values, only visible ASCII characters (32-127) are permitted.
/// For Binary values, the string must be valid base64.
///
/// # Panics
///
/// This function panics if the argument contains invalid metadata value
/// characters.
///
/// # Examples
///
/// ```
/// # use tonic::metadata::*;
/// let val = AsciiMetadataValue::from_static("hello");
/// assert_eq!(val, "hello");
/// ```
///
/// ```
/// # use tonic::metadata::*;
/// let val = BinaryMetadataValue::from_static("SGVsbG8hIQ==");
/// assert_eq!(val, "Hello!!");
/// ```
#[inline]
pub fn from_static(src: &'static str) -> Self {
MetadataValue {
inner: VE::from_static(src),
phantom: PhantomData,
}
}
/// Convert a `Bytes` directly into a `MetadataValue` without validating.
/// For `MetadataValue<Binary>` the provided parameter must be base64
/// encoded without padding bytes at the end.
///
/// # Safety
///
/// This function does NOT validate that illegal bytes are not contained
/// within the buffer.
#[inline]
pub unsafe fn from_shared_unchecked(src: Bytes) -> Self {
MetadataValue {
inner: HeaderValue::from_maybe_shared_unchecked(src),
phantom: PhantomData,
}
}
/// Returns true if the `MetadataValue` has a length of zero bytes.
///
/// # Examples
///
/// ```
/// # use tonic::metadata::*;
/// let val = AsciiMetadataValue::from_static("");
/// assert!(val.is_empty());
///
/// let val = AsciiMetadataValue::from_static("hello");
/// assert!(!val.is_empty());
/// ```
#[inline]
pub fn is_empty(&self) -> bool {
VE::is_empty(self.inner.as_bytes())
}
/// Converts a `MetadataValue` to a Bytes buffer. This method cannot
/// fail for Ascii values. For Ascii values, `as_bytes` is more convenient
/// to use.
///
/// # Examples
///
/// ```
/// # use tonic::metadata::*;
/// let val = AsciiMetadataValue::from_static("hello");
/// assert_eq!(val.to_bytes().unwrap().as_ref(), b"hello");
/// ```
///
/// ```
/// # use tonic::metadata::*;
/// let val = BinaryMetadataValue::from_bytes(b"hello");
/// assert_eq!(val.to_bytes().unwrap().as_ref(), b"hello");
/// ```
#[inline]
pub fn to_bytes(&self) -> Result<Bytes, InvalidMetadataValueBytes> {
VE::decode(self.inner.as_bytes())
}
/// Mark that the metadata value represents sensitive information.
///
/// # Examples
///
/// ```
/// # use tonic::metadata::*;
/// let mut val = AsciiMetadataValue::from_static("my secret");
///
/// val.set_sensitive(true);
/// assert!(val.is_sensitive());
///
/// val.set_sensitive(false);
/// assert!(!val.is_sensitive());
/// ```
#[inline]
pub fn set_sensitive(&mut self, val: bool) {
self.inner.set_sensitive(val);
}
/// Returns `true` if the value represents sensitive data.
///
/// Sensitive data could represent passwords or other data that should not
/// be stored on disk or in memory. This setting can be used by components
/// like caches to avoid storing the value. HPACK encoders must set the
/// metadata field to never index when `is_sensitive` returns true.
///
/// Note that sensitivity is not factored into equality or ordering.
///
/// # Examples
///
/// ```
/// # use tonic::metadata::*;
/// let mut val = AsciiMetadataValue::from_static("my secret");
///
/// val.set_sensitive(true);
/// assert!(val.is_sensitive());
///
/// val.set_sensitive(false);
/// assert!(!val.is_sensitive());
/// ```
#[inline]
pub fn is_sensitive(&self) -> bool {
self.inner.is_sensitive()
}
/// Converts a `MetadataValue` to a byte slice. For Binary values, the
/// return value is base64 encoded.
///
/// # Examples
///
/// ```
/// # use tonic::metadata::*;
/// let val = AsciiMetadataValue::from_static("hello");
/// assert_eq!(val.as_encoded_bytes(), b"hello");
/// ```
///
/// ```
/// # use tonic::metadata::*;
/// let val = BinaryMetadataValue::from_bytes(b"Hello!");
/// assert_eq!(val.as_encoded_bytes(), b"SGVsbG8h");
/// ```
#[inline]
pub fn as_encoded_bytes(&self) -> &[u8] {
self.inner.as_bytes()
}
/// Converts a HeaderValue to a MetadataValue. This method assumes that the
/// caller has made sure that the value is of the correct Ascii or Binary
/// value encoding.
#[inline]
pub(crate) fn unchecked_from_header_value(value: HeaderValue) -> Self {
MetadataValue {
inner: value,
phantom: PhantomData,
}
}
/// Converts a HeaderValue reference to a MetadataValue. This method assumes
/// that the caller has made sure that the value is of the correct Ascii or
/// Binary value encoding.
#[inline]
pub(crate) fn unchecked_from_header_value_ref(header_value: &HeaderValue) -> &Self {
unsafe { &*(header_value as *const HeaderValue as *const Self) }
}
/// Converts a HeaderValue reference to a MetadataValue. This method assumes
/// that the caller has made sure that the value is of the correct Ascii or
/// Binary value encoding.
#[inline]
pub(crate) fn unchecked_from_mut_header_value_ref(header_value: &mut HeaderValue) -> &mut Self {
unsafe { &mut *(header_value as *mut HeaderValue as *mut Self) }
}
}
/// Attempt to convert a byte slice to a `MetadataValue`.
///
/// For Ascii metadata values, If the argument contains invalid metadata
/// value bytes, an error is returned. Only byte values between 32 and 255
/// (inclusive) are permitted, excluding byte 127 (DEL).
///
/// For Binary metadata values this method cannot fail. See also the Binary
/// only version of this method `from_bytes`.
///
/// # Examples
///
/// ```
/// # use tonic::metadata::*;
/// let val = AsciiMetadataValue::try_from(b"hello\xfa").unwrap();
/// assert_eq!(val, &b"hello\xfa"[..]);
/// ```
///
/// An invalid value
///
/// ```
/// # use tonic::metadata::*;
/// let val = AsciiMetadataValue::try_from(b"\n");
/// assert!(val.is_err());
/// ```
impl<'a, VE: ValueEncoding> TryFrom<&'a [u8]> for MetadataValue<VE> {
type Error = InvalidMetadataValueBytes;
#[inline]
fn try_from(src: &[u8]) -> Result<Self, Self::Error> {
VE::from_bytes(src).map(|value| MetadataValue {
inner: value,
phantom: PhantomData,
})
}
}
/// Attempt to convert a byte slice to a `MetadataValue`.
///
/// For Ascii metadata values, If the argument contains invalid metadata
/// value bytes, an error is returned. Only byte values between 32 and 255
/// (inclusive) are permitted, excluding byte 127 (DEL).
///
/// For Binary metadata values this method cannot fail. See also the Binary
/// only version of this method `from_bytes`.
///
/// # Examples
///
/// ```
/// # use tonic::metadata::*;
/// let val = AsciiMetadataValue::try_from(b"hello\xfa").unwrap();
/// assert_eq!(val, &b"hello\xfa"[..]);
/// ```
///
/// An invalid value
///
/// ```
/// # use tonic::metadata::*;
/// let val = AsciiMetadataValue::try_from(b"\n");
/// assert!(val.is_err());
/// ```
impl<'a, VE: ValueEncoding, const N: usize> TryFrom<&'a [u8; N]> for MetadataValue<VE> {
type Error = InvalidMetadataValueBytes;
#[inline]
fn try_from(src: &[u8; N]) -> Result<Self, Self::Error> {
Self::try_from(src.as_ref())
}
}
/// Attempt to convert a `Bytes` buffer to a `MetadataValue`.
///
/// For `MetadataValue<Ascii>`, if the argument contains invalid metadata
/// value bytes, an error is returned. Only byte values between 32 and 255
/// (inclusive) are permitted, excluding byte 127 (DEL).
///
/// For `MetadataValue<Binary>`, if the argument is not valid base64, an
/// error is returned. In use cases where the input is not base64 encoded,
/// use `from_bytes`; if the value has to be encoded it's not possible to
/// share the memory anyways.
impl<VE: ValueEncoding> TryFrom<Bytes> for MetadataValue<VE> {
type Error = InvalidMetadataValueBytes;
#[inline]
fn try_from(src: Bytes) -> Result<Self, Self::Error> {
VE::from_shared(src).map(|value| MetadataValue {
inner: value,
phantom: PhantomData,
})
}
}
/// Attempt to convert a Vec of bytes to a `MetadataValue`.
///
/// For `MetadataValue<Ascii>`, if the argument contains invalid metadata
/// value bytes, an error is returned. Only byte values between 32 and 255
/// (inclusive) are permitted, excluding byte 127 (DEL).
///
/// For `MetadataValue<Binary>`, if the argument is not valid base64, an
/// error is returned. In use cases where the input is not base64 encoded,
/// use `from_bytes`; if the value has to be encoded it's not possible to
/// share the memory anyways.
impl<VE: ValueEncoding> TryFrom<Vec<u8>> for MetadataValue<VE> {
type Error = InvalidMetadataValueBytes;
#[inline]
fn try_from(src: Vec<u8>) -> Result<Self, Self::Error> {
Self::try_from(src.as_slice())
}
}
/// Attempt to convert a string to a `MetadataValue<Ascii>`.
///
/// If the argument contains invalid metadata value characters, an error is
/// returned. Only visible ASCII characters (32-127) are permitted. Use
/// `from_bytes` to create a `MetadataValue` that includes opaque octets
/// (128-255).
impl<'a> TryFrom<&'a str> for MetadataValue<Ascii> {
type Error = InvalidMetadataValue;
#[inline]
fn try_from(s: &'a str) -> Result<Self, Self::Error> {
s.parse()
}
}
/// Attempt to convert a string to a `MetadataValue<Ascii>`.
///
/// If the argument contains invalid metadata value characters, an error is
/// returned. Only visible ASCII characters (32-127) are permitted. Use
/// `from_bytes` to create a `MetadataValue` that includes opaque octets
/// (128-255).
impl<'a> TryFrom<&'a String> for MetadataValue<Ascii> {
type Error = InvalidMetadataValue;
#[inline]
fn try_from(s: &'a String) -> Result<Self, Self::Error> {
s.parse()
}
}
/// Attempt to convert a string to a `MetadataValue<Ascii>`.
///
/// If the argument contains invalid metadata value characters, an error is
/// returned. Only visible ASCII characters (32-127) are permitted. Use
/// `from_bytes` to create a `MetadataValue` that includes opaque octets
/// (128-255).
impl TryFrom<String> for MetadataValue<Ascii> {
type Error = InvalidMetadataValue;
#[inline]
fn try_from(s: String) -> Result<Self, Self::Error> {
s.parse()
}
}
// is_empty is defined in the generic impl block above
#[allow(clippy::len_without_is_empty)]
impl MetadataValue<Ascii> {
/// Converts a MetadataKey into a `MetadataValue<Ascii>`.
///
/// Since every valid MetadataKey is a valid MetadataValue this is done
/// infallibly.
///
/// # Examples
///
/// ```
/// # use tonic::metadata::*;
/// let val = AsciiMetadataValue::from_key::<Ascii>("accept".parse().unwrap());
/// assert_eq!(val, AsciiMetadataValue::try_from(b"accept").unwrap());
/// ```
#[inline]
pub fn from_key<KeyVE: ValueEncoding>(key: MetadataKey<KeyVE>) -> Self {
key.into()
}
/// Returns the length of `self`, in bytes.
///
/// This method is not available for `MetadataValue<Binary>` because that
/// cannot be implemented in constant time, which most people would probably
/// expect. To get the length of `MetadataValue<Binary>`, convert it to a
/// Bytes value and measure its length.
///
/// # Examples
///
/// ```
/// # use tonic::metadata::*;
/// let val = AsciiMetadataValue::from_static("hello");
/// assert_eq!(val.len(), 5);
/// ```
#[inline]
pub fn len(&self) -> usize {
self.inner.len()
}
/// Yields a `&str` slice if the `MetadataValue` only contains visible ASCII
/// chars.
///
/// This function will perform a scan of the metadata value, checking all the
/// characters.
///
/// # Examples
///
/// ```
/// # use tonic::metadata::*;
/// let val = AsciiMetadataValue::from_static("hello");
/// assert_eq!(val.to_str().unwrap(), "hello");
/// ```
pub fn to_str(&self) -> Result<&str, ToStrError> {
self.inner.to_str().map_err(|_| ToStrError::new())
}
/// Converts a `MetadataValue` to a byte slice. For Binary values, use
/// `to_bytes`.
///
/// # Examples
///
/// ```
/// # use tonic::metadata::*;
/// let val = AsciiMetadataValue::from_static("hello");
/// assert_eq!(val.as_bytes(), b"hello");
/// ```
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.inner.as_bytes()
}
}
impl MetadataValue<Binary> {
/// Convert a byte slice to a `MetadataValue<Binary>`.
///
/// # Examples
///
/// ```
/// # use tonic::metadata::*;
/// let val = BinaryMetadataValue::from_bytes(b"hello\xfa");
/// assert_eq!(val, &b"hello\xfa"[..]);
/// ```
#[inline]
pub fn from_bytes(src: &[u8]) -> Self {
// Only the Ascii version of try_from can fail.
Self::try_from(src).unwrap()
}
}
impl<VE: ValueEncoding> AsRef<[u8]> for MetadataValue<VE> {
#[inline]
fn as_ref(&self) -> &[u8] {
self.inner.as_ref()
}
}
impl<VE: ValueEncoding> fmt::Debug for MetadataValue<VE> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
VE::fmt(&self.inner, f)
}
}
impl<KeyVE: ValueEncoding> From<MetadataKey<KeyVE>> for MetadataValue<Ascii> {
#[inline]
fn from(h: MetadataKey<KeyVE>) -> MetadataValue<Ascii> {
MetadataValue {
inner: h.inner.into(),
phantom: PhantomData,
}
}
}
macro_rules! from_integers {
($($name:ident: $t:ident => $max_len:expr),*) => {$(
impl From<$t> for MetadataValue<Ascii> {
fn from(num: $t) -> MetadataValue<Ascii> {
MetadataValue {
inner: HeaderValue::from(num),
phantom: PhantomData,
}
}
}
#[test]
fn $name() {
let n: $t = 55;
let val = AsciiMetadataValue::from(n);
assert_eq!(val, &n.to_string());
let n = ::std::$t::MAX;
let val = AsciiMetadataValue::from(n);
assert_eq!(val, &n.to_string());
}
)*};
}
from_integers! {
// integer type => maximum decimal length
// u8 purposely left off... AsciiMetadataValue::from(b'3') could be confusing
from_u16: u16 => 5,
from_i16: i16 => 6,
from_u32: u32 => 10,
from_i32: i32 => 11,
from_u64: u64 => 20,
from_i64: i64 => 20
}
#[cfg(target_pointer_width = "16")]
from_integers! {
from_usize: usize => 5,
from_isize: isize => 6
}
#[cfg(target_pointer_width = "32")]
from_integers! {
from_usize: usize => 10,
from_isize: isize => 11
}
#[cfg(target_pointer_width = "64")]
from_integers! {
from_usize: usize => 20,
from_isize: isize => 20
}
#[cfg(test)]
mod from_metadata_value_tests {
use super::*;
use crate::metadata::map::MetadataMap;
#[test]
fn it_can_insert_metadata_key_as_metadata_value() {
let mut map = MetadataMap::new();
map.insert(
"accept",
MetadataKey::<Ascii>::from_bytes(b"hello-world")
.unwrap()
.into(),
);
assert_eq!(
map.get("accept").unwrap(),
AsciiMetadataValue::try_from(b"hello-world").unwrap()
);
}
}
impl FromStr for MetadataValue<Ascii> {
type Err = InvalidMetadataValue;
#[inline]
fn from_str(s: &str) -> Result<MetadataValue<Ascii>, Self::Err> {
HeaderValue::from_str(s)
.map(|value| MetadataValue {
inner: value,
phantom: PhantomData,
})
.map_err(|_| InvalidMetadataValue::new())
}
}
impl<VE: ValueEncoding> From<MetadataValue<VE>> for Bytes {
#[inline]
fn from(value: MetadataValue<VE>) -> Bytes {
Bytes::copy_from_slice(value.inner.as_bytes())
}
}
impl<'a, VE: ValueEncoding> From<&'a MetadataValue<VE>> for MetadataValue<VE> {
#[inline]
fn from(t: &'a MetadataValue<VE>) -> Self {
t.clone()
}
}
// ===== ToStrError =====
impl ToStrError {
pub(crate) fn new() -> Self {
ToStrError { _priv: () }
}
}
impl fmt::Display for ToStrError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("failed to convert metadata to a str")
}
}
impl Error for ToStrError {}
impl Hash for MetadataValue<Ascii> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner.hash(state)
}
}
impl Hash for MetadataValue<Binary> {
fn hash<H: Hasher>(&self, state: &mut H) {
match self.to_bytes() {
Ok(b) => b.hash(state),
Err(e) => e.hash(state),
}
}
}
// ===== PartialEq / PartialOrd =====
impl<VE: ValueEncoding> PartialEq for MetadataValue<VE> {
#[inline]
fn eq(&self, other: &MetadataValue<VE>) -> bool {
// Note: Different binary strings that after base64 decoding
// will count as the same value for Binary values. Also,
// different invalid base64 values count as equal for Binary
// values.
VE::values_equal(&self.inner, &other.inner)
}
}
impl<VE: ValueEncoding> Eq for MetadataValue<VE> {}
impl<VE: ValueEncoding> PartialOrd for MetadataValue<VE> {
#[inline]
fn partial_cmp(&self, other: &MetadataValue<VE>) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<VE: ValueEncoding> Ord for MetadataValue<VE> {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.inner.cmp(&other.inner)
}
}
impl<VE: ValueEncoding> PartialEq<str> for MetadataValue<VE> {
#[inline]
fn eq(&self, other: &str) -> bool {
VE::equals(&self.inner, other.as_bytes())
}
}
impl<VE: ValueEncoding> PartialEq<[u8]> for MetadataValue<VE> {
#[inline]
fn eq(&self, other: &[u8]) -> bool {
VE::equals(&self.inner, other)
}
}
impl<VE: ValueEncoding> PartialOrd<str> for MetadataValue<VE> {
#[inline]
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
self.inner.partial_cmp(other.as_bytes())
}
}
impl<VE: ValueEncoding> PartialOrd<[u8]> for MetadataValue<VE> {
#[inline]
fn partial_cmp(&self, other: &[u8]) -> Option<cmp::Ordering> {
self.inner.partial_cmp(other)
}
}
impl<VE: ValueEncoding> PartialEq<MetadataValue<VE>> for str {
#[inline]
fn eq(&self, other: &MetadataValue<VE>) -> bool {
*other == *self
}
}
impl<VE: ValueEncoding> PartialEq<MetadataValue<VE>> for [u8] {
#[inline]
fn eq(&self, other: &MetadataValue<VE>) -> bool {
*other == *self
}
}
impl<VE: ValueEncoding> PartialOrd<MetadataValue<VE>> for str {
#[inline]
fn partial_cmp(&self, other: &MetadataValue<VE>) -> Option<cmp::Ordering> {
self.as_bytes().partial_cmp(other.inner.as_bytes())
}
}
impl<VE: ValueEncoding> PartialOrd<MetadataValue<VE>> for [u8] {
#[inline]
fn partial_cmp(&self, other: &MetadataValue<VE>) -> Option<cmp::Ordering> {
self.partial_cmp(other.inner.as_bytes())
}
}
impl<VE: ValueEncoding> PartialEq<String> for MetadataValue<VE> {
#[inline]
fn eq(&self, other: &String) -> bool {
*self == other[..]
}
}
impl<VE: ValueEncoding> PartialOrd<String> for MetadataValue<VE> {
#[inline]
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
self.inner.partial_cmp(other.as_bytes())
}
}
impl<VE: ValueEncoding> PartialEq<MetadataValue<VE>> for String {
#[inline]
fn eq(&self, other: &MetadataValue<VE>) -> bool {
*other == *self
}
}
impl<VE: ValueEncoding> PartialOrd<MetadataValue<VE>> for String {
#[inline]
fn partial_cmp(&self, other: &MetadataValue<VE>) -> Option<cmp::Ordering> {
self.as_bytes().partial_cmp(other.inner.as_bytes())
}
}
impl<'a, VE: ValueEncoding> PartialEq<MetadataValue<VE>> for &'a MetadataValue<VE> {
#[inline]
fn eq(&self, other: &MetadataValue<VE>) -> bool {
**self == *other
}
}
impl<'a, VE: ValueEncoding> PartialOrd<MetadataValue<VE>> for &'a MetadataValue<VE> {
#[inline]
fn partial_cmp(&self, other: &MetadataValue<VE>) -> Option<cmp::Ordering> {
(**self).partial_cmp(other)
}
}
impl<'a, VE: ValueEncoding, T: ?Sized> PartialEq<&'a T> for MetadataValue<VE>
where
MetadataValue<VE>: PartialEq<T>,
{
#[inline]
fn eq(&self, other: &&'a T) -> bool {
*self == **other
}
}
impl<'a, VE: ValueEncoding, T: ?Sized> PartialOrd<&'a T> for MetadataValue<VE>
where
MetadataValue<VE>: PartialOrd<T>,
{
#[inline]
fn partial_cmp(&self, other: &&'a T) -> Option<cmp::Ordering> {
self.partial_cmp(*other)
}
}
impl<'a, VE: ValueEncoding> PartialEq<MetadataValue<VE>> for &'a str {
#[inline]
fn eq(&self, other: &MetadataValue<VE>) -> bool {
*other == *self
}
}
impl<'a, VE: ValueEncoding> PartialOrd<MetadataValue<VE>> for &'a str {
#[inline]
fn partial_cmp(&self, other: &MetadataValue<VE>) -> Option<cmp::Ordering> {
self.as_bytes().partial_cmp(other.inner.as_bytes())
}
}
#[test]
fn test_debug() {
let cases = &[
("hello", "\"hello\""),
("hello \"world\"", "\"hello \\\"world\\\"\""),
("\u{7FFF}hello", "\"\\xe7\\xbf\\xbfhello\""),
];
for &(value, expected) in cases {
let val = AsciiMetadataValue::try_from(value.as_bytes()).unwrap();
let actual = format!("{:?}", val);
assert_eq!(expected, actual);
}
let mut sensitive = AsciiMetadataValue::from_static("password");
sensitive.set_sensitive(true);
assert_eq!("Sensitive", format!("{:?}", sensitive));
}
#[test]
fn test_is_empty() {
fn from_str<VE: ValueEncoding>(s: &str) -> MetadataValue<VE> {
MetadataValue::<VE>::unchecked_from_header_value(s.parse().unwrap())
}
assert!(from_str::<Ascii>("").is_empty());
assert!(from_str::<Binary>("").is_empty());
assert!(!from_str::<Ascii>("a").is_empty());
assert!(!from_str::<Binary>("a").is_empty());
assert!(!from_str::<Ascii>("=").is_empty());
assert!(from_str::<Binary>("=").is_empty());
assert!(!from_str::<Ascii>("===").is_empty());
assert!(from_str::<Binary>("===").is_empty());
assert!(!from_str::<Ascii>("=====").is_empty());
assert!(from_str::<Binary>("=====").is_empty());
}
#[test]
fn test_from_shared_base64_encodes() {
let value = BinaryMetadataValue::try_from(Bytes::from_static(b"Hello")).unwrap();
assert_eq!(value.as_encoded_bytes(), b"SGVsbG8");
}
#[test]
fn test_value_eq_value() {
type BMV = BinaryMetadataValue;
type AMV = AsciiMetadataValue;
assert_eq!(AMV::from_static("abc"), AMV::from_static("abc"));
assert_ne!(AMV::from_static("abc"), AMV::from_static("ABC"));
assert_eq!(BMV::from_bytes(b"abc"), BMV::from_bytes(b"abc"));
assert_ne!(BMV::from_bytes(b"abc"), BMV::from_bytes(b"ABC"));
// Padding is ignored.
assert_eq!(
BMV::from_static("SGVsbG8hIQ=="),
BMV::from_static("SGVsbG8hIQ")
);
// Invalid values are all just invalid from this point of view.
unsafe {
assert_eq!(
BMV::from_shared_unchecked(Bytes::from_static(b"..{}")),
BMV::from_shared_unchecked(Bytes::from_static(b"{}.."))
);
}
}
#[test]
fn test_value_eq_str() {
type BMV = BinaryMetadataValue;
type AMV = AsciiMetadataValue;
assert_eq!(AMV::from_static("abc"), "abc");
assert_ne!(AMV::from_static("abc"), "ABC");
assert_eq!("abc", AMV::from_static("abc"));
assert_ne!("ABC", AMV::from_static("abc"));
assert_eq!(BMV::from_bytes(b"abc"), "abc");
assert_ne!(BMV::from_bytes(b"abc"), "ABC");
assert_eq!("abc", BMV::from_bytes(b"abc"));
assert_ne!("ABC", BMV::from_bytes(b"abc"));
// Padding is ignored.
assert_eq!(BMV::from_static("SGVsbG8hIQ=="), "Hello!!");
assert_eq!("Hello!!", BMV::from_static("SGVsbG8hIQ=="));
}
#[test]
fn test_value_eq_bytes() {
type BMV = BinaryMetadataValue;
type AMV = AsciiMetadataValue;
assert_eq!(AMV::from_static("abc"), "abc".as_bytes());
assert_ne!(AMV::from_static("abc"), "ABC".as_bytes());
assert_eq!(*"abc".as_bytes(), AMV::from_static("abc"));
assert_ne!(*"ABC".as_bytes(), AMV::from_static("abc"));
assert_eq!(*"abc".as_bytes(), BMV::from_bytes(b"abc"));
assert_ne!(*"ABC".as_bytes(), BMV::from_bytes(b"abc"));
// Padding is ignored.
assert_eq!(BMV::from_static("SGVsbG8hIQ=="), "Hello!!".as_bytes());
assert_eq!(*"Hello!!".as_bytes(), BMV::from_static("SGVsbG8hIQ=="));
}
#[test]
fn test_ascii_value_hash() {
use std::collections::hash_map::DefaultHasher;
type AMV = AsciiMetadataValue;
fn hash(value: AMV) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}
let value1 = AMV::from_static("abc");
let value2 = AMV::from_static("abc");
assert_eq!(value1, value2);
assert_eq!(hash(value1), hash(value2));
let value1 = AMV::from_static("abc");
let value2 = AMV::from_static("xyz");
assert_ne!(value1, value2);
assert_ne!(hash(value1), hash(value2));
}
#[test]
fn test_valid_binary_value_hash() {
use std::collections::hash_map::DefaultHasher;
type BMV = BinaryMetadataValue;
fn hash(value: BMV) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}
let value1 = BMV::from_bytes(b"abc");
let value2 = BMV::from_bytes(b"abc");
assert_eq!(value1, value2);
assert_eq!(hash(value1), hash(value2));
let value1 = BMV::from_bytes(b"abc");
let value2 = BMV::from_bytes(b"xyz");
assert_ne!(value1, value2);
assert_ne!(hash(value1), hash(value2));
}
#[test]
fn test_invalid_binary_value_hash() {
use std::collections::hash_map::DefaultHasher;
type BMV = BinaryMetadataValue;
fn hash(value: BMV) -> u64 {
let mut hasher = DefaultHasher::new();
value.hash(&mut hasher);
hasher.finish()
}
unsafe {
let value1 = BMV::from_shared_unchecked(Bytes::from_static(b"..{}"));
let value2 = BMV::from_shared_unchecked(Bytes::from_static(b"{}.."));
assert_eq!(value1, value2);
assert_eq!(hash(value1), hash(value2));
}
unsafe {
let valid = BMV::from_bytes(b"abc");
let invalid = BMV::from_shared_unchecked(Bytes::from_static(b"{}.."));
assert_ne!(valid, invalid);
assert_ne!(hash(valid), hash(invalid));
}
}