This is an automated email from the ASF dual-hosted git repository.

pjfanning pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko-http.git


The following commit(s) were added to refs/heads/main by this push:
     new b67759ca1 stop parsing chunks once the chunk count limit is reached 
(#1220)
b67759ca1 is described below

commit b67759ca1229a1d7e848345f6f8aca4b5f0bd4fc
Author: PJ Fanning <[email protected]>
AuthorDate: Mon Aug 31 22:26:35 2026 +0100

    stop parsing chunks once the chunk count limit is reached (#1220)
    
    Motivation:
    The `max-chunk-count` check in `HttpMessageParser.parseChunkBody` has no
    `else` branch, so the `StateResult` of `failEntityStream` is discarded and
    parsing continues after the error has been emitted: the current chunk is
    emitted, the parser trampolines into the next one, and every following
    chunk in the buffer emits another error. The parser therefore keeps
    producing output after it has set itself to terminated. A consumer that
    stops at the first error does not notice, which is why the behaviour has
    gone unseen; the parser still does the work.
    
    Modification:
    Put the rest of `parseChunkBody` into the `else` branch of the check, the
    way the other limits in this parser are written.
    
    Result:
    Nothing is emitted after the chunk count error, and the parser stops
    parsing the rest of the body.
    
    Tests:
    - sbt "http-core/testOnly 
org.apache.pekko.http.impl.engine.parsing.RequestParserCRLFSpec 
org.apache.pekko.http.impl.engine.parsing.RequestParserLFSpec" - pass, 1 new 
test that collects the raw parser output; without the change it sees the chunks 
"a", "b", "c", "" instead of "a", "b"
    - sbt http-core/test - pass
    - sbt http-core/mimaReportBinaryIssues - pass
    - sbt http-core/scalafmt http-core/Test/scalafmt - clean
    
    References:
    None - makes the max-chunk-count limit stop parsing
---
 .../impl/engine/parsing/HttpMessageParser.scala    | 23 ++++++++++++----------
 .../impl/engine/parsing/RequestParserSpec.scala    | 23 ++++++++++++++++++++++
 2 files changed, 36 insertions(+), 10 deletions(-)

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 d7e903e03..48d011219 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
 


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to