1#![allow(
2 clippy::should_implement_trait,
3 clippy::no_effect,
4 clippy::missing_safety_doc
5)]
6use std::{cmp, fmt, hash, str, str::FromStr};
7
8use ntex_bytes::{ByteString, Bytes, BytesMut};
9
10#[derive(Clone, Eq)]
20pub struct HeaderValue {
21 inner: Bytes,
22 is_sensitive: bool,
23}
24
25#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
26#[error("Invalid HTTP header value")]
27pub struct InvalidHeaderValue {
30 _priv: (),
31}
32
33#[derive(thiserror::Error, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
38#[error("failed to convert header to a str")]
39pub struct ToStrError {
40 _priv: (),
41}
42
43impl HeaderValue {
44 #[inline]
63 pub const fn from_static(src: &'static str) -> HeaderValue {
64 let bytes = src.as_bytes();
65 let mut i = 0;
66 while i < bytes.len() {
67 assert!(is_visible_ascii(bytes[i]), "invalid header value");
68 i += 1;
69 }
70
71 HeaderValue {
72 inner: Bytes::from_static(bytes),
73 is_sensitive: false,
74 }
75 }
76
77 #[inline]
103 pub fn from_str(src: &str) -> Result<HeaderValue, InvalidHeaderValue> {
104 HeaderValue::try_from_generic(src, |s| Bytes::copy_from_slice(s.as_bytes()))
105 }
106
107 #[inline]
129 pub fn from_bytes(src: &[u8]) -> Result<HeaderValue, InvalidHeaderValue> {
130 HeaderValue::try_from_generic(src, Bytes::copy_from_slice)
131 }
132
133 pub fn from_shared<T>(src: T) -> Result<HeaderValue, InvalidHeaderValue>
139 where
140 Bytes: From<T>,
141 {
142 let inner = Bytes::from(src);
143 for &b in inner.as_ref() {
144 if !is_valid(b) {
145 return Err(InvalidHeaderValue { _priv: () });
146 }
147 }
148
149 Ok(HeaderValue {
150 inner,
151 is_sensitive: false,
152 })
153 }
154
155 pub unsafe fn from_shared_unchecked(src: Bytes) -> HeaderValue {
162 HeaderValue {
163 inner: src,
164 is_sensitive: false,
165 }
166 }
167
168 fn try_from_generic<T: AsRef<[u8]>, F: FnOnce(T) -> Bytes>(
169 src: T,
170 into: F,
171 ) -> Result<HeaderValue, InvalidHeaderValue> {
172 for &b in src.as_ref() {
173 if !is_valid(b) {
174 return Err(InvalidHeaderValue { _priv: () });
175 }
176 }
177 Ok(HeaderValue {
178 inner: into(src),
179 is_sensitive: false,
180 })
181 }
182
183 pub fn to_str(&self) -> Result<&str, ToStrError> {
197 let bytes = self.as_ref();
198
199 for &b in bytes {
200 if !is_visible_ascii(b) {
201 return Err(ToStrError { _priv: () });
202 }
203 }
204
205 unsafe { Ok(str::from_utf8_unchecked(bytes)) }
206 }
207
208 #[inline]
220 pub fn len(&self) -> usize {
221 self.as_ref().len()
222 }
223
224 #[inline]
237 pub fn is_empty(&self) -> bool {
238 self.len() == 0
239 }
240
241 #[inline]
251 pub fn as_bytes(&self) -> &[u8] {
252 self.as_ref()
253 }
254
255 #[inline]
266 pub fn as_shared(&self) -> &Bytes {
267 &self.inner
268 }
269
270 #[inline]
285 pub fn set_sensitive(&mut self, val: bool) {
286 self.is_sensitive = val;
287 }
288
289 #[inline]
316 pub fn is_sensitive(&self) -> bool {
317 self.is_sensitive
318 }
319}
320
321impl AsRef<[u8]> for HeaderValue {
322 #[inline]
323 fn as_ref(&self) -> &[u8] {
324 self.inner.as_ref()
325 }
326}
327
328impl fmt::Debug for HeaderValue {
329 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
330 if self.is_sensitive {
331 f.write_str("Sensitive")
332 } else {
333 f.write_str("\"")?;
334 let mut from = 0;
335 let bytes = self.as_bytes();
336 for (i, &b) in bytes.iter().enumerate() {
337 if !is_visible_ascii(b) || b == b'"' {
338 if from != i {
339 f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..i]) })?;
340 }
341 if b == b'"' {
342 f.write_str("\\\"")?;
343 } else {
344 write!(f, "\\x{b:x}")?;
345 }
346 from = i + 1;
347 }
348 }
349
350 f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..]) })?;
351 f.write_str("\"")
352 }
353 }
354}
355
356impl FromStr for HeaderValue {
357 type Err = InvalidHeaderValue;
358
359 #[inline]
360 fn from_str(s: &str) -> Result<HeaderValue, Self::Err> {
361 HeaderValue::from_str(s)
362 }
363}
364
365impl<'a> From<&'a HeaderValue> for HeaderValue {
366 #[inline]
367 fn from(t: &'a HeaderValue) -> Self {
368 t.clone()
369 }
370}
371
372impl From<http::header::HeaderValue> for HeaderValue {
373 #[inline]
374 fn from(t: http::header::HeaderValue) -> Self {
375 let inner = Bytes::copy_from_slice(t.as_ref());
376 HeaderValue {
377 inner,
378 is_sensitive: t.is_sensitive(),
379 }
380 }
381}
382
383impl<'a> From<&'a http::header::HeaderValue> for HeaderValue {
384 #[inline]
385 fn from(t: &'a http::header::HeaderValue) -> Self {
386 let inner = Bytes::copy_from_slice(t.as_ref());
387 HeaderValue {
388 inner,
389 is_sensitive: t.is_sensitive(),
390 }
391 }
392}
393
394impl<'a> TryFrom<&'a str> for HeaderValue {
395 type Error = InvalidHeaderValue;
396
397 #[inline]
398 fn try_from(t: &'a str) -> Result<Self, Self::Error> {
399 t.parse()
400 }
401}
402
403impl<'a> TryFrom<&'a String> for HeaderValue {
404 type Error = InvalidHeaderValue;
405 #[inline]
406 fn try_from(s: &'a String) -> Result<Self, Self::Error> {
407 Self::from_bytes(s.as_bytes())
408 }
409}
410
411impl<'a> TryFrom<&'a ByteString> for HeaderValue {
412 type Error = InvalidHeaderValue;
413 #[inline]
414 fn try_from(s: &'a ByteString) -> Result<Self, Self::Error> {
415 Self::from_shared(s.as_bytes().clone())
416 }
417}
418
419impl<'a> TryFrom<&'a [u8]> for HeaderValue {
420 type Error = InvalidHeaderValue;
421
422 #[inline]
423 fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
424 HeaderValue::from_bytes(t)
425 }
426}
427
428impl TryFrom<String> for HeaderValue {
429 type Error = InvalidHeaderValue;
430
431 #[inline]
432 fn try_from(s: String) -> Result<Self, Self::Error> {
433 HeaderValue::from_shared(s)
434 }
435}
436
437impl TryFrom<ByteString> for HeaderValue {
438 type Error = InvalidHeaderValue;
439 #[inline]
440 fn try_from(s: ByteString) -> Result<Self, Self::Error> {
441 Self::from_shared(s.into_bytes())
442 }
443}
444
445impl TryFrom<Vec<u8>> for HeaderValue {
446 type Error = InvalidHeaderValue;
447
448 #[inline]
449 fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
450 HeaderValue::from_shared(vec)
451 }
452}
453
454impl TryFrom<Bytes> for HeaderValue {
455 type Error = InvalidHeaderValue;
456
457 #[inline]
458 fn try_from(bytes: Bytes) -> Result<Self, Self::Error> {
459 HeaderValue::from_shared(bytes)
460 }
461}
462
463impl TryFrom<BytesMut> for HeaderValue {
464 type Error = InvalidHeaderValue;
465
466 #[inline]
467 fn try_from(bytes: BytesMut) -> Result<Self, Self::Error> {
468 HeaderValue::from_shared(bytes)
469 }
470}
471
472impl From<HeaderValue> for http::header::HeaderValue {
473 #[inline]
474 fn from(t: HeaderValue) -> Self {
475 let mut hdr = Self::from_bytes(t.as_bytes()).unwrap();
476 hdr.set_sensitive(t.is_sensitive());
477 hdr
478 }
479}
480
481impl<'a> From<&'a HeaderValue> for http::header::HeaderValue {
482 #[inline]
483 fn from(t: &'a HeaderValue) -> Self {
484 let mut hdr = Self::from_bytes(t.as_bytes()).unwrap();
485 hdr.set_sensitive(t.is_sensitive());
486 hdr
487 }
488}
489
490const fn is_visible_ascii(b: u8) -> bool {
491 b >= 32 && b < 127 || b == b'\t'
492}
493
494#[inline]
495fn is_valid(b: u8) -> bool {
496 b >= 32 && b != 127 || b == b'\t'
497}
498
499impl hash::Hash for HeaderValue {
503 #[inline]
504 fn hash<H: hash::Hasher>(&self, state: &mut H) {
505 self.inner.hash(state);
506 }
507}
508
509impl PartialEq for HeaderValue {
510 #[inline]
511 fn eq(&self, other: &HeaderValue) -> bool {
512 self.inner == other.inner
513 }
514}
515
516impl PartialOrd for HeaderValue {
517 #[inline]
518 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
519 Some(self.cmp(other))
520 }
521}
522
523impl Ord for HeaderValue {
524 #[inline]
525 fn cmp(&self, other: &Self) -> cmp::Ordering {
526 self.inner.cmp(&other.inner)
527 }
528}
529
530impl PartialEq<str> for HeaderValue {
531 #[inline]
532 fn eq(&self, other: &str) -> bool {
533 self.inner == other.as_bytes()
534 }
535}
536
537impl PartialEq<[u8]> for HeaderValue {
538 #[inline]
539 fn eq(&self, other: &[u8]) -> bool {
540 self.inner == other
541 }
542}
543
544impl PartialOrd<str> for HeaderValue {
545 #[inline]
546 fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
547 (*self.inner).partial_cmp(other.as_bytes())
548 }
549}
550
551impl PartialOrd<[u8]> for HeaderValue {
552 #[inline]
553 fn partial_cmp(&self, other: &[u8]) -> Option<cmp::Ordering> {
554 (*self.inner).partial_cmp(other)
555 }
556}
557
558impl PartialEq<HeaderValue> for str {
559 #[inline]
560 fn eq(&self, other: &HeaderValue) -> bool {
561 *other == *self
562 }
563}
564
565impl PartialEq<HeaderValue> for [u8] {
566 #[inline]
567 fn eq(&self, other: &HeaderValue) -> bool {
568 *other == *self
569 }
570}
571
572impl PartialOrd<HeaderValue> for str {
573 #[inline]
574 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
575 self.as_bytes().partial_cmp(other.as_bytes())
576 }
577}
578
579impl PartialOrd<HeaderValue> for [u8] {
580 #[inline]
581 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
582 self.partial_cmp(other.as_bytes())
583 }
584}
585
586impl PartialEq<String> for HeaderValue {
587 #[inline]
588 fn eq(&self, other: &String) -> bool {
589 *self == other[..]
590 }
591}
592
593impl PartialOrd<String> for HeaderValue {
594 #[inline]
595 fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
596 self.inner.partial_cmp(other.as_bytes())
597 }
598}
599
600impl PartialEq<HeaderValue> for String {
601 #[inline]
602 fn eq(&self, other: &HeaderValue) -> bool {
603 *other == *self
604 }
605}
606
607impl PartialOrd<HeaderValue> for String {
608 #[inline]
609 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
610 self.as_bytes().partial_cmp(other.as_bytes())
611 }
612}
613
614impl PartialEq<HeaderValue> for &HeaderValue {
615 #[inline]
616 fn eq(&self, other: &HeaderValue) -> bool {
617 **self == *other
618 }
619}
620
621impl PartialOrd<HeaderValue> for &HeaderValue {
622 #[inline]
623 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
624 (**self).partial_cmp(other)
625 }
626}
627
628impl<'a, T: ?Sized> PartialEq<&'a T> for HeaderValue
629where
630 HeaderValue: PartialEq<T>,
631{
632 #[inline]
633 fn eq(&self, other: &&'a T) -> bool {
634 *self == **other
635 }
636}
637
638impl<'a, T: ?Sized> PartialOrd<&'a T> for HeaderValue
639where
640 HeaderValue: PartialOrd<T>,
641{
642 #[inline]
643 fn partial_cmp(&self, other: &&'a T) -> Option<cmp::Ordering> {
644 self.partial_cmp(*other)
645 }
646}
647
648impl PartialEq<HeaderValue> for &str {
649 #[inline]
650 fn eq(&self, other: &HeaderValue) -> bool {
651 *other == *self
652 }
653}
654
655impl PartialOrd<HeaderValue> for &str {
656 #[inline]
657 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
658 self.as_bytes().partial_cmp(other.as_bytes())
659 }
660}
661
662macro_rules! from_integers {
663 ($($name:ident: $t:ident => $max_len:expr),*) => {$(
664 impl From<$t> for HeaderValue {
665 fn from(num: $t) -> HeaderValue {
666 let mut b = itoa::Buffer::new();
667 let inner = Bytes::copy_from_slice(b.format(num).as_ref());
668 HeaderValue {
669 inner,
670 is_sensitive: false,
671 }
672 }
673 }
674
675 #[test]
676 fn $name() {
677 let n: $t = 55;
678 let val = HeaderValue::from(n);
679 assert_eq!(val, &n.to_string());
680
681 let n = $t::MAX;
682 let val = HeaderValue::from(n);
683 assert_eq!(val, &n.to_string());
684 }
685 )*};
686}
687
688from_integers! {
689 from_u16: u16 => 5,
693 from_i16: i16 => 6,
694 from_u32: u32 => 10,
695 from_i32: i32 => 11,
696 from_u64: u64 => 20,
697 from_i64: i64 => 20
698}
699
700#[cfg(target_pointer_width = "16")]
701from_integers! {
702 from_usize: usize => 5,
703 from_isize: isize => 6
704}
705
706#[cfg(target_pointer_width = "32")]
707from_integers! {
708 from_usize: usize => 10,
709 from_isize: isize => 11
710}
711
712#[cfg(target_pointer_width = "64")]
713from_integers! {
714 from_usize: usize => 20,
715 from_isize: isize => 20
716}
717
718#[cfg(test)]
719mod tests {
720 use super::*;
721
722 #[test]
723 fn test_hash_ignores_sensitive() {
724 use std::hash::{BuildHasher, RandomState};
725
726 let state = RandomState::new();
727 let hdr = HeaderValue::from_static("secret");
728 let mut sensitive = hdr.clone();
729 sensitive.set_sensitive(true);
730 assert_eq!(hdr, sensitive);
731 assert_eq!(state.hash_one(&hdr), state.hash_one(&sensitive));
732 }
733
734 #[test]
735 #[allow(clippy::op_ref, clippy::cmp_owned)]
736 fn test_basics() {
737 assert!(HeaderValue::from_str("").unwrap().is_empty());
738
739 let hdr = HeaderValue::from_bytes(b"upgrade").unwrap();
740 let hdr2 = HeaderValue::from(&hdr);
741 assert_eq!(hdr, hdr2);
742
743 let hdr3 = HeaderValue::from_shared(Bytes::from_static(b"upgrade")).unwrap();
744 assert_eq!(hdr, hdr3);
745
746 let hdr = http::header::HeaderValue::from_bytes(b"upgrade").unwrap();
747 let hdr2 = HeaderValue::from(&hdr);
748 assert_eq!(hdr2.as_bytes(), b"upgrade");
749 assert_eq!(hdr2.as_shared(), &Bytes::from_static(b"upgrade"));
750 let hdr2 = HeaderValue::from(hdr);
751 assert_eq!(hdr2.as_bytes(), b"upgrade");
752
753 let hdr = HeaderValue::try_from("upgrade".to_string()).unwrap();
754 assert_eq!(hdr.as_bytes(), b"upgrade");
755 let hdr = HeaderValue::try_from(&("upgrade".to_string())).unwrap();
756 assert_eq!(hdr.as_bytes(), b"upgrade");
757 let hdr = HeaderValue::try_from(ByteString::from("upgrade")).unwrap();
758 assert_eq!(hdr.as_bytes(), b"upgrade");
759 let hdr = HeaderValue::try_from(&ByteString::from("upgrade")).unwrap();
760 assert_eq!(hdr.as_bytes(), b"upgrade");
761 let hdr2 = HeaderValue::try_from(&ByteString::from("upgrade2")).unwrap();
762
763 assert_eq!(hdr, hdr);
764 assert_eq!(hdr, &hdr);
765 assert_eq!(hdr, "upgrade");
766 assert_eq!(hdr, "upgrade".to_string());
767 assert_eq!(hdr, b"upgrade".as_ref());
768 assert_eq!("upgrade", hdr);
769 assert_eq!("upgrade", &hdr);
770 assert_eq!("upgrade".to_string(), hdr);
771 assert_eq!(b"upgrade".as_ref(), &hdr);
772 assert!(hdr < hdr2);
773 assert!(hdr < &hdr2);
774 assert!(&hdr < &hdr2);
775 assert!(hdr2 > hdr);
776 assert!(&hdr2 > hdr);
777 assert!(&hdr < "upgrade2");
778 assert!(hdr < "upgrade2");
779 assert!(hdr < "upgrade2".to_string());
780 assert!(hdr < &b"upgrade2"[..]);
781 assert!(hdr < b"upgrade2"[..]);
782 assert_ne!(hdr, &b"upgrade2"[..]);
783 assert_ne!(hdr, b"upgrade2"[..]);
784 assert!("upgrade2" > hdr);
785 assert!("upgrade2" > &hdr);
786 assert!("upgrade2".to_string() > hdr);
787 assert!(b"upgrade2"[..] > hdr);
788 assert_ne!("upgrade2"[..], hdr);
789 }
790
791 #[test]
792 fn test_try_from() {
793 HeaderValue::try_from(vec![127]).unwrap_err();
794
795 let hdr = HeaderValue::try_from(Bytes::from_static(b"upgrade")).unwrap();
796 assert_eq!(hdr, "upgrade");
797 HeaderValue::try_from(Bytes::from_static(b"\n")).unwrap_err();
798 let hdr = HeaderValue::try_from(BytesMut::copy_from_slice(b"upgrade")).unwrap();
799 assert_eq!(hdr, "upgrade");
800 HeaderValue::try_from(BytesMut::copy_from_slice(b"\n")).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, "Invalid HTTP header value");
835
836 let s = format!("{}", ToStrError { _priv: {} });
837 assert_eq!(s, "failed to convert header to a str");
838 }
839
840 #[test]
841 #[should_panic(expected = "invalid header value")]
842 fn from_static_invalid() {
843 let src: &'static str = std::hint::black_box("bad\nvalue");
844 let _ = HeaderValue::from_static(src);
845 }
846}