From ab1b252018254c52f01c8415ebe773c71df57fe1 Mon Sep 17 00:00:00 2001 From: Cedric Vandendriessche Date: Sun, 31 Mar 2024 21:18:27 +0200 Subject: [PATCH] Parse entire HTTP chunk size #396 (#528) * Parse entire HTTP chunk size #396 Continue parsing HTTP chunk size up until Int.MaxValue so the actual size of the chunk can be logged if it exceeds the configured max chunk size. * Add reference to Integer.MAX_VALUE in the error message * Fix formatting --- .../impl/engine/parsing/HttpMessageParser.scala | 9 ++++++--- .../impl/engine/parsing/RequestParserSpec.scala | 15 +++++++++++++-- 2 files changed, 19 insertions(+), 5 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 ea7edccaf..374f3c3e2 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 @@ -307,17 +307,20 @@ private[http] trait HttpMessageParser[Output >: MessageOutput <: ParserOutput] { s"HTTP chunk extension length exceeds configured limit of ${settings.maxChunkExtLength} characters") @tailrec def parseSize(cursor: Int, size: Long): StateResult = - if (size <= settings.maxChunkSize) { + if (size <= Int.MaxValue) { byteChar(input, cursor) match { case c if CharacterClasses.HEXDIG(c) => parseSize(cursor + 1, size * 16 + CharUtils.hexValue(c)) - case ';' if cursor > offset => parseChunkExtensions(size.toInt, cursor + 1)() + case c if size > settings.maxChunkSize => + failEntityStream( + s"HTTP chunk of $size bytes exceeds the configured limit of ${settings.maxChunkSize} bytes") + case ';' if cursor > offset => parseChunkExtensions(size.toInt, cursor + 1)() case '\r' if cursor > offset && byteChar(input, cursor + 1) == '\n' => parseChunkBody(size.toInt, "", cursor + 2) case '\n' if cursor > offset => parseChunkBody(size.toInt, "", cursor + 1) case c if CharacterClasses.WSP(c) => parseSize(cursor + 1, size) // illegal according to the spec but can happen, see issue #1812 case c => failEntityStream(s"Illegal character '${escape(c)}' in chunk start") } - } else failEntityStream(s"HTTP chunk size exceeds the configured limit of ${settings.maxChunkSize} bytes") + } else failEntityStream(s"HTTP chunk size exceeds Integer.MAX_VALUE (${Int.MaxValue}) bytes") try parseSize(offset, 0) catch { 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 7b6d8079c..fbbbec7ba 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 @@ -531,13 +531,24 @@ abstract class RequestParserSpec(mode: String, newLine: String) extends AnyFreeS closeAfterResponseCompletion shouldEqual Seq(false) } - "too-large chunk size" in new Test { + "too-large chunk size (> Int.MaxValue)" in new Test { Seq( start, """1a2b3c4d5e |""") should generalMultiParseTo( Right(baseRequest), - Left(EntityStreamError(ErrorInfo("HTTP chunk size exceeds the configured limit of 1048576 bytes")))) + Left(EntityStreamError(ErrorInfo("HTTP chunk size exceeds Integer.MAX_VALUE (2147483647) bytes")))) + closeAfterResponseCompletion shouldEqual Seq(false) + } + + "too-large chunk size" in new Test { + Seq( + start, + """400000 + |""") should generalMultiParseTo( + Right(baseRequest), + Left( + EntityStreamError(ErrorInfo("HTTP chunk of 4194304 bytes exceeds the configured limit of 1048576 bytes")))) closeAfterResponseCompletion shouldEqual Seq(false) }