1#![allow(
2 clippy::should_implement_trait,
3 clippy::no_effect,
4 clippy::missing_safety_doc
5)]
6use std::{cmp, error::Error, fmt, str, str::FromStr};
7
8use ntex_bytes::{ByteString, Bytes};
9
10#[allow(clippy::derived_hash_with_manual_eq)]
11#[derive(Clone, Hash, Eq)]
21pub struct HeaderValue {
22 inner: Bytes,
23 is_sensitive: bool,
24}
25
26#[derive(thiserror::Error, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
27#[error("Invalid HTTP header value")]
28pub struct InvalidHeaderValue {
31 _priv: (),
32}
33
34#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
39pub struct ToStrError {
40 _priv: (),
41}
42
43impl HeaderValue {
44 #[inline]
84 #[allow(unconditional_panic)] pub const fn from_static(src: &'static str) -> HeaderValue {
86 let bytes = src.as_bytes();
87 let mut i = 0;
88 while i < bytes.len() {
89 if !is_visible_ascii(bytes[i]) {
90 #[allow(clippy::out_of_bounds_indexing)]
91 ([] as [u8; 0])[0]; }
93 i += 1;
94 }
95
96 HeaderValue {
97 inner: Bytes::from_static(bytes),
98 is_sensitive: false,
99 }
100 }
101
102 #[inline]
128 pub fn from_str(src: &str) -> Result<HeaderValue, InvalidHeaderValue> {
129 HeaderValue::try_from_generic(src, |s| Bytes::copy_from_slice(s.as_bytes()))
130 }
131
132 #[inline]
157 pub fn from_bytes(src: &[u8]) -> Result<HeaderValue, InvalidHeaderValue> {
158 HeaderValue::try_from_generic(src, Bytes::copy_from_slice)
159 }
160
161 pub fn from_shared<T>(src: T) -> Result<HeaderValue, InvalidHeaderValue>
166 where
167 Bytes: From<T>,
168 {
169 let inner = Bytes::from(src);
170 for &b in inner.as_ref() {
171 if !is_valid(b) {
172 return Err(InvalidHeaderValue { _priv: () });
173 }
174 }
175
176 Ok(HeaderValue {
177 inner,
178 is_sensitive: false,
179 })
180 }
181
182 pub unsafe fn from_shared_unchecked(src: Bytes) -> HeaderValue {
187 HeaderValue {
188 inner: src,
189 is_sensitive: false,
190 }
191 }
192
193 #[deprecated]
194 #[doc(hidden)]
195 pub fn from_maybe_shared<T>(src: T) -> Result<HeaderValue, InvalidHeaderValue>
196 where
197 Bytes: From<T>,
198 {
199 HeaderValue::from_shared(src)
200 }
201
202 fn try_from_generic<T: AsRef<[u8]>, F: FnOnce(T) -> Bytes>(
203 src: T,
204 into: F,
205 ) -> Result<HeaderValue, InvalidHeaderValue> {
206 for &b in src.as_ref() {
207 if !is_valid(b) {
208 return Err(InvalidHeaderValue { _priv: () });
209 }
210 }
211 Ok(HeaderValue {
212 inner: into(src),
213 is_sensitive: false,
214 })
215 }
216
217 pub fn to_str(&self) -> Result<&str, ToStrError> {
231 let bytes = self.as_ref();
232
233 for &b in bytes {
234 if !is_visible_ascii(b) {
235 return Err(ToStrError { _priv: () });
236 }
237 }
238
239 unsafe { Ok(str::from_utf8_unchecked(bytes)) }
240 }
241
242 #[inline]
254 pub fn len(&self) -> usize {
255 self.as_ref().len()
256 }
257
258 #[inline]
271 pub fn is_empty(&self) -> bool {
272 self.len() == 0
273 }
274
275 #[inline]
285 pub fn as_bytes(&self) -> &[u8] {
286 self.as_ref()
287 }
288
289 #[inline]
300 pub fn as_shared(&self) -> &Bytes {
301 &self.inner
302 }
303
304 #[inline]
319 pub fn set_sensitive(&mut self, val: bool) {
320 self.is_sensitive = val;
321 }
322
323 #[inline]
350 pub fn is_sensitive(&self) -> bool {
351 self.is_sensitive
352 }
353}
354
355impl AsRef<[u8]> for HeaderValue {
356 #[inline]
357 fn as_ref(&self) -> &[u8] {
358 self.inner.as_ref()
359 }
360}
361
362impl fmt::Debug for HeaderValue {
363 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
364 if self.is_sensitive {
365 f.write_str("Sensitive")
366 } else {
367 f.write_str("\"")?;
368 let mut from = 0;
369 let bytes = self.as_bytes();
370 for (i, &b) in bytes.iter().enumerate() {
371 if !is_visible_ascii(b) || b == b'"' {
372 if from != i {
373 f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..i]) })?;
374 }
375 if b == b'"' {
376 f.write_str("\\\"")?;
377 } else {
378 write!(f, "\\x{b:x}")?;
379 }
380 from = i + 1;
381 }
382 }
383
384 f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..]) })?;
385 f.write_str("\"")
386 }
387 }
388}
389
390impl FromStr for HeaderValue {
391 type Err = InvalidHeaderValue;
392
393 #[inline]
394 fn from_str(s: &str) -> Result<HeaderValue, Self::Err> {
395 HeaderValue::from_str(s)
396 }
397}
398
399impl<'a> From<&'a HeaderValue> for HeaderValue {
400 #[inline]
401 fn from(t: &'a HeaderValue) -> Self {
402 t.clone()
403 }
404}
405
406impl From<http::header::HeaderValue> for HeaderValue {
407 #[inline]
408 fn from(t: http::header::HeaderValue) -> Self {
409 let inner = Bytes::copy_from_slice(t.as_ref());
410 HeaderValue {
411 inner,
412 is_sensitive: t.is_sensitive(),
413 }
414 }
415}
416
417impl<'a> From<&'a http::header::HeaderValue> for HeaderValue {
418 #[inline]
419 fn from(t: &'a http::header::HeaderValue) -> Self {
420 let inner = Bytes::copy_from_slice(t.as_ref());
421 HeaderValue {
422 inner,
423 is_sensitive: t.is_sensitive(),
424 }
425 }
426}
427
428impl<'a> TryFrom<&'a str> for HeaderValue {
429 type Error = InvalidHeaderValue;
430
431 #[inline]
432 fn try_from(t: &'a str) -> Result<Self, Self::Error> {
433 t.parse()
434 }
435}
436
437impl<'a> TryFrom<&'a String> for HeaderValue {
438 type Error = InvalidHeaderValue;
439 #[inline]
440 fn try_from(s: &'a String) -> Result<Self, Self::Error> {
441 Self::from_bytes(s.as_bytes())
442 }
443}
444
445impl<'a> TryFrom<&'a ByteString> for HeaderValue {
446 type Error = InvalidHeaderValue;
447 #[inline]
448 fn try_from(s: &'a ByteString) -> Result<Self, Self::Error> {
449 Self::from_shared(s.as_bytes().clone())
450 }
451}
452
453impl<'a> TryFrom<&'a [u8]> for HeaderValue {
454 type Error = InvalidHeaderValue;
455
456 #[inline]
457 fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
458 HeaderValue::from_bytes(t)
459 }
460}
461
462impl TryFrom<String> for HeaderValue {
463 type Error = InvalidHeaderValue;
464
465 #[inline]
466 fn try_from(s: String) -> Result<Self, Self::Error> {
467 HeaderValue::from_shared(s)
468 }
469}
470
471impl TryFrom<ByteString> for HeaderValue {
472 type Error = InvalidHeaderValue;
473 #[inline]
474 fn try_from(s: ByteString) -> Result<Self, Self::Error> {
475 Self::from_shared(s.into_bytes())
476 }
477}
478
479impl TryFrom<Vec<u8>> for HeaderValue {
480 type Error = InvalidHeaderValue;
481
482 #[inline]
483 fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
484 HeaderValue::from_shared(vec)
485 }
486}
487
488impl From<HeaderValue> for http::header::HeaderValue {
489 #[inline]
490 fn from(t: HeaderValue) -> Self {
491 let mut hdr = Self::from_bytes(t.as_bytes()).unwrap();
492 hdr.set_sensitive(t.is_sensitive());
493 hdr
494 }
495}
496
497impl<'a> From<&'a HeaderValue> for http::header::HeaderValue {
498 #[inline]
499 fn from(t: &'a HeaderValue) -> Self {
500 let mut hdr = Self::from_bytes(t.as_bytes()).unwrap();
501 hdr.set_sensitive(t.is_sensitive());
502 hdr
503 }
504}
505
506const fn is_visible_ascii(b: u8) -> bool {
507 b >= 32 && b < 127 || b == b'\t'
508}
509
510#[inline]
511fn is_valid(b: u8) -> bool {
512 b >= 32 && b != 127 || b == b'\t'
513}
514
515impl fmt::Debug for InvalidHeaderValue {
516 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
517 f.debug_struct("InvalidHeaderValue")
518 .finish()
520 }
521}
522
523impl Error for ToStrError {}
524
525impl fmt::Display for ToStrError {
526 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
527 f.write_str("failed to convert header to a str")
528 }
529}
530
531impl PartialEq for HeaderValue {
534 #[inline]
535 fn eq(&self, other: &HeaderValue) -> bool {
536 self.inner == other.inner
537 }
538}
539
540impl PartialOrd for HeaderValue {
541 #[inline]
542 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
543 Some(self.cmp(other))
544 }
545}
546
547impl Ord for HeaderValue {
548 #[inline]
549 fn cmp(&self, other: &Self) -> cmp::Ordering {
550 self.inner.cmp(&other.inner)
551 }
552}
553
554impl PartialEq<str> for HeaderValue {
555 #[inline]
556 fn eq(&self, other: &str) -> bool {
557 self.inner == other.as_bytes()
558 }
559}
560
561impl PartialEq<[u8]> for HeaderValue {
562 #[inline]
563 fn eq(&self, other: &[u8]) -> bool {
564 self.inner == other
565 }
566}
567
568impl PartialOrd<str> for HeaderValue {
569 #[inline]
570 fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
571 (*self.inner).partial_cmp(other.as_bytes())
572 }
573}
574
575impl PartialOrd<[u8]> for HeaderValue {
576 #[inline]
577 fn partial_cmp(&self, other: &[u8]) -> Option<cmp::Ordering> {
578 (*self.inner).partial_cmp(other)
579 }
580}
581
582impl PartialEq<HeaderValue> for str {
583 #[inline]
584 fn eq(&self, other: &HeaderValue) -> bool {
585 *other == *self
586 }
587}
588
589impl PartialEq<HeaderValue> for [u8] {
590 #[inline]
591 fn eq(&self, other: &HeaderValue) -> bool {
592 *other == *self
593 }
594}
595
596impl PartialOrd<HeaderValue> for str {
597 #[inline]
598 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
599 self.as_bytes().partial_cmp(other.as_bytes())
600 }
601}
602
603impl PartialOrd<HeaderValue> for [u8] {
604 #[inline]
605 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
606 self.partial_cmp(other.as_bytes())
607 }
608}
609
610impl PartialEq<String> for HeaderValue {
611 #[inline]
612 fn eq(&self, other: &String) -> bool {
613 *self == other[..]
614 }
615}
616
617impl PartialOrd<String> for HeaderValue {
618 #[inline]
619 fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
620 self.inner.partial_cmp(other.as_bytes())
621 }
622}
623
624impl PartialEq<HeaderValue> for String {
625 #[inline]
626 fn eq(&self, other: &HeaderValue) -> bool {
627 *other == *self
628 }
629}
630
631impl PartialOrd<HeaderValue> for String {
632 #[inline]
633 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
634 self.as_bytes().partial_cmp(other.as_bytes())
635 }
636}
637
638impl PartialEq<HeaderValue> for &HeaderValue {
639 #[inline]
640 fn eq(&self, other: &HeaderValue) -> bool {
641 **self == *other
642 }
643}
644
645impl PartialOrd<HeaderValue> for &HeaderValue {
646 #[inline]
647 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
648 (**self).partial_cmp(other)
649 }
650}
651
652impl<'a, T: ?Sized> PartialEq<&'a T> for HeaderValue
653where
654 HeaderValue: PartialEq<T>,
655{
656 #[inline]
657 fn eq(&self, other: &&'a T) -> bool {
658 *self == **other
659 }
660}
661
662impl<'a, T: ?Sized> PartialOrd<&'a T> for HeaderValue
663where
664 HeaderValue: PartialOrd<T>,
665{
666 #[inline]
667 fn partial_cmp(&self, other: &&'a T) -> Option<cmp::Ordering> {
668 self.partial_cmp(*other)
669 }
670}
671
672impl PartialEq<HeaderValue> for &str {
673 #[inline]
674 fn eq(&self, other: &HeaderValue) -> bool {
675 *other == *self
676 }
677}
678
679impl PartialOrd<HeaderValue> for &str {
680 #[inline]
681 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
682 self.as_bytes().partial_cmp(other.as_bytes())
683 }
684}
685
686macro_rules! from_integers {
687 ($($name:ident: $t:ident => $max_len:expr),*) => {$(
688 impl From<$t> for HeaderValue {
689 fn from(num: $t) -> HeaderValue {
690 let mut b = itoa::Buffer::new();
691 let inner = Bytes::copy_from_slice(b.format(num).as_ref());
692 HeaderValue {
693 inner,
694 is_sensitive: false,
695 }
696 }
697 }
698
699 #[test]
700 fn $name() {
701 let n: $t = 55;
702 let val = HeaderValue::from(n);
703 assert_eq!(val, &n.to_string());
704
705 let n = $t::MAX;
706 let val = HeaderValue::from(n);
707 assert_eq!(val, &n.to_string());
708 }
709 )*};
710}
711
712from_integers! {
713 from_u16: u16 => 5,
717 from_i16: i16 => 6,
718 from_u32: u32 => 10,
719 from_i32: i32 => 11,
720 from_u64: u64 => 20,
721 from_i64: i64 => 20
722}
723
724#[cfg(target_pointer_width = "16")]
725from_integers! {
726 from_usize: usize => 5,
727 from_isize: isize => 6
728}
729
730#[cfg(target_pointer_width = "32")]
731from_integers! {
732 from_usize: usize => 10,
733 from_isize: isize => 11
734}
735
736#[cfg(target_pointer_width = "64")]
737from_integers! {
738 from_usize: usize => 20,
739 from_isize: isize => 20
740}
741
742#[cfg(test)]
743mod tests {
744 use super::*;
745
746 #[test]
747 #[allow(clippy::op_ref, clippy::cmp_owned, deprecated)]
748 fn test_basics() {
749 assert!(HeaderValue::from_str("").unwrap().is_empty());
750
751 let hdr = HeaderValue::from_bytes(b"upgrade").unwrap();
752 let hdr2 = HeaderValue::from(&hdr);
753 assert_eq!(hdr, hdr2);
754
755 let hdr3 = HeaderValue::from_maybe_shared(Bytes::from_static(b"upgrade")).unwrap();
756 assert_eq!(hdr, hdr3);
757
758 let hdr = http::header::HeaderValue::from_bytes(b"upgrade").unwrap();
759 let hdr2 = HeaderValue::from(&hdr);
760 assert_eq!(hdr2.as_bytes(), b"upgrade");
761 assert_eq!(hdr2.as_shared(), &Bytes::from_static(b"upgrade"));
762 let hdr2 = HeaderValue::from(hdr);
763 assert_eq!(hdr2.as_bytes(), b"upgrade");
764
765 let hdr = HeaderValue::try_from("upgrade".to_string()).unwrap();
766 assert_eq!(hdr.as_bytes(), b"upgrade");
767 let hdr = HeaderValue::try_from(&("upgrade".to_string())).unwrap();
768 assert_eq!(hdr.as_bytes(), b"upgrade");
769 let hdr = HeaderValue::try_from(ByteString::from("upgrade")).unwrap();
770 assert_eq!(hdr.as_bytes(), b"upgrade");
771 let hdr = HeaderValue::try_from(&ByteString::from("upgrade")).unwrap();
772 assert_eq!(hdr.as_bytes(), b"upgrade");
773 let hdr2 = HeaderValue::try_from(&ByteString::from("upgrade2")).unwrap();
774
775 assert!(hdr == hdr);
776 assert!(hdr == &hdr);
777 assert!(hdr == "upgrade");
778 assert!(hdr == "upgrade".to_string());
779 assert!("upgrade" == hdr);
780 assert!("upgrade" == &hdr);
781 assert!("upgrade".to_string() == hdr);
782 assert!(hdr < hdr2);
783 assert!(hdr < &hdr2);
784 assert!(&hdr < &hdr2);
785 assert!(&hdr < "upgrade2");
786 assert!(hdr < "upgrade2");
787 assert!(hdr < "upgrade2".to_string());
788 assert!(hdr < &b"upgrade2"[..]);
789 assert!(hdr < b"upgrade2"[..]);
790 assert!(hdr != &b"upgrade2"[..]);
791 assert!(hdr != b"upgrade2"[..]);
792 assert!("upgrade2" > hdr);
793 assert!("upgrade2".to_string() > hdr);
794 assert!(b"upgrade2"[..] > hdr);
795 assert!("upgrade2"[..] != hdr);
796 }
797
798 #[test]
799 fn test_try_from() {
800 HeaderValue::try_from(vec![127]).unwrap_err();
801 }
802
803 #[test]
804 fn it_converts_using_try_from() {
805 assert!(HeaderValue::from_bytes(b"upgrade").is_ok());
806 }
807
808 #[test]
809 fn into_http_value() {
810 let hdr = HeaderValue::from_bytes(b"upgrade").unwrap();
811 let _ = http::header::HeaderValue::from(&hdr);
812 let _ = http::header::HeaderValue::from(hdr);
813 }
814
815 #[test]
816 fn test_fmt() {
817 let cases = &[
818 ("hello", "\"hello\""),
819 ("hello \"world\"", "\"hello \\\"world\\\"\""),
820 ("\u{7FFF}hello", "\"\\xe7\\xbf\\xbfhello\""),
821 ];
822
823 for &(value, expected) in cases {
824 let val = HeaderValue::from_bytes(value.as_bytes()).unwrap();
825 let actual = format!("{val:?}");
826 assert_eq!(expected, actual);
827 }
828
829 let mut sensitive = HeaderValue::from_static("password");
830 sensitive.set_sensitive(true);
831 assert_eq!("Sensitive", format!("{sensitive:?}"));
832
833 let s = format!("{:?}", InvalidHeaderValue { _priv: {} });
834 assert_eq!(s, "InvalidHeaderValue");
835
836 let s = format!("{}", ToStrError { _priv: {} });
837 assert_eq!(s, "failed to convert header to a str");
838 }
839}