@@ -604,67 +604,181 @@ public static void validateHeader(CharSequence name, Iterable<? extends CharSequ
604
604
});
605
605
}
606
606
607
- public static void validateHeaderValue (CharSequence seq ) {
607
+ public static void validateHeaderValue (CharSequence value ) {
608
+ if (value instanceof AsciiString ) {
609
+ validateAsciiHeaderValue ((AsciiString ) value );
610
+ } else if (value instanceof String ) {
611
+ validateStringHeaderValue ((String ) value );
612
+ } else {
613
+ validateSequenceHeaderValue (value );
614
+ }
615
+ }
616
+
617
+ private static void validateAsciiHeaderValue (AsciiString value ) {
618
+ final int length = value .length ();
619
+ if (length == 0 ) {
620
+ return ;
621
+ }
622
+ byte [] asciiChars = value .array ();
623
+ int off = value .arrayOffset ();
624
+ if (off == 0 && length == asciiChars .length ) {
625
+ for (int index = 0 ; index < asciiChars .length ; index ++) {
626
+ int latinChar = asciiChars [index ] & 0xFF ;
627
+ if (latinChar == 0x7F ) {
628
+ throw new IllegalArgumentException ("a header value contains a prohibited character '127': " + value );
629
+ }
630
+ // non-printable chars are rare so let's make it a fall-back method, whilst still accepting HTAB
631
+ if (latinChar < 32 && latinChar != 0x09 ) {
632
+ validateSequenceHeaderValue (value , index - off );
633
+ break ;
634
+ }
635
+ }
636
+ } else {
637
+ validateAsciiRangeHeaderValue (value , off , length , asciiChars );
638
+ }
639
+ }
640
+
641
+ /**
642
+ * This method is the slow-path generic version of {@link #validateAsciiHeaderValue(AsciiString)} which
643
+ * is optimized for {@link AsciiString} instances which are backed by a 0-offset full-blown byte array.
644
+ */
645
+ private static void validateAsciiRangeHeaderValue (AsciiString value , int off , int length , byte [] asciiChars ) {
646
+ int end = off + length ;
647
+ for (int index = off ; index < end ; index ++) {
648
+ int latinChar = asciiChars [index ] & 0xFF ;
649
+ if (latinChar == 0x7F ) {
650
+ throw new IllegalArgumentException ("a header value contains a prohibited character '127': " + value );
651
+ }
652
+ // non-printable chars are rare so let's make it a fall-back method, whilst still accepting HTAB
653
+ if (latinChar < 32 && latinChar != 0x09 ) {
654
+ validateSequenceHeaderValue (value , index - off );
655
+ break ;
656
+ }
657
+ }
658
+ }
608
659
609
- int state = 0 ;
610
- // Start looping through each of the character
611
- for ( int index = 0 ; index < seq . length (); index ++ ) {
612
- state = validateValueChar ( seq , state , seq . charAt ( index )) ;
660
+ private static void validateStringHeaderValue ( String value ) {
661
+ final int length = value . length ();
662
+ if ( length == 0 ) {
663
+ return ;
613
664
}
614
665
615
- if (state != 0 ) {
616
- throw new IllegalArgumentException ("a header value must not end with '\\ r' or '\\ n':" + seq );
666
+ for (int index = 0 ; index < length ; index ++) {
667
+ char latinChar = value .charAt (index );
668
+ if (latinChar == 0x7F ) {
669
+ throw new IllegalArgumentException ("a header value contains a prohibited character '127': " + value );
670
+ }
671
+ // non-printable chars are rare so let's make it a fall-back method, whilst still accepting HTAB
672
+ if (latinChar < 32 && latinChar != 0x09 ) {
673
+ validateSequenceHeaderValue (value , index );
674
+ break ;
675
+ }
676
+ }
677
+ }
678
+
679
+ private static void validateSequenceHeaderValue (CharSequence value ) {
680
+ final int length = value .length ();
681
+ if (length == 0 ) {
682
+ return ;
683
+ }
684
+
685
+ for (int index = 0 ; index < length ; index ++) {
686
+ char latinChar = value .charAt (index );
687
+ if (latinChar == 0x7F ) {
688
+ throw new IllegalArgumentException ("a header value contains a prohibited character '127': " + value );
689
+ }
690
+ // non-printable chars are rare so let's make it a fall-back method, whilst still accepting HTAB
691
+ if (latinChar < 32 && latinChar != 0x09 ) {
692
+ validateSequenceHeaderValue (value , index );
693
+ break ;
694
+ }
617
695
}
618
696
}
619
697
620
698
private static final int HIGHEST_INVALID_VALUE_CHAR_MASK = ~0x1F ;
699
+ private static final int NO_CR_LF_STATE = 0 ;
700
+ private static final int CR_STATE = 1 ;
701
+ private static final int LF_STATE = 2 ;
702
+
703
+ /**
704
+ * This method is taken as we need to validate the header value for the non-printable characters.
705
+ */
706
+ private static void validateSequenceHeaderValue (CharSequence seq , int index ) {
707
+ // we already expect the very-first character to be non-printable
708
+ int state = validateValueChar (seq , NO_CR_LF_STATE , seq .charAt (index ));
709
+ for (int i = index + 1 ; i < seq .length (); i ++) {
710
+ state = validateValueChar (seq , state , seq .charAt (i ));
711
+ }
712
+ if (state != NO_CR_LF_STATE ) {
713
+ throw new IllegalArgumentException ("a header value must not end with '\\ r' or '\\ n':" + seq );
714
+ }
715
+ }
621
716
622
- private static int validateValueChar (CharSequence seq , int state , char character ) {
717
+ private static int validateValueChar (CharSequence seq , int state , char ch ) {
623
718
/*
624
719
* State:
625
720
* 0: Previous character was neither CR nor LF
626
721
* 1: The previous character was CR
627
722
* 2: The previous character was LF
628
723
*/
629
- if ((character & HIGHEST_INVALID_VALUE_CHAR_MASK ) == 0 || character == 0x7F ) { // 0x7F is "DEL".
630
- // The only characters allowed in the range 0x00-0x1F are : HTAB, LF and CR
631
- switch (character ) {
632
- case 0x09 : // Horizontal tab - HTAB
633
- case 0x0a : // Line feed - LF
634
- case 0x0d : // Carriage return - CR
635
- break ;
636
- default :
637
- throw new IllegalArgumentException ("a header value contains a prohibited character '" + (int ) character + "': " + seq );
724
+ if (ch == 0x7F ) {
725
+ throw new IllegalArgumentException ("a header value contains a prohibited character '127': " + seq );
726
+ }
727
+ if ((ch & HIGHEST_INVALID_VALUE_CHAR_MASK ) == 0 ) {
728
+ // this is a rare scenario
729
+ validateNonPrintableCtrlChar (seq , ch );
730
+ // this can include LF and CR as they are non-printable characters
731
+ if (state == NO_CR_LF_STATE ) {
732
+ // Check the CRLF (HT | SP) pattern
733
+ switch (ch ) {
734
+ case '\r' :
735
+ return CR_STATE ;
736
+ case '\n' :
737
+ return LF_STATE ;
738
+ }
739
+ return NO_CR_LF_STATE ;
638
740
}
639
741
}
742
+ if (state != NO_CR_LF_STATE ) {
743
+ // this is a rare scenario
744
+ return validateCrLfChar (seq , state , ch );
745
+ } else {
746
+ return NO_CR_LF_STATE ;
747
+ }
748
+ }
640
749
641
- // Check the CRLF (HT | SP) pattern
750
+ private static int validateCrLfChar ( CharSequence seq , int state , char ch ) {
642
751
switch (state ) {
643
- case 0 :
644
- switch (character ) {
645
- case '\r' :
646
- return 1 ;
647
- case '\n' :
648
- return 2 ;
649
- }
650
- break ;
651
- case 1 :
652
- switch (character ) {
653
- case '\n' :
654
- return 2 ;
655
- default :
656
- throw new IllegalArgumentException ("only '\\ n' is allowed after '\\ r': " + seq );
752
+ case CR_STATE :
753
+ if (ch == '\n' ) {
754
+ return LF_STATE ;
657
755
}
658
- case 2 :
659
- switch (character ) {
756
+ throw new IllegalArgumentException ("only '\\ n' is allowed after '\\ r': " + seq );
757
+ case LF_STATE :
758
+ switch (ch ) {
660
759
case '\t' :
661
760
case ' ' :
662
- return 0 ;
761
+ // return to the normal state
762
+ return NO_CR_LF_STATE ;
663
763
default :
664
764
throw new IllegalArgumentException ("only ' ' and '\\ t' are allowed after '\\ n': " + seq );
665
765
}
766
+ default :
767
+ // this should never happen
768
+ throw new AssertionError ();
769
+ }
770
+ }
771
+
772
+ private static void validateNonPrintableCtrlChar (CharSequence seq , int ch ) {
773
+ // The only characters allowed in the range 0x00-0x1F are : HTAB, LF and CR
774
+ switch (ch ) {
775
+ case 0x09 : // Horizontal tab - HTAB
776
+ case 0x0a : // Line feed - LF
777
+ case 0x0d : // Carriage return - CR
778
+ break ;
779
+ default :
780
+ throw new IllegalArgumentException ("a header value contains a prohibited character '" + (int ) ch + "': " + seq );
666
781
}
667
- return state ;
668
782
}
669
783
670
784
private static final boolean [] VALID_H_NAME_ASCII_CHARS ;
@@ -700,13 +814,15 @@ private static int validateValueChar(CharSequence seq, int state, char character
700
814
public static void validateHeaderName (CharSequence value ) {
701
815
if (value instanceof AsciiString ) {
702
816
// no need to check for ASCII-ness anymore
703
- validateHeaderName ((AsciiString ) value );
817
+ validateAsciiHeaderName ((AsciiString ) value );
818
+ } else if (value instanceof String ) {
819
+ validateStringHeaderName ((String ) value );
704
820
} else {
705
- validateHeaderName0 (value );
821
+ validateSequenceHeaderName (value );
706
822
}
707
823
}
708
824
709
- private static void validateHeaderName (AsciiString value ) {
825
+ private static void validateAsciiHeaderName (AsciiString value ) {
710
826
final int len = value .length ();
711
827
final int off = value .arrayOffset ();
712
828
final byte [] asciiChars = value .array ();
@@ -722,7 +838,20 @@ private static void validateHeaderName(AsciiString value) {
722
838
}
723
839
}
724
840
725
- private static void validateHeaderName0 (CharSequence value ) {
841
+ private static void validateStringHeaderName (String value ) {
842
+ for (int i = 0 ; i < value .length (); i ++) {
843
+ final char c = value .charAt (i );
844
+ // Check to see if the character is not an ASCII character, or invalid
845
+ if (c > 0x7f ) {
846
+ throw new IllegalArgumentException ("a header name cannot contain non-ASCII character: " + value );
847
+ }
848
+ if (!VALID_H_NAME_ASCII_CHARS [c & 0x7F ]) {
849
+ throw new IllegalArgumentException ("a header name cannot contain some prohibited characters, such as : " + value );
850
+ }
851
+ }
852
+ }
853
+
854
+ private static void validateSequenceHeaderName (CharSequence value ) {
726
855
for (int i = 0 ; i < value .length (); i ++) {
727
856
final char c = value .charAt (i );
728
857
// Check to see if the character is not an ASCII character, or invalid
0 commit comments