diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpMessageParser.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpMessageParser.scala index 110564916..e7bcad180 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpMessageParser.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpMessageParser.scala @@ -281,16 +281,19 @@ private[http] trait HttpMessageParser[Output >: MessageOutput <: ParserOutput] { if (chunkCount >= settings.maxChunkCount) failEntityStream( s"HTTP chunk count exceeds the configured limit of ${settings.maxChunkCount} chunks") - val chunkBodyEnd = cursor + chunkSize - def result(terminatorLen: Int) = { - emit(EntityChunk(HttpEntity.Chunk(input.slice(cursor, chunkBodyEnd).compact, extension))) - Trampoline(_ => - parseChunk(input, chunkBodyEnd + terminatorLen, isLastMessage, totalBytesRead + chunkSize, chunkCount + 1)) - } - byteAt(input, chunkBodyEnd) match { - case CR_BYTE if byteAt(input, chunkBodyEnd + 1) == LF_BYTE => result(2) - case LF_BYTE => result(1) - case x => failEntityStream("Illegal chunk termination") + else { + val chunkBodyEnd = cursor + chunkSize + def result(terminatorLen: Int) = { + emit(EntityChunk(HttpEntity.Chunk(input.slice(cursor, chunkBodyEnd).compact, extension))) + Trampoline(_ => + parseChunk(input, chunkBodyEnd + terminatorLen, isLastMessage, totalBytesRead + chunkSize, + chunkCount + 1)) + } + byteAt(input, chunkBodyEnd) match { + case CR_BYTE if byteAt(input, chunkBodyEnd + 1) == LF_BYTE => result(2) + case LF_BYTE => result(1) + case x => failEntityStream("Illegal chunk termination") + } } } else parseTrailer(extension, cursor) diff --git a/http-core/src/test/scala/org/apache/pekko/http/impl/engine/parsing/RequestParserSpec.scala b/http-core/src/test/scala/org/apache/pekko/http/impl/engine/parsing/RequestParserSpec.scala index 53ff54e85..702871d45 100644 --- a/http-core/src/test/scala/org/apache/pekko/http/impl/engine/parsing/RequestParserSpec.scala +++ b/http-core/src/test/scala/org/apache/pekko/http/impl/engine/parsing/RequestParserSpec.scala @@ -337,6 +337,29 @@ abstract class RequestParserSpec(mode: String, newLine: String) extends AnyFreeS closeAfterResponseCompletion shouldEqual Seq(false) } + "stop parsing a request that has more chunks than the configured limit" in new Test { + override protected def parserSettings: ParserSettings = super.parserSettings.withMaxChunkCount(2) + + val input = prep(start + + """1 + |a + |1 + |b + |1 + |c + |0 + | + |""") + // collect the raw parser output: nothing must be emitted after the error, in particular no further chunk + val outputs = + Source.single(SessionBytes(TLSPlacebo.dummySession, ByteString(input))) + .via(newParser).runWith(Sink.seq).awaitResult(awaitAtMost) + + outputs.collect { case EntityChunk(chunk) => chunk.data.utf8String } shouldEqual Seq("a", "b") + outputs.last shouldEqual EntityStreamError( + ErrorInfo("HTTP chunk count exceeds the configured limit of 2 chunks")) + } + "don't overflow the stack for large buffers of chunks" in new Test { override val awaitAtMost = 10000.millis.dilated