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 fn try_from_generic<T: AsRef<[u8]>, F: FnOnce(T) -> Bytes>(
194 src: T,
195 into: F,
196 ) -> Result<HeaderValue, InvalidHeaderValue> {
197 for &b in src.as_ref() {
198 if !is_valid(b) {
199 return Err(InvalidHeaderValue { _priv: () });
200 }
201 }
202 Ok(HeaderValue {
203 inner: into(src),
204 is_sensitive: false,
205 })
206 }
207
208 pub fn to_str(&self) -> Result<&str, ToStrError> {
222 let bytes = self.as_ref();
223
224 for &b in bytes {
225 if !is_visible_ascii(b) {
226 return Err(ToStrError { _priv: () });
227 }
228 }
229
230 unsafe { Ok(str::from_utf8_unchecked(bytes)) }
231 }
232
233 #[inline]
245 pub fn len(&self) -> usize {
246 self.as_ref().len()
247 }
248
249 #[inline]
262 pub fn is_empty(&self) -> bool {
263 self.len() == 0
264 }
265
266 #[inline]
276 pub fn as_bytes(&self) -> &[u8] {
277 self.as_ref()
278 }
279
280 #[inline]
291 pub fn as_shared(&self) -> &Bytes {
292 &self.inner
293 }
294
295 #[inline]
310 pub fn set_sensitive(&mut self, val: bool) {
311 self.is_sensitive = val;
312 }
313
314 #[inline]
341 pub fn is_sensitive(&self) -> bool {
342 self.is_sensitive
343 }
344}
345
346impl AsRef<[u8]> for HeaderValue {
347 #[inline]
348 fn as_ref(&self) -> &[u8] {
349 self.inner.as_ref()
350 }
351}
352
353impl fmt::Debug for HeaderValue {
354 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
355 if self.is_sensitive {
356 f.write_str("Sensitive")
357 } else {
358 f.write_str("\"")?;
359 let mut from = 0;
360 let bytes = self.as_bytes();
361 for (i, &b) in bytes.iter().enumerate() {
362 if !is_visible_ascii(b) || b == b'"' {
363 if from != i {
364 f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..i]) })?;
365 }
366 if b == b'"' {
367 f.write_str("\\\"")?;
368 } else {
369 write!(f, "\\x{b:x}")?;
370 }
371 from = i + 1;
372 }
373 }
374
375 f.write_str(unsafe { str::from_utf8_unchecked(&bytes[from..]) })?;
376 f.write_str("\"")
377 }
378 }
379}
380
381impl FromStr for HeaderValue {
382 type Err = InvalidHeaderValue;
383
384 #[inline]
385 fn from_str(s: &str) -> Result<HeaderValue, Self::Err> {
386 HeaderValue::from_str(s)
387 }
388}
389
390impl<'a> From<&'a HeaderValue> for HeaderValue {
391 #[inline]
392 fn from(t: &'a HeaderValue) -> Self {
393 t.clone()
394 }
395}
396
397impl From<http::header::HeaderValue> for HeaderValue {
398 #[inline]
399 fn from(t: http::header::HeaderValue) -> Self {
400 let inner = Bytes::copy_from_slice(t.as_ref());
401 HeaderValue {
402 inner,
403 is_sensitive: t.is_sensitive(),
404 }
405 }
406}
407
408impl<'a> From<&'a http::header::HeaderValue> for HeaderValue {
409 #[inline]
410 fn from(t: &'a http::header::HeaderValue) -> Self {
411 let inner = Bytes::copy_from_slice(t.as_ref());
412 HeaderValue {
413 inner,
414 is_sensitive: t.is_sensitive(),
415 }
416 }
417}
418
419impl<'a> TryFrom<&'a str> for HeaderValue {
420 type Error = InvalidHeaderValue;
421
422 #[inline]
423 fn try_from(t: &'a str) -> Result<Self, Self::Error> {
424 t.parse()
425 }
426}
427
428impl<'a> TryFrom<&'a String> for HeaderValue {
429 type Error = InvalidHeaderValue;
430 #[inline]
431 fn try_from(s: &'a String) -> Result<Self, Self::Error> {
432 Self::from_bytes(s.as_bytes())
433 }
434}
435
436impl<'a> TryFrom<&'a ByteString> for HeaderValue {
437 type Error = InvalidHeaderValue;
438 #[inline]
439 fn try_from(s: &'a ByteString) -> Result<Self, Self::Error> {
440 Self::from_shared(s.as_bytes().clone())
441 }
442}
443
444impl<'a> TryFrom<&'a [u8]> for HeaderValue {
445 type Error = InvalidHeaderValue;
446
447 #[inline]
448 fn try_from(t: &'a [u8]) -> Result<Self, Self::Error> {
449 HeaderValue::from_bytes(t)
450 }
451}
452
453impl TryFrom<String> for HeaderValue {
454 type Error = InvalidHeaderValue;
455
456 #[inline]
457 fn try_from(s: String) -> Result<Self, Self::Error> {
458 HeaderValue::from_shared(s)
459 }
460}
461
462impl TryFrom<ByteString> for HeaderValue {
463 type Error = InvalidHeaderValue;
464 #[inline]
465 fn try_from(s: ByteString) -> Result<Self, Self::Error> {
466 Self::from_shared(s.into_bytes())
467 }
468}
469
470impl TryFrom<Vec<u8>> for HeaderValue {
471 type Error = InvalidHeaderValue;
472
473 #[inline]
474 fn try_from(vec: Vec<u8>) -> Result<Self, Self::Error> {
475 HeaderValue::from_shared(vec)
476 }
477}
478
479impl From<HeaderValue> for http::header::HeaderValue {
480 #[inline]
481 fn from(t: HeaderValue) -> Self {
482 let mut hdr = Self::from_bytes(t.as_bytes()).unwrap();
483 hdr.set_sensitive(t.is_sensitive());
484 hdr
485 }
486}
487
488impl<'a> From<&'a HeaderValue> for http::header::HeaderValue {
489 #[inline]
490 fn from(t: &'a 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
497const fn is_visible_ascii(b: u8) -> bool {
498 b >= 32 && b < 127 || b == b'\t'
499}
500
501#[inline]
502fn is_valid(b: u8) -> bool {
503 b >= 32 && b != 127 || b == b'\t'
504}
505
506impl fmt::Debug for InvalidHeaderValue {
507 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508 f.debug_struct("InvalidHeaderValue")
509 .finish()
511 }
512}
513
514impl Error for ToStrError {}
515
516impl fmt::Display for ToStrError {
517 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
518 f.write_str("failed to convert header to a str")
519 }
520}
521
522impl PartialEq for HeaderValue {
525 #[inline]
526 fn eq(&self, other: &HeaderValue) -> bool {
527 self.inner == other.inner
528 }
529}
530
531impl PartialOrd for HeaderValue {
532 #[inline]
533 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
534 Some(self.cmp(other))
535 }
536}
537
538impl Ord for HeaderValue {
539 #[inline]
540 fn cmp(&self, other: &Self) -> cmp::Ordering {
541 self.inner.cmp(&other.inner)
542 }
543}
544
545impl PartialEq<str> for HeaderValue {
546 #[inline]
547 fn eq(&self, other: &str) -> bool {
548 self.inner == other.as_bytes()
549 }
550}
551
552impl PartialEq<[u8]> for HeaderValue {
553 #[inline]
554 fn eq(&self, other: &[u8]) -> bool {
555 self.inner == other
556 }
557}
558
559impl PartialOrd<str> for HeaderValue {
560 #[inline]
561 fn partial_cmp(&self, other: &str) -> Option<cmp::Ordering> {
562 (*self.inner).partial_cmp(other.as_bytes())
563 }
564}
565
566impl PartialOrd<[u8]> for HeaderValue {
567 #[inline]
568 fn partial_cmp(&self, other: &[u8]) -> Option<cmp::Ordering> {
569 (*self.inner).partial_cmp(other)
570 }
571}
572
573impl PartialEq<HeaderValue> for str {
574 #[inline]
575 fn eq(&self, other: &HeaderValue) -> bool {
576 *other == *self
577 }
578}
579
580impl PartialEq<HeaderValue> for [u8] {
581 #[inline]
582 fn eq(&self, other: &HeaderValue) -> bool {
583 *other == *self
584 }
585}
586
587impl PartialOrd<HeaderValue> for str {
588 #[inline]
589 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
590 self.as_bytes().partial_cmp(other.as_bytes())
591 }
592}
593
594impl PartialOrd<HeaderValue> for [u8] {
595 #[inline]
596 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
597 self.partial_cmp(other.as_bytes())
598 }
599}
600
601impl PartialEq<String> for HeaderValue {
602 #[inline]
603 fn eq(&self, other: &String) -> bool {
604 *self == other[..]
605 }
606}
607
608impl PartialOrd<String> for HeaderValue {
609 #[inline]
610 fn partial_cmp(&self, other: &String) -> Option<cmp::Ordering> {
611 self.inner.partial_cmp(other.as_bytes())
612 }
613}
614
615impl PartialEq<HeaderValue> for String {
616 #[inline]
617 fn eq(&self, other: &HeaderValue) -> bool {
618 *other == *self
619 }
620}
621
622impl PartialOrd<HeaderValue> for String {
623 #[inline]
624 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
625 self.as_bytes().partial_cmp(other.as_bytes())
626 }
627}
628
629impl PartialEq<HeaderValue> for &HeaderValue {
630 #[inline]
631 fn eq(&self, other: &HeaderValue) -> bool {
632 **self == *other
633 }
634}
635
636impl PartialOrd<HeaderValue> for &HeaderValue {
637 #[inline]
638 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
639 (**self).partial_cmp(other)
640 }
641}
642
643impl<'a, T: ?Sized> PartialEq<&'a T> for HeaderValue
644where
645 HeaderValue: PartialEq<T>,
646{
647 #[inline]
648 fn eq(&self, other: &&'a T) -> bool {
649 *self == **other
650 }
651}
652
653impl<'a, T: ?Sized> PartialOrd<&'a T> for HeaderValue
654where
655 HeaderValue: PartialOrd<T>,
656{
657 #[inline]
658 fn partial_cmp(&self, other: &&'a T) -> Option<cmp::Ordering> {
659 self.partial_cmp(*other)
660 }
661}
662
663impl PartialEq<HeaderValue> for &str {
664 #[inline]
665 fn eq(&self, other: &HeaderValue) -> bool {
666 *other == *self
667 }
668}
669
670impl PartialOrd<HeaderValue> for &str {
671 #[inline]
672 fn partial_cmp(&self, other: &HeaderValue) -> Option<cmp::Ordering> {
673 self.as_bytes().partial_cmp(other.as_bytes())
674 }
675}
676
677macro_rules! from_integers {
678 ($($name:ident: $t:ident => $max_len:expr),*) => {$(
679 impl From<$t> for HeaderValue {
680 fn from(num: $t) -> HeaderValue {
681 let mut b = itoa::Buffer::new();
682 let inner = Bytes::copy_from_slice(b.format(num).as_ref());
683 HeaderValue {
684 inner,
685 is_sensitive: false,
686 }
687 }
688 }
689
690 #[test]
691 fn $name() {
692 let n: $t = 55;
693 let val = HeaderValue::from(n);
694 assert_eq!(val, &n.to_string());
695
696 let n = $t::MAX;
697 let val = HeaderValue::from(n);
698 assert_eq!(val, &n.to_string());
699 }
700 )*};
701}
702
703from_integers! {
704 from_u16: u16 => 5,
708 from_i16: i16 => 6,
709 from_u32: u32 => 10,
710 from_i32: i32 => 11,
711 from_u64: u64 => 20,
712 from_i64: i64 => 20
713}
714
715#[cfg(target_pointer_width = "16")]
716from_integers! {
717 from_usize: usize => 5,
718 from_isize: isize => 6
719}
720
721#[cfg(target_pointer_width = "32")]
722from_integers! {
723 from_usize: usize => 10,
724 from_isize: isize => 11
725}
726
727#[cfg(target_pointer_width = "64")]
728from_integers! {
729 from_usize: usize => 20,
730 from_isize: isize => 20
731}
732
733#[cfg(test)]
734mod tests {
735 use super::*;
736
737 #[test]
738 #[allow(clippy::op_ref, clippy::cmp_owned)]
739 fn test_basics() {
740 assert!(HeaderValue::from_str("").unwrap().is_empty());
741
742 let hdr = HeaderValue::from_bytes(b"upgrade").unwrap();
743 let hdr2 = HeaderValue::from(&hdr);
744 assert_eq!(hdr, hdr2);
745
746 let hdr3 = HeaderValue::from_shared(Bytes::from_static(b"upgrade")).unwrap();
747 assert_eq!(hdr, hdr3);
748
749 let hdr = http::header::HeaderValue::from_bytes(b"upgrade").unwrap();
750 let hdr2 = HeaderValue::from(&hdr);
751 assert_eq!(hdr2.as_bytes(), b"upgrade");
752 assert_eq!(hdr2.as_shared(), &Bytes::from_static(b"upgrade"));
753 let hdr2 = HeaderValue::from(hdr);
754 assert_eq!(hdr2.as_bytes(), b"upgrade");
755
756 let hdr = HeaderValue::try_from("upgrade".to_string()).unwrap();
757 assert_eq!(hdr.as_bytes(), b"upgrade");
758 let hdr = HeaderValue::try_from(&("upgrade".to_string())).unwrap();
759 assert_eq!(hdr.as_bytes(), b"upgrade");
760 let hdr = HeaderValue::try_from(ByteString::from("upgrade")).unwrap();
761 assert_eq!(hdr.as_bytes(), b"upgrade");
762 let hdr = HeaderValue::try_from(&ByteString::from("upgrade")).unwrap();
763 assert_eq!(hdr.as_bytes(), b"upgrade");
764 let hdr2 = HeaderValue::try_from(&ByteString::from("upgrade2")).unwrap();
765
766 assert!(hdr == hdr);
767 assert!(hdr == &hdr);
768 assert!(hdr == "upgrade");
769 assert!(hdr == "upgrade".to_string());
770 assert!("upgrade" == hdr);
771 assert!("upgrade" == &hdr);
772 assert!("upgrade".to_string() == hdr);
773 assert!(hdr < hdr2);
774 assert!(hdr < &hdr2);
775 assert!(&hdr < &hdr2);
776 assert!(&hdr < "upgrade2");
777 assert!(hdr < "upgrade2");
778 assert!(hdr < "upgrade2".to_string());
779 assert!(hdr < &b"upgrade2"[..]);
780 assert!(hdr < b"upgrade2"[..]);
781 assert!(hdr != &b"upgrade2"[..]);
782 assert!(hdr != b"upgrade2"[..]);
783 assert!("upgrade2" > hdr);
784 assert!("upgrade2".to_string() > hdr);
785 assert!(b"upgrade2"[..] > hdr);
786 assert!("upgrade2"[..] != hdr);
787 }
788
789 #[test]
790 fn test_try_from() {
791 HeaderValue::try_from(vec![127]).unwrap_err();
792 }
793
794 #[test]
795 fn it_converts_using_try_from() {
796 assert!(HeaderValue::from_bytes(b"upgrade").is_ok());
797 }
798
799 #[test]
800 fn into_http_value() {
801 let hdr = HeaderValue::from_bytes(b"upgrade").unwrap();
802 let _ = http::header::HeaderValue::from(&hdr);
803 let _ = http::header::HeaderValue::from(hdr);
804 }
805
806 #[test]
807 fn test_fmt() {
808 let cases = &[
809 ("hello", "\"hello\""),
810 ("hello \"world\"", "\"hello \\\"world\\\"\""),
811 ("\u{7FFF}hello", "\"\\xe7\\xbf\\xbfhello\""),
812 ];
813
814 for &(value, expected) in cases {
815 let val = HeaderValue::from_bytes(value.as_bytes()).unwrap();
816 let actual = format!("{val:?}");
817 assert_eq!(expected, actual);
818 }
819
820 let mut sensitive = HeaderValue::from_static("password");
821 sensitive.set_sensitive(true);
822 assert_eq!("Sensitive", format!("{sensitive:?}"));
823
824 let s = format!("{:?}", InvalidHeaderValue { _priv: {} });
825 assert_eq!(s, "InvalidHeaderValue");
826
827 let s = format!("{}", ToStrError { _priv: {} });
828 assert_eq!(s, "failed to convert header to a str");
829 }
830}