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