Skip to content

Commit e9327f2

Browse files
committed
Improve HTTP validation for inlining and specialized types
1 parent b8d0699 commit e9327f2

File tree

1 file changed

+98
-31
lines changed

1 file changed

+98
-31
lines changed

src/main/java/io/vertx/core/http/impl/HttpUtils.java

Lines changed: 98 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -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 = validateValueChar(asciiString, state, (char) Byte.toUnsignedInt(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,7 +901,23 @@ 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;
920+
880921

881922
private static int validateValueChar(CharSequence seq, int state, char character) {
882923
/*
@@ -885,45 +926,56 @@ private static int validateValueChar(CharSequence seq, int state, char character
885926
* 1: The previous character was CR
886927
* 2: The previous character was LF
887928
*/
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
929+
if ((character & HIGHEST_INVALID_VALUE_CHAR_MASK) == 0 || character == 0x7F) {
930+
validateNonPrintableCtrlChar(seq, character);
931+
}
932+
933+
// Check the CRLF (HT | SP) pattern
934+
if (state == NO_CR_LF_STATE) {
890935
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);
936+
case '\r':
937+
return CR_STATE;
938+
case '\n':
939+
return LF_STATE;
897940
}
941+
return NO_CR_LF_STATE;
942+
} else {
943+
return validateCrLf(seq, state, character);
898944
}
945+
}
899946

900-
// Check the CRLF (HT | SP) pattern
947+
private static int validateCrLf(CharSequence seq, int state, char character) {
901948
switch (state) {
902-
case 0:
903-
switch (character) {
904-
case '\r':
905-
return 1;
906-
case '\n':
907-
return 2;
908-
}
909-
break;
910-
case 1:
911-
switch (character) {
912-
case '\n':
913-
return 2;
914-
default:
915-
throw new IllegalArgumentException("only '\\n' is allowed after '\\r': " + seq);
949+
case CR_STATE:
950+
if (character == '\n') {
951+
return LF_STATE;
916952
}
917-
case 2:
953+
throw new IllegalArgumentException("only '\\n' is allowed after '\\r': " + seq);
954+
case LF_STATE:
918955
switch (character) {
919956
case '\t':
920957
case ' ':
921-
return 0;
958+
// return to the normal state
959+
return NO_CR_LF_STATE;
922960
default:
923961
throw new IllegalArgumentException("only ' ' and '\\t' are allowed after '\\n': " + seq);
924962
}
963+
default:
964+
// this should never happen
965+
throw new AssertionError();
966+
}
967+
}
968+
969+
private static void validateNonPrintableCtrlChar(CharSequence seq, char character) {
970+
// The only characters allowed in the range 0x00-0x1F are : HTAB, LF and CR
971+
switch (character) {
972+
case 0x09: // Horizontal tab - HTAB
973+
case 0x0a: // Line feed - LF
974+
case 0x0d: // Carriage return - CR
975+
break;
976+
default:
977+
throw new IllegalArgumentException("a header value contains a prohibited character '" + (int) character + "': " + seq);
925978
}
926-
return state;
927979
}
928980

929981
private static final boolean[] VALID_H_NAME_ASCII_CHARS;
@@ -959,13 +1011,15 @@ private static int validateValueChar(CharSequence seq, int state, char character
9591011
public static void validateHeaderName(CharSequence value) {
9601012
if (value instanceof AsciiString) {
9611013
// no need to check for ASCII-ness anymore
962-
validateHeaderName((AsciiString) value);
1014+
validateAsciiHeaderName((AsciiString) value);
1015+
} else if(value instanceof String) {
1016+
validateStringHeaderName((String) value);
9631017
} else {
964-
validateHeaderName0(value);
1018+
validateSequenceHeaderName(value);
9651019
}
9661020
}
9671021

968-
private static void validateHeaderName(AsciiString value) {
1022+
private static void validateAsciiHeaderName(AsciiString value) {
9691023
final int len = value.length();
9701024
final int off = value.arrayOffset();
9711025
final byte[] asciiChars = value.array();
@@ -981,7 +1035,20 @@ private static void validateHeaderName(AsciiString value) {
9811035
}
9821036
}
9831037

984-
private static void validateHeaderName0(CharSequence value) {
1038+
private static void validateStringHeaderName(String value) {
1039+
for (int i = 0; i < value.length(); i++) {
1040+
final char c = value.charAt(i);
1041+
// Check to see if the character is not an ASCII character, or invalid
1042+
if (c > 0x7f) {
1043+
throw new IllegalArgumentException("a header name cannot contain non-ASCII character: " + value);
1044+
}
1045+
if (!VALID_H_NAME_ASCII_CHARS[c & 0x7F]) {
1046+
throw new IllegalArgumentException("a header name cannot contain some prohibited characters, such as : " + value);
1047+
}
1048+
}
1049+
}
1050+
1051+
private static void validateSequenceHeaderName(CharSequence value) {
9851052
for (int i = 0; i < value.length(); i++) {
9861053
final char c = value.charAt(i);
9871054
// Check to see if the character is not an ASCII character, or invalid

0 commit comments

Comments
 (0)