@@ -863,7 +863,32 @@ public static void validateHeader(CharSequence name, Iterable<? extends CharSequ
863863 });
864864 }
865865
866- public static void validateHeaderValue (CharSequence seq ) {
866+ public static void validateHeaderValue (CharSequence value ) {
867+ if (value instanceof AsciiString ) {
868+ validateAsciiHeaderValue ((AsciiString ) value );
869+ } else if (value instanceof String ) {
870+ validateStringHeaderValue ((String ) value );
871+ } else {
872+ validateSequenceHeaderValue (value );
873+ }
874+ }
875+
876+ private static void validateAsciiHeaderValue (AsciiString asciiString ) {
877+ byte [] asciiChars = asciiString .array ();
878+ int off = asciiString .arrayOffset ();
879+ int len = asciiString .length ();
880+ int state = 0 ;
881+ // Start looping through each of the character
882+ for (int index = 0 ; index < len ; index ++) {
883+ state = validateLatinValue (asciiString , state , asciiChars [off + index ]);
884+ }
885+
886+ if (state != 0 ) {
887+ throw new IllegalArgumentException ("a header value must not end with '\\ r' or '\\ n':" + asciiString );
888+ }
889+ }
890+
891+ private static void validateStringHeaderValue (String seq ) {
867892
868893 int state = 0 ;
869894 // Start looping through each of the character
@@ -876,54 +901,140 @@ public static void validateHeaderValue(CharSequence seq) {
876901 }
877902 }
878903
904+ private static void validateSequenceHeaderValue (CharSequence seq ) {
905+ int state = 0 ;
906+ // Start looping through each of the character
907+ for (int index = 0 ; index < seq .length (); index ++) {
908+ state = validateValueChar (seq , state , seq .charAt (index ));
909+ }
910+
911+ if (state != 0 ) {
912+ throw new IllegalArgumentException ("a header value must not end with '\\ r' or '\\ n':" + seq );
913+ }
914+ }
915+
879916 private static final int HIGHEST_INVALID_VALUE_CHAR_MASK = ~0x1F ;
917+ private static final int NO_CR_LF_STATE = 0 ;
918+ private static final int CR_STATE = 1 ;
919+ private static final int LF_STATE = 2 ;
880920
881- private static int validateValueChar (CharSequence seq , int state , char character ) {
921+ private static int validateLatinValue (CharSequence seq , int state , byte latinChar ) {
882922 /*
883923 * State:
884924 * 0: Previous character was neither CR nor LF
885925 * 1: The previous character was CR
886926 * 2: The previous character was LF
887927 */
888- if ((character & HIGHEST_INVALID_VALUE_CHAR_MASK ) == 0 || character == 0x7F ) { // 0x7F is "DEL".
889- // The only characters allowed in the range 0x00-0x1F are : HTAB, LF and CR
890- switch (character ) {
891- case 0x09 : // Horizontal tab - HTAB
892- case 0x0a : // Line feed - LF
893- case 0x0d : // Carriage return - CR
894- break ;
895- default :
896- throw new IllegalArgumentException ("a header value contains a prohibited character '" + (int ) character + "': " + seq );
897- }
928+ if ((latinChar & HIGHEST_INVALID_VALUE_CHAR_MASK ) == 0 || latinChar == 0x7F ) {
929+ validateNonPrintableCtrlLatin (seq , latinChar );
898930 }
899931
900932 // Check the CRLF (HT | SP) pattern
933+ if (state == NO_CR_LF_STATE ) {
934+ switch (latinChar ) {
935+ case '\r' :
936+ return CR_STATE ;
937+ case '\n' :
938+ return LF_STATE ;
939+ }
940+ return NO_CR_LF_STATE ;
941+ } else {
942+ return validateCrLfLatin (seq , state , latinChar );
943+ }
944+ }
945+ private static void validateNonPrintableCtrlLatin (CharSequence seq , byte latinChar ) {
946+ // The only characters allowed in the range 0x00-0x1F are : HTAB, LF and CR
947+ switch (latinChar ) {
948+ case 0x09 : // Horizontal tab - HTAB
949+ case 0x0a : // Line feed - LF
950+ case 0x0d : // Carriage return - CR
951+ break ;
952+ default :
953+ throw new IllegalArgumentException ("a header value contains a prohibited character '" + Byte .toUnsignedInt (latinChar ) + "': " + seq );
954+ }
955+ }
956+
957+
958+ private static int validateCrLfLatin (CharSequence seq , int state , byte latinChar ) {
901959 switch (state ) {
902- case 0 :
903- switch (character ) {
904- case '\r' :
905- return 1 ;
906- case '\n' :
907- return 2 ;
960+ case CR_STATE :
961+ if (latinChar == '\n' ) {
962+ return LF_STATE ;
908963 }
909- break ;
910- case 1 :
911- switch (character ) {
912- case '\n' :
913- return 2 ;
964+ throw new IllegalArgumentException ("only '\\ n' is allowed after '\\ r': " + seq );
965+ case LF_STATE :
966+ switch (latinChar ) {
967+ case '\t' :
968+ case ' ' :
969+ // return to the normal state
970+ return NO_CR_LF_STATE ;
914971 default :
915- throw new IllegalArgumentException ("only '\\ n' is allowed after '\\ r ': " + seq );
972+ throw new IllegalArgumentException ("only ' ' and ' \\ t' are allowed after '\\ n ': " + seq );
916973 }
917- case 2 :
974+ default :
975+ // this should never happen
976+ throw new AssertionError ();
977+ }
978+ }
979+
980+
981+ private static int validateValueChar (CharSequence seq , int state , char character ) {
982+ /*
983+ * State:
984+ * 0: Previous character was neither CR nor LF
985+ * 1: The previous character was CR
986+ * 2: The previous character was LF
987+ */
988+ if ((character & HIGHEST_INVALID_VALUE_CHAR_MASK ) == 0 || character == 0x7F ) {
989+ validateNonPrintableCtrlChar (seq , character );
990+ }
991+
992+ // Check the CRLF (HT | SP) pattern
993+ if (state == NO_CR_LF_STATE ) {
994+ switch (character ) {
995+ case '\r' :
996+ return CR_STATE ;
997+ case '\n' :
998+ return LF_STATE ;
999+ }
1000+ return NO_CR_LF_STATE ;
1001+ } else {
1002+ return validateCrLfChar (seq , state , character );
1003+ }
1004+ }
1005+
1006+ private static int validateCrLfChar (CharSequence seq , int state , char character ) {
1007+ switch (state ) {
1008+ case CR_STATE :
1009+ if (character == '\n' ) {
1010+ return LF_STATE ;
1011+ }
1012+ throw new IllegalArgumentException ("only '\\ n' is allowed after '\\ r': " + seq );
1013+ case LF_STATE :
9181014 switch (character ) {
9191015 case '\t' :
9201016 case ' ' :
921- return 0 ;
1017+ // return to the normal state
1018+ return NO_CR_LF_STATE ;
9221019 default :
9231020 throw new IllegalArgumentException ("only ' ' and '\\ t' are allowed after '\\ n': " + seq );
9241021 }
1022+ default :
1023+ // this should never happen
1024+ throw new AssertionError ();
1025+ }
1026+ }
1027+
1028+ private static void validateNonPrintableCtrlChar (CharSequence seq , char character ) {
1029+ // The only characters allowed in the range 0x00-0x1F are : HTAB, LF and CR
1030+ switch (character ) {
1031+ case 0x09 : // Horizontal tab - HTAB
1032+ case 0x0a : // Line feed - LF
1033+ case 0x0d : // Carriage return - CR
1034+ break ;
1035+ default :
1036+ throw new IllegalArgumentException ("a header value contains a prohibited character '" + (int ) character + "': " + seq );
9251037 }
926- return state ;
9271038 }
9281039
9291040 private static final boolean [] VALID_H_NAME_ASCII_CHARS ;
@@ -959,13 +1070,15 @@ private static int validateValueChar(CharSequence seq, int state, char character
9591070 public static void validateHeaderName (CharSequence value ) {
9601071 if (value instanceof AsciiString ) {
9611072 // no need to check for ASCII-ness anymore
962- validateHeaderName ((AsciiString ) value );
1073+ validateAsciiHeaderName ((AsciiString ) value );
1074+ } else if (value instanceof String ) {
1075+ validateStringHeaderName ((String ) value );
9631076 } else {
964- validateHeaderName0 (value );
1077+ validateSequenceHeaderName (value );
9651078 }
9661079 }
9671080
968- private static void validateHeaderName (AsciiString value ) {
1081+ private static void validateAsciiHeaderName (AsciiString value ) {
9691082 final int len = value .length ();
9701083 final int off = value .arrayOffset ();
9711084 final byte [] asciiChars = value .array ();
@@ -981,7 +1094,20 @@ private static void validateHeaderName(AsciiString value) {
9811094 }
9821095 }
9831096
984- private static void validateHeaderName0 (CharSequence value ) {
1097+ private static void validateStringHeaderName (String value ) {
1098+ for (int i = 0 ; i < value .length (); i ++) {
1099+ final char c = value .charAt (i );
1100+ // Check to see if the character is not an ASCII character, or invalid
1101+ if (c > 0x7f ) {
1102+ throw new IllegalArgumentException ("a header name cannot contain non-ASCII character: " + value );
1103+ }
1104+ if (!VALID_H_NAME_ASCII_CHARS [c & 0x7F ]) {
1105+ throw new IllegalArgumentException ("a header name cannot contain some prohibited characters, such as : " + value );
1106+ }
1107+ }
1108+ }
1109+
1110+ private static void validateSequenceHeaderName (CharSequence value ) {
9851111 for (int i = 0 ; i < value .length (); i ++) {
9861112 final char c = value .charAt (i );
9871113 // Check to see if the character is not an ASCII character, or invalid
0 commit comments