diff --git a/src/main/java/io/vertx/core/http/impl/HttpUtils.java b/src/main/java/io/vertx/core/http/impl/HttpUtils.java index 923c3ab7e2b..de10da83aec 100644 --- a/src/main/java/io/vertx/core/http/impl/HttpUtils.java +++ b/src/main/java/io/vertx/core/http/impl/HttpUtils.java @@ -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; + } } } @@ -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': @@ -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 @@ -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); } }