#![allow(
clippy::derive_hash_xor_eq,
clippy::should_implement_trait,
clippy::no_effect,
clippy::missing_safety_doc
)]
use std::{cmp, convert::TryFrom, error::Error, fmt, str, str::FromStr};
use ntex_bytes::{ByteString, Bytes};
#[derive(Clone, Hash)]
pub struct HeaderValue {
inner: Bytes,
is_sensitive: bool,
}
pub struct InvalidHeaderValue {
_priv: (),
}
#[derive(Debug)]
pub struct ToStrError {
_priv: (),
}
impl HeaderValue {
#[inline]
#[allow(unconditional_panic)] pub const fn from_static(src: &'static str) -> HeaderValue {
let bytes = src.as_bytes();
let mut i = 0;
while i < bytes.len() {
if !is_visible_ascii(bytes[i]) {
([] as [u8; 0])[0]; }
i += 1;
}
HeaderValue {
inner: Bytes::from_static(bytes),
is_sensitive: false,
}
}
#[inline]
pub fn from_str(src: &str) -> Result<HeaderValue, InvalidHeaderValue> {
HeaderValue::try_from_generic(src, |s| Bytes::copy_from_slice(s.as_bytes()))
}
#[inline]
pub fn from_bytes(src: &[u8]) -> Result<HeaderValue, InvalidHeaderValue> {
HeaderValue::try_from_generic(src, Bytes::copy_from_slice)
}
pub fn from_shared<T>(src: T) -> Result<HeaderValue, InvalidHeaderValue>
where
Bytes: From<T>,
{
let inner = Bytes::from(src);
for &b in inner.as_ref() {
if !is_valid(b) {
return Err(InvalidHeaderValue { _priv: () });
}
}
Ok(HeaderValue {
inner,
is_sensitive: false,
})
}
pub unsafe fn from_shared_unchecked(src: Bytes) -> HeaderValue {
HeaderValue {
inner: src,
is_sensitive: false,
}
}
#[deprecated]
#[doc(hidden)]
pub fn from_maybe_shared<T>(src: T) -> Result<HeaderValue, InvalidHeaderValue>
where
Bytes: From<T>,
{
HeaderValue::from_shared(src)
}
fn try_from_generic<T: AsRef<[u8]>, F: FnOnce(T) -> Bytes>(
src: T,
into: F,
) -> Result<HeaderValue, InvalidHeaderValue> {
for &b in src.as_ref() {
if !is_valid(b) {
return Err(InvalidHeaderValue { _priv: () });
}
}
Ok(HeaderValue {
inner: into(src),
is_sensitive: false,
})
}
pub fn to_str(&self) -> Result<&str, ToStrError> {
let bytes = self.as_ref();
for &b in bytes {
if !is_visible_ascii(b) {
return Err(ToStrError { _priv: () });
}
}
unsafe { Ok(str::from_utf8_unchecked(bytes)) }
}
#[inline]
pub fn len(&self) -> usize {
self.as_ref().len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn as_bytes(&self) -> &[u8] {
self.as_ref()
}
#[inline]
pub fn as_shared(&self) -> &Bytes {
&self.inner
}
#[inline]
pub fn set_sensitive(&mut self, val: bool) {
self.is_sensitive = val;
}
#[inline]
pub fn is_sensitive(&self) -> bool {
self.is_sensitive
}
}
impl AsRef<[u8]> for HeaderValue {
#[inline]
fn as_ref(&self) -> &[u8] {
self.inner.as_ref()
}
}
impl fmt::Debug for HeaderValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_sensitive {
f.write_str("Sensitive")
} else {
f.write_str("\"")?;
let mut from = 0;
let bytes = self.as_bytes();
for (i, &b) in bytes.iter().enumerate() {
if !is_visible_ascii(b) || b == b'"' {
if from != i {
f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..i]) })?;
}
if b == b'"' {
f.write_str("\\\"")?;
} else {
write!(f, "\\x{:x}", b)?;
}
from = i + 1;
}
}
f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..]) })?;
f.write_str("\"")
}
}
}
impl FromStr for HeaderValue {
type Err = InvalidHeaderValue;
#[inline]
fn from_str(s: &str) -> Result<HeaderValue, Self::Err> {
HeaderValue::from_str(s)
}
}
impl<'a> From<&'a HeaderValue> for HeaderValue {
#[inline]
fn from(t: &'a HeaderValue) -> Self {
t.clone()
}
}
impl From<http::header::HeaderValue> for HeaderValue {
#[inline]
fn from(t: http::header::HeaderValue) -> Self {
let inner = Bytes::copy_from_slice(t.as_ref());
HeaderValue {
inner,
is_sensitive: t.is_sensitive(),
}
}
}
impl<'a> From<&'a http::header::HeaderValue> for HeaderValue {
#[inline]
fn from(t: &'a http::header::HeaderValue) -> Self {
let inner = Bytes::copy_from_slice(t.as_ref());
HeaderValue {
inner,
is_sensitive: t.is_sensitive(),
}
}
}
impl<'a> TryFrom<&'a str> for HeaderValue {
type Error = InvalidHeaderValue;
#[inline]
fn try_from(t: &'a str) -> Result<Self, Self::Error> {
t.parse()
}
}
impl<'a> TryFrom<&'a String> for HeaderValue {
type Error = InvalidHeaderValue;
#[inline]
fn try_from(s: &'a String) -> Result<Self, Self::Error> {
Self::from_bytes(s.as_bytes())
}
}
impl<'a> TryFrom<&'a ByteString> for HeaderValue {
type Error = InvalidHeaderValue;
#[inline]
fn try_from(s: &'a ByteString) -> Result<Self, Self::Error> {
Self::from_shared(s.as_bytes().clone())
}
}
impl<'a> TryFrom<&'a [u8]> for HeaderValue {
type Error = InvalidHeaderValue;
#[inline]
fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
HeaderValue::from_bytes(t)
}
}
impl TryFrom<String> for HeaderValue {
type Error = InvalidHeaderValue;
#[inline]
fn try_from(s: String) -> Result<Self, Self::Error> {
HeaderValue::from_shared(s)
}
}
impl TryFrom<ByteString> for HeaderValue {
type Error = InvalidHeaderValue;
#[inline]
fn try_from(s: ByteString) -> Result<Self, Self::Error> {
Self::from_shared(s.into_bytes())
}
}
impl TryFrom<Vec<u8>> for HeaderValue {
type Error = InvalidHeaderValue;
#[inline]
fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
HeaderValue::from_shared(vec)
}
}
impl From<HeaderValue> for http::header::HeaderValue {
#[inline]
fn from(t: HeaderValue) -> Self {
let mut hdr = Self::from_bytes(t.as_bytes()).unwrap();
hdr.set_sensitive(t.is_sensitive());
hdr
}
}
impl<'a> From<&'a HeaderValue> for http::header::HeaderValue {
#[inline]
fn from(t: &'a HeaderValue) -> Self {
let mut hdr = Self::from_bytes(t.as_bytes()).unwrap();
hdr.set_sensitive(t.is_sensitive());
hdr
}
}
const fn is_visible_ascii(b: u8) -> bool {
b >= 32 && b < 127 || b == b'\t'
}
#[inline]
fn is_valid(b: u8) -> bool {
b >= 32 && b != 127 || b == b'\t'
}
impl Error for InvalidHeaderValue {}
impl fmt::Debug for InvalidHeaderValue {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("InvalidHeaderValue")
.finish()
}
}
impl fmt::Display for InvalidHeaderValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("failed to parse header value")
}
}
impl Error for ToStrError {}
impl fmt::Display for ToStrError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("failed to convert header to a str")
}
}
impl PartialEq for HeaderValue {
#[inline]
fn eq(&self, other: &HeaderValue) -> bool {
self.inner == other.inner
}
}
impl Eq for HeaderValue {}
impl PartialOrd for HeaderValue {
#[inline]
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
self.inner.partial_cmp(&other.inner)
}
}
impl Ord for HeaderValue {
#[inline]
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.inner.cmp(&other.inner)
}
}
impl PartialEq<str> for HeaderValue {
#[inline]
fn eq(&self, other: &str) -> bool {
self.inner == other.as_bytes()
}
}
impl PartialEq<[u8]> for HeaderValue {
#[inline]
fn eq(&self, other: &[u8]) -> bool {
self.inner == other
}
}
impl PartialOrd<str> for HeaderValue {
#[inline]
fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
(*self.inner).partial_cmp(other.as_bytes())
}
}
impl PartialOrd<[u8]> for HeaderValue {
#[inline]
fn partial_cmp(&self, other: &[u8]) -> Option<cmp::Ordering> {
(*self.inner).partial_cmp(other)
}
}
impl PartialEq<HeaderValue> for str {
#[inline]
fn eq(&self, other: &HeaderValue) -> bool {
*other == *self
}
}
impl PartialEq<HeaderValue> for [u8] {
#[inline]
fn eq(&self, other: &HeaderValue) -> bool {
*other == *self
}
}
impl PartialOrd<HeaderValue> for str {
#[inline]
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
self.as_bytes().partial_cmp(other.as_bytes())
}
}
impl PartialOrd<HeaderValue> for [u8] {
#[inline]
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
self.partial_cmp(other.as_bytes())
}
}
impl PartialEq<String> for HeaderValue {
#[inline]
fn eq(&self, other: &String) -> bool {
*self == other[..]
}
}
impl PartialOrd<String> for HeaderValue {
#[inline]
fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
self.inner.partial_cmp(other.as_bytes())
}
}
impl PartialEq<HeaderValue> for String {
#[inline]
fn eq(&self, other: &HeaderValue) -> bool {
*other == *self
}
}
impl PartialOrd<HeaderValue> for String {
#[inline]
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
self.as_bytes().partial_cmp(other.as_bytes())
}
}
impl<'a> PartialEq<HeaderValue> for &'a HeaderValue {
#[inline]
fn eq(&self, other: &HeaderValue) -> bool {
**self == *other
}
}
impl<'a> PartialOrd<HeaderValue> for &'a HeaderValue {
#[inline]
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
(**self).partial_cmp(other)
}
}
impl<'a, T: ?Sized> PartialEq<&'a T> for HeaderValue
where
HeaderValue: PartialEq<T>,
{
#[inline]
fn eq(&self, other: &&'a T) -> bool {
*self == **other
}
}
impl<'a, T: ?Sized> PartialOrd<&'a T> for HeaderValue
where
HeaderValue: PartialOrd<T>,
{
#[inline]
fn partial_cmp(&self, other: &&'a T) -> Option<cmp::Ordering> {
self.partial_cmp(*other)
}
}
impl<'a> PartialEq<HeaderValue> for &'a str {
#[inline]
fn eq(&self, other: &HeaderValue) -> bool {
*other == *self
}
}
impl<'a> PartialOrd<HeaderValue> for &'a str {
#[inline]
fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
self.as_bytes().partial_cmp(other.as_bytes())
}
}
macro_rules! from_integers {
($($name:ident: $t:ident => $max_len:expr),*) => {$(
impl From<$t> for HeaderValue {
fn from(num: $t) -> HeaderValue {
let mut b = itoa::Buffer::new();
let inner = Bytes::copy_from_slice(b.format(num).as_ref());
HeaderValue {
inner,
is_sensitive: false,
}
}
}
#[test]
fn $name() {
let n: $t = 55;
let val = HeaderValue::from(n);
assert_eq!(val, &n.to_string());
let n = ::std::$t::MAX;
let val = HeaderValue::from(n);
assert_eq!(val, &n.to_string());
}
)*};
}
from_integers! {
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 tests {
use super::*;
#[test]
fn test_basics() {
assert!(HeaderValue::from_str("").unwrap().is_empty());
let hdr = HeaderValue::from_bytes(b"upgrade").unwrap();
let hdr2 = HeaderValue::from(&hdr);
assert_eq!(hdr, hdr2);
let hdr = http::header::HeaderValue::from_bytes(b"upgrade").unwrap();
let hdr2 = HeaderValue::from(&hdr);
assert_eq!(hdr2.as_bytes(), b"upgrade");
assert_eq!(hdr2.as_shared(), &Bytes::from_static(b"upgrade"));
let hdr2 = HeaderValue::from(hdr);
assert_eq!(hdr2.as_bytes(), b"upgrade");
let hdr = HeaderValue::try_from("upgrade".to_string()).unwrap();
assert_eq!(hdr.as_bytes(), b"upgrade");
let hdr = HeaderValue::try_from(ByteString::from("upgrade")).unwrap();
assert_eq!(hdr.as_bytes(), b"upgrade");
let hdr = HeaderValue::try_from(&ByteString::from("upgrade")).unwrap();
assert_eq!(hdr.as_bytes(), b"upgrade");
}
#[test]
fn test_try_from() {
HeaderValue::try_from(vec![127]).unwrap_err();
}
#[test]
fn it_converts_using_try_from() {
assert!(HeaderValue::from_bytes(b"upgrade").is_ok());
}
#[test]
fn into_http_value() {
let hdr = HeaderValue::from_bytes(b"upgrade").unwrap();
let _ = http::header::HeaderValue::from(&hdr);
let _ = http::header::HeaderValue::from(hdr);
}
#[test]
fn test_fmt() {
let cases = &[
("hello", "\"hello\""),
("hello \"world\"", "\"hello \\\"world\\\"\""),
("\u{7FFF}hello", "\"\\xe7\\xbf\\xbfhello\""),
];
for &(value, expected) in cases {
let val = HeaderValue::from_bytes(value.as_bytes()).unwrap();
let actual = format!("{:?}", val);
assert_eq!(expected, actual);
}
let mut sensitive = HeaderValue::from_static("password");
sensitive.set_sensitive(true);
assert_eq!("Sensitive", format!("{:?}", sensitive));
let s = format!("{:?}", InvalidHeaderValue { _priv: {} });
assert_eq!(s, "InvalidHeaderValue");
let s = format!("{}", ToStrError { _priv: {} });
assert_eq!(s, "failed to convert header to a str");
}
}