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
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 ea7edcc..374f3c3 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 @@
         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 7b6d807..fbbbec7 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 @@
         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)
       }