diff --git a/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java b/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
index debdf57ed8a9..4621ab82e48b 100644
--- a/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
+++ b/java/org/apache/coyote/http11/filters/ChunkedInputFilter.java
@@ -199,13 +199,32 @@ public int available() {
available = readChunk.remaining();
}
- // Handle some edge cases
+ if (available > 2 && (parseState == ParseState.CHUNK_BODY_CRLF || parseState == ParseState.CHUNK_HEADER)) {
+ if (parseState == ParseState.CHUNK_BODY_CRLF) {
+ skipCRLF();
+ }
+ if (parseState == ParseState.CHUNK_HEADER) {
+ skipChunkHeader();
+ }
+ available = readChunk.remaining();
+ // If ending as TRAILER_FIELDS, then the next read will be EOF and available can be > 0
+ // If ending as CHUNK_HEADER then there's nothing left to read for now
+ // If ending as CHUNK_BODY_CRLF, then if there's more than two left, there will be data to read or leading to EOF
+ }
if (available == 1 && parseState == ParseState.CHUNK_BODY_CRLF) {
// Either just the CR or just the LF are left in the buffer. There is no data to read.
- available = 0;
+ if (!skipCRLF()) {
+ available = readChunk.remaining();
+ } else {
+ available = 0;
+ }
} else if (available == 2 && !crFound && parseState == ParseState.CHUNK_BODY_CRLF) {
// Just CRLF is left in the buffer. There is no data to read.
- available = 0;
+ if (!skipCRLF()) {
+ available = readChunk.remaining();
+ } else {
+ available = 0;
+ }
}
if (available == 0) {
@@ -391,6 +410,69 @@ private boolean parseChunkHeader() throws IOException {
}
+ private boolean skipChunkHeader() {
+
+ boolean eol = false;
+
+ while (!eol) {
+ if (readChunk == null || readChunk.position() >= readChunk.limit()) {
+ return false;
+ }
+
+ byte chr = readChunk.get(readChunk.position());
+ if (chr == Constants.CR || chr == Constants.LF) {
+ parsingExtension = false;
+ if (!skipCRLF()) {
+ return false;
+ }
+ eol = true;
+ } else if (chr == Constants.SEMI_COLON && !parsingExtension) {
+ // First semi-colon marks the start of the extension. Further
+ // semi-colons may appear to separate multiple chunk-extensions.
+ // These need to be processed as part of parsing the extensions.
+ parsingExtension = true;
+ extensionSize++;
+ } else if (!parsingExtension) {
+ int charValue = HexUtils.getDec(chr);
+ if (charValue != -1 && chunkSizeDigitsRead < 8) {
+ chunkSizeDigitsRead++;
+ remaining = (remaining << 4) | charValue;
+ } else {
+ // Isn't valid hex so this is an error condition
+ return false;
+ }
+ } else {
+ // Extension 'parsing'
+ // Note that the chunk-extension is neither parsed nor
+ // validated. Currently it is simply ignored.
+ extensionSize++;
+ if (maxExtensionSize > -1 && extensionSize > maxExtensionSize) {
+ return false;
+ }
+ }
+
+ // Parsing the CRLF increments pos
+ if (!eol) {
+ readChunk.position(readChunk.position() + 1);
+ }
+ }
+
+ if (chunkSizeDigitsRead == 0 || remaining < 0) {
+ return false;
+ } else {
+ chunkSizeDigitsRead = 0;
+ }
+
+ if (remaining == 0) {
+ parseState = ParseState.TRAILER_FIELDS;
+ } else {
+ parseState = ParseState.CHUNK_BODY;
+ }
+
+ return true;
+ }
+
+
private int parseChunkBody(ApplicationBufferHandler handler) throws IOException {
int result = 0;
@@ -461,6 +543,39 @@ private boolean parseCRLF() throws IOException {
}
+ private boolean skipCRLF() {
+
+ boolean eol = false;
+
+ while (!eol) {
+ if (readChunk == null || readChunk.position() >= readChunk.limit()) {
+ return false;
+ }
+
+ byte chr = readChunk.get(readChunk.position());
+ if (chr == Constants.CR) {
+ if (crFound) {
+ return false;
+ }
+ crFound = true;
+ } else if (chr == Constants.LF) {
+ if (!crFound) {
+ return false;
+ }
+ eol = true;
+ } else {
+ return false;
+ }
+
+ readChunk.position(readChunk.position() + 1);
+ }
+
+ crFound = false;
+ parseState = ParseState.CHUNK_HEADER;
+ return true;
+ }
+
+
/**
* Parse end chunk data.
*
diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml
index b32dc1563fd7..f51364d2acc5 100644
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -228,6 +228,10 @@
allowBackslash attributes determines how the decoded
URI is processed. (markt)
+
+ 69545: Improve CRLF skipping for the available
+ method of the ChunkedInputFilter
. (remm)
+