Skip to content

Commit

Permalink
Further improvement
Browse files Browse the repository at this point in the history
  • Loading branch information
franz1981 committed Nov 17, 2024
1 parent 2c450ca commit 818cee8
Showing 1 changed file with 65 additions and 102 deletions.
167 changes: 65 additions & 102 deletions src/main/java/io/vertx/core/http/impl/HttpUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -873,46 +873,63 @@ public static void validateHeaderValue(CharSequence value) {
}
}

private static void validateAsciiHeaderValue(AsciiString asciiString) {
final int length = asciiString.length();
private static void validateAsciiHeaderValue(AsciiString value) {
final int length = value.length();
if (length == 0) {
return;
}
byte[] asciiChars = asciiString.array();
int off = asciiString.arrayOffset();
byte[] asciiChars = value.array();
int off = value.arrayOffset();
int end = off + length;
int state = NO_CR_LF_STATE;
// Start looping through each of the character

for (int index = off; index < end; index++) {
state = validateLatinValue(asciiString, state, asciiChars[index]);
}
if (state != 0) {
throw new IllegalArgumentException("a header value must not end with '\\r' or '\\n':" + asciiString);
int latinChar = asciiChars[index] & 0xFF;
if (latinChar == 0x7F) {
throw new IllegalArgumentException("a header value contains a prohibited character '127': " + value);
}
// non-printable chars are rare so let's make it a fall-back method, whilst still accepting HTAB
if (latinChar < 32 && latinChar != 0x09) {
validateSequenceHeaderValue(value, index - off);
break;
}
}
}

private static void validateStringHeaderValue(String seq) {

int state = NO_CR_LF_STATE;
// Start looping through each of the character
for (int index = 0; index < seq.length(); index++) {
state = validateValueChar(seq, state, seq.charAt(index));
private static void validateStringHeaderValue(String value) {
final int length = value.length();
if (length == 0) {
return;
}

if (state != 0) {
throw new IllegalArgumentException("a header value must not end with '\\r' or '\\n':" + seq);
for (int index = 0; index < length; index++) {
char latinChar = value.charAt(index);
if (latinChar == 0x7F) {
throw new IllegalArgumentException("a header value contains a prohibited character '127': " + value);
}
// non-printable chars are rare so let's make it a fall-back method, whilst still accepting HTAB
if (latinChar < 32 && latinChar != 0x09) {
validateSequenceHeaderValue(value, index);
break;
}
}
}

private static void validateSequenceHeaderValue(CharSequence seq) {
int state = 0;
// Start looping through each of the character
for (int index = 0; index < seq.length(); index++) {
state = validateValueChar(seq, state, seq.charAt(index));
private static void validateSequenceHeaderValue(CharSequence value) {
final int length = value.length();
if (length == 0) {
return;
}

if (state != 0) {
throw new IllegalArgumentException("a header value must not end with '\\r' or '\\n':" + seq);
for (int index = 0; index < length; index++) {
char latinChar = value.charAt(index);
if (latinChar == 0x7F) {
throw new IllegalArgumentException("a header value contains a prohibited character '127': " + value);
}
// non-printable chars are rare so let's make it a fall-back method, whilst still accepting HTAB
if (latinChar < 32 && latinChar != 0x09) {
validateSequenceHeaderValue(value, index);
break;
}
}
}

Expand All @@ -921,91 +938,37 @@ private static void validateSequenceHeaderValue(CharSequence seq) {
private static final int CR_STATE = 1;
private static final int LF_STATE = 2;

private static int validateLatinValue(CharSequence seq, int state, byte latinChar) {
/*
* State:
* 0: Previous character was neither CR nor LF
* 1: The previous character was CR
* 2: The previous character was LF
*/
if (latinChar == 0x7F) {
throw new IllegalArgumentException("a header value contains a prohibited character '127': " + seq);
}
if ((latinChar & HIGHEST_INVALID_VALUE_CHAR_MASK) == 0) {
// this is a rare scenario
validateNonPrintableCtrlLatin(seq, latinChar);
// this can include LF and CR as they are non-printable characters
if (state == NO_CR_LF_STATE) {
// Check the CRLF (HT | SP) pattern
switch (latinChar) {
case '\r':
return CR_STATE;
case '\n':
return LF_STATE;
}
return NO_CR_LF_STATE;
/**
* This method is taken as we need to validate the header value for the non-printable characters.
*/
private static void validateSequenceHeaderValue(CharSequence seq, int index) {
// we already expect the very-first character to be non-printable
int state = validateValueChar(seq, NO_CR_LF_STATE, seq.charAt(index));
for (int i = index + 1; i < seq.length(); i++) {
state = validateValueChar(seq, state, seq.charAt(i));
}
if (state != NO_CR_LF_STATE) {
throw new IllegalArgumentException("a header value must not end with '\\r' or '\\n':" + seq);
}
}
if (state != NO_CR_LF_STATE) {
// this is a rare scenario
return validateCrLfLatin(seq, state, latinChar);
} else {
return NO_CR_LF_STATE;
}
}

private static void validateNonPrintableCtrlLatin(CharSequence seq, byte latinChar) {
// The only characters allowed in the range 0x00-0x1F are : HTAB, LF and CR
switch (latinChar) {
case 0x09: // Horizontal tab - HTAB
case 0x0a: // Line feed - LF
case 0x0d: // Carriage return - CR
break;
default:
throw new IllegalArgumentException("a header value contains a prohibited character '" + Byte.toUnsignedInt(latinChar) + "': " + seq);
}
}


private static int validateCrLfLatin(CharSequence seq, int state, byte latinChar) {
switch (state) {
case CR_STATE:
if (latinChar == '\n') {
return LF_STATE;
}
throw new IllegalArgumentException("only '\\n' is allowed after '\\r': " + seq);
case LF_STATE:
switch (latinChar) {
case '\t':
case ' ':
// return to the normal state
return NO_CR_LF_STATE;
default:
throw new IllegalArgumentException("only ' ' and '\\t' are allowed after '\\n': " + seq);
}
default:
// this should never happen
throw new AssertionError();
}
}

private static int validateValueChar(CharSequence seq, int state, char character) {
private static int validateValueChar(CharSequence seq, int state, char ch) {
/*
* State:
* 0: Previous character was neither CR nor LF
* 1: The previous character was CR
* 2: The previous character was LF
*/
if (character == 0x7F) {
if (ch == 0x7F) {
throw new IllegalArgumentException("a header value contains a prohibited character '127': " + seq);
}
if ((character & HIGHEST_INVALID_VALUE_CHAR_MASK) == 0) {
if ((ch & HIGHEST_INVALID_VALUE_CHAR_MASK) == 0) {
// this is a rare scenario
validateNonPrintableCtrlChar(seq, character);
validateNonPrintableCtrlChar(seq, ch);
// this can include LF and CR as they are non-printable characters
if (state == NO_CR_LF_STATE) {
// Check the CRLF (HT | SP) pattern
switch (character) {
switch (ch) {
case '\r':
return CR_STATE;
case '\n':
Expand All @@ -1016,21 +979,21 @@ private static int validateValueChar(CharSequence seq, int state, char character
}
if (state != NO_CR_LF_STATE) {
// this is a rare scenario
return validateCrLfChar(seq, state, character);
return validateCrLfChar(seq, state, ch);
} else {
return NO_CR_LF_STATE;
}
}

private static int validateCrLfChar(CharSequence seq, int state, char character) {
private static int validateCrLfChar(CharSequence seq, int state, char ch) {
switch (state) {
case CR_STATE:
if (character == '\n') {
if (ch == '\n') {
return LF_STATE;
}
throw new IllegalArgumentException("only '\\n' is allowed after '\\r': " + seq);
case LF_STATE:
switch (character) {
switch (ch) {
case '\t':
case ' ':
// return to the normal state
Expand All @@ -1044,15 +1007,15 @@ private static int validateCrLfChar(CharSequence seq, int state, char character)
}
}

private static void validateNonPrintableCtrlChar(CharSequence seq, int character) {
private static void validateNonPrintableCtrlChar(CharSequence seq, int ch) {
// The only characters allowed in the range 0x00-0x1F are : HTAB, LF and CR
switch (character) {
switch (ch) {
case 0x09: // Horizontal tab - HTAB
case 0x0a: // Line feed - LF
case 0x0d: // Carriage return - CR
break;
default:
throw new IllegalArgumentException("a header value contains a prohibited character '" + (int) character + "': " + seq);
throw new IllegalArgumentException("a header value contains a prohibited character '" + (int) ch + "': " + seq);
}
}

Expand Down

0 comments on commit 818cee8

Please sign in to comment.