From 2c06be3d114d5e54b8051fb8fa2f9189223397dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 22 May 2024 19:12:02 +0200 Subject: [PATCH 01/13] add working test case --- .../src/test/scala/zio/http/FormSpec.scala | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala b/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala index 9e1b0117fc..7a9b926586 100644 --- a/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala @@ -17,16 +17,13 @@ package zio.http import java.nio.charset.StandardCharsets - import scala.annotation.nowarn - import zio._ import zio.test.Assertion._ import zio.test.TestAspect._ import zio.test._ - -import zio.stream.{ZStream, ZStreamAspect} - +import zio.stream.ZStream +import zio.stream.ZStreamAspect import zio.http.Header.ContentTransferEncoding import zio.http.forms.Fixtures._ @@ -86,7 +83,7 @@ object FormSpec extends ZIOHttpSpec { form2 == form, ) }, - test("encoding with custom paramaters [charset]") { + test("encoding with custom parameters [charset]") { val form = Form( FormField.textField( @@ -144,6 +141,38 @@ object FormSpec extends ZIOHttpSpec { } }, + test("decoding") { + val body = Chunk.fromArray( + s"""|--(((AaB03x)))${CR} + |Content-Disposition: form-data; name="hocon-data"${CR} + |Content-Type: text/plain${CR} + |${CR} + |foos: []${CR} + |--(((AaB03x)))${CR} + |Content-Disposition: form-data; name="json-data"${CR} + |Content-Type: text/plain${CR} + |${CR} + |{ "bars": [] }${CR} + |--(((AaB03x)))--${CRLF}""".stripMargin.getBytes(), + ) + + val form = Form( + FormField.textField("hocon-data", "foos: []", MediaType.text.`plain`), + FormField.textField("json-data", """{ "bars": [] }""", MediaType.text.`plain`), + ) + + val boundary = Boundary("(((AaB03x)))") + + val actualByteStream = form.multipartBytes(boundary) + + for { + form2 <- Form.fromMultipartBytes(body) + actualBytes <- actualByteStream.runCollect + } yield assertTrue( + actualBytes == body, + form2 == form, + ) + }, ) val multiFormStreamingSuite: Spec[Any, Throwable] = From f076ed54c9bbf1b1e990019f8563d9332d65a11d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 22 May 2024 19:12:17 +0200 Subject: [PATCH 02/13] break test --- zio-http/jvm/src/test/scala/zio/http/FormSpec.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala b/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala index 7a9b926586..a591a44865 100644 --- a/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala @@ -153,7 +153,7 @@ object FormSpec extends ZIOHttpSpec { |Content-Type: text/plain${CR} |${CR} |{ "bars": [] }${CR} - |--(((AaB03x)))--${CRLF}""".stripMargin.getBytes(), + |--(((AaB03x)))--${CR}""".stripMargin.getBytes(), ) val form = Form( From c9d7763e36febe58423e9d216692f73d5d517c41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 22 May 2024 20:56:34 +0200 Subject: [PATCH 03/13] wip --- .../scala/zio/http/internal/FormState.scala | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala index 2ca5c03b21..7222d94eaf 100644 --- a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala +++ b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala @@ -17,7 +17,6 @@ package zio.http.internal import zio._ - import zio.http.Boundary import zio.http.internal.FormAST._ @@ -29,8 +28,9 @@ private[http] object FormState { final class FormStateBuffer(boundary: Boundary) extends FormState { self => - private val tree0: ChunkBuilder[FormAST] = ChunkBuilder.make[FormAST]() - private val buffer: ChunkBuilder.Byte = new ChunkBuilder.Byte + private val tree0: ChunkBuilder[FormAST] = ChunkBuilder.make[FormAST]() + private val buffer: ChunkBuilder.Byte = new ChunkBuilder.Byte + private val boundaryMatches: Array[Boolean] = new Array[Boolean](boundary.closingBoundaryBytes.size) private var lastByte: OptionalByte = OptionalByte.None private var isBufferEmpty = true @@ -54,6 +54,17 @@ private[http] object FormState { val crlf = byte == '\n' && !lastByte.isEmpty && lastByte.get == '\r' + var boundaryDetected = false; + val addThis = if (!this.lastByte.isEmpty && buffer.knownSize == -1) 1 else 0 + val pos = buffer.knownSize + 1 + addThis + if (pos <= boundary.closingBoundaryBytes.size) { + boundaryMatches.update(pos, boundary.closingBoundaryBytes(pos) == byte) + } + + if (pos == boundary.closingBoundaryBytes.size) { + boundaryDetected = boundaryMatches.forall(_ == true) + } + def flush(ast: FormAST): Unit = { buffer.clear() lastByte = OptionalByte.None @@ -72,7 +83,7 @@ private[http] object FormState { case ClosingBoundary(_) => BoundaryClosed(tree) } - } else if (crlf && (phase eq Phase.Part2)) { + } else if ((crlf || boundaryDetected) && (phase eq Phase.Part2)) { val ast = FormAST.makePart2(buffer.result(), boundary) ast match { From 0645065973a0134b363d52a4f1268ac2fe97415e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 22 May 2024 22:35:45 +0200 Subject: [PATCH 04/13] fix bug --- .../src/test/scala/zio/http/FormSpec.scala | 18 ++++--------- .../scala/zio/http/internal/FormState.scala | 27 +++++++++++-------- 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala b/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala index a591a44865..87b54b29ab 100644 --- a/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala @@ -141,7 +141,7 @@ object FormSpec extends ZIOHttpSpec { } }, - test("decoding") { + test("decoding no lf at the end") { val body = Chunk.fromArray( s"""|--(((AaB03x)))${CR} |Content-Disposition: form-data; name="hocon-data"${CR} @@ -153,7 +153,7 @@ object FormSpec extends ZIOHttpSpec { |Content-Type: text/plain${CR} |${CR} |{ "bars": [] }${CR} - |--(((AaB03x)))--${CR}""".stripMargin.getBytes(), + |--(((AaB03x)))--""".stripMargin.getBytes(), ) val form = Form( @@ -161,17 +161,9 @@ object FormSpec extends ZIOHttpSpec { FormField.textField("json-data", """{ "bars": [] }""", MediaType.text.`plain`), ) - val boundary = Boundary("(((AaB03x)))") - - val actualByteStream = form.multipartBytes(boundary) - - for { - form2 <- Form.fromMultipartBytes(body) - actualBytes <- actualByteStream.runCollect - } yield assertTrue( - actualBytes == body, - form2 == form, - ) + Form + .fromMultipartBytes(body) + .map(form2 => assertTrue(form2 == form)) }, ) diff --git a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala index 7222d94eaf..e4a3f08940 100644 --- a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala +++ b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala @@ -29,8 +29,8 @@ private[http] object FormState { final class FormStateBuffer(boundary: Boundary) extends FormState { self => private val tree0: ChunkBuilder[FormAST] = ChunkBuilder.make[FormAST]() - private val buffer: ChunkBuilder.Byte = new ChunkBuilder.Byte - private val boundaryMatches: Array[Boolean] = new Array[Boolean](boundary.closingBoundaryBytes.size) + private var buffer: Chunk[Byte] = Chunk.empty + private var boundaryMatches: Array[Boolean] = new Array[Boolean](boundary.closingBoundaryBytes.size) private var lastByte: OptionalByte = OptionalByte.None private var isBufferEmpty = true @@ -55,18 +55,18 @@ private[http] object FormState { val crlf = byte == '\n' && !lastByte.isEmpty && lastByte.get == '\r' var boundaryDetected = false; - val addThis = if (!this.lastByte.isEmpty && buffer.knownSize == -1) 1 else 0 - val pos = buffer.knownSize + 1 + addThis - if (pos <= boundary.closingBoundaryBytes.size) { + val addThis = if (this.lastByte.isEmpty) 0 else 1 + val pos = buffer.knownSize + addThis + if (pos < boundary.closingBoundaryBytes.size) { boundaryMatches.update(pos, boundary.closingBoundaryBytes(pos) == byte) } - if (pos == boundary.closingBoundaryBytes.size) { + if (pos == boundary.closingBoundaryBytes.size - 1) { boundaryDetected = boundaryMatches.forall(_ == true) } def flush(ast: FormAST): Unit = { - buffer.clear() + buffer = Chunk.empty lastByte = OptionalByte.None if (crlf && isBufferEmpty && (phase eq Phase.Part1)) phase0 = Phase.Part2 if (ast.isContent && dropContents) () else addToTree(ast) @@ -74,7 +74,7 @@ private[http] object FormState { } if (crlf && (phase eq Phase.Part1)) { - val ast = FormAST.makePart1(buffer.result(), boundary) + val ast = FormAST.makePart1(buffer, boundary) ast match { case content: Content => flush(content); self @@ -84,7 +84,11 @@ private[http] object FormState { } } else if ((crlf || boundaryDetected) && (phase eq Phase.Part2)) { - val ast = FormAST.makePart2(buffer.result(), boundary) + if (boundaryDetected) { + buffer = buffer.appended("-".getBytes().head) + buffer = buffer.appended("-".getBytes().head) + } + val ast = FormAST.makePart2(buffer, boundary) ast match { case content: Content => @@ -97,7 +101,7 @@ private[http] object FormState { } else { if (!lastByte.isEmpty) { if (isBufferEmpty) isBufferEmpty = false - buffer += lastByte.get + buffer = buffer.appended(lastByte.get) } lastByte = OptionalByte.Some(byte) self @@ -112,7 +116,8 @@ private[http] object FormState { def reset(): Unit = { tree0.clear() - buffer.clear() + buffer = Chunk.empty + boundaryMatches = new Array[Boolean](boundary.closingBoundaryBytes.size) isBufferEmpty = true dropContents = false phase0 = Phase.Part1 From 2c8b2238e19977d1e4058a1047ad1cedcb98df61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Mon, 10 Jun 2024 12:08:31 +0200 Subject: [PATCH 05/13] make boundaryMatches val --- .../src/main/scala/zio/http/internal/FormState.scala | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala index e4a3f08940..5af82f6bf4 100644 --- a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala +++ b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala @@ -30,7 +30,7 @@ private[http] object FormState { private val tree0: ChunkBuilder[FormAST] = ChunkBuilder.make[FormAST]() private var buffer: Chunk[Byte] = Chunk.empty - private var boundaryMatches: Array[Boolean] = new Array[Boolean](boundary.closingBoundaryBytes.size) + private val boundaryMatches: Array[Boolean] = new Array[Boolean](boundary.closingBoundaryBytes.size) private var lastByte: OptionalByte = OptionalByte.None private var isBufferEmpty = true @@ -67,6 +67,7 @@ private[http] object FormState { def flush(ast: FormAST): Unit = { buffer = Chunk.empty + boundaryMatches.map(_ => false) lastByte = OptionalByte.None if (crlf && isBufferEmpty && (phase eq Phase.Part1)) phase0 = Phase.Part2 if (ast.isContent && dropContents) () else addToTree(ast) @@ -85,8 +86,8 @@ private[http] object FormState { } else if ((crlf || boundaryDetected) && (phase eq Phase.Part2)) { if (boundaryDetected) { - buffer = buffer.appended("-".getBytes().head) - buffer = buffer.appended("-".getBytes().head) + val hyphen = "-".getBytes().head + buffer = buffer.appended(hyphen).appended(hyphen) } val ast = FormAST.makePart2(buffer, boundary) @@ -117,7 +118,7 @@ private[http] object FormState { def reset(): Unit = { tree0.clear() buffer = Chunk.empty - boundaryMatches = new Array[Boolean](boundary.closingBoundaryBytes.size) + boundaryMatches.map(_ => false) isBufferEmpty = true dropContents = false phase0 = Phase.Part1 From 4fa5684242df41f198fb5f76d90d2ade2aafc818 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Mon, 10 Jun 2024 13:01:12 +0200 Subject: [PATCH 06/13] revert buffer to ChunkBuilder --- .../scala/zio/http/internal/FormState.scala | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala index 5af82f6bf4..30b986c558 100644 --- a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala +++ b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala @@ -29,7 +29,8 @@ private[http] object FormState { final class FormStateBuffer(boundary: Boundary) extends FormState { self => private val tree0: ChunkBuilder[FormAST] = ChunkBuilder.make[FormAST]() - private var buffer: Chunk[Byte] = Chunk.empty + private var buffer: ChunkBuilder[Byte] = new ChunkBuilder.Byte + private var bufferSize: Int = 0 private val boundaryMatches: Array[Boolean] = new Array[Boolean](boundary.closingBoundaryBytes.size) private var lastByte: OptionalByte = OptionalByte.None @@ -56,7 +57,7 @@ private[http] object FormState { var boundaryDetected = false; val addThis = if (this.lastByte.isEmpty) 0 else 1 - val pos = buffer.knownSize + addThis + val pos = bufferSize + addThis if (pos < boundary.closingBoundaryBytes.size) { boundaryMatches.update(pos, boundary.closingBoundaryBytes(pos) == byte) } @@ -66,7 +67,8 @@ private[http] object FormState { } def flush(ast: FormAST): Unit = { - buffer = Chunk.empty + buffer.clear() + bufferSize = 0 boundaryMatches.map(_ => false) lastByte = OptionalByte.None if (crlf && isBufferEmpty && (phase eq Phase.Part1)) phase0 = Phase.Part2 @@ -75,7 +77,7 @@ private[http] object FormState { } if (crlf && (phase eq Phase.Part1)) { - val ast = FormAST.makePart1(buffer, boundary) + val ast = FormAST.makePart1(buffer.result(), boundary) ast match { case content: Content => flush(content); self @@ -87,9 +89,10 @@ private[http] object FormState { } else if ((crlf || boundaryDetected) && (phase eq Phase.Part2)) { if (boundaryDetected) { val hyphen = "-".getBytes().head - buffer = buffer.appended(hyphen).appended(hyphen) + buffer += hyphen + buffer += hyphen } - val ast = FormAST.makePart2(buffer, boundary) + val ast = FormAST.makePart2(buffer.result(), boundary) ast match { case content: Content => @@ -102,7 +105,8 @@ private[http] object FormState { } else { if (!lastByte.isEmpty) { if (isBufferEmpty) isBufferEmpty = false - buffer = buffer.appended(lastByte.get) + buffer += lastByte.get + bufferSize = bufferSize + 1 } lastByte = OptionalByte.Some(byte) self @@ -117,7 +121,8 @@ private[http] object FormState { def reset(): Unit = { tree0.clear() - buffer = Chunk.empty + buffer.clear() + bufferSize = 0 boundaryMatches.map(_ => false) isBufferEmpty = true dropContents = false From 10f288e5fe32fd09bf7498b3dbed91df878106f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Mon, 10 Jun 2024 13:01:56 +0200 Subject: [PATCH 07/13] buffer is val --- .../shared/src/main/scala/zio/http/internal/FormState.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala index 30b986c558..c20c7a26dc 100644 --- a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala +++ b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala @@ -29,7 +29,7 @@ private[http] object FormState { final class FormStateBuffer(boundary: Boundary) extends FormState { self => private val tree0: ChunkBuilder[FormAST] = ChunkBuilder.make[FormAST]() - private var buffer: ChunkBuilder[Byte] = new ChunkBuilder.Byte + private val buffer: ChunkBuilder[Byte] = new ChunkBuilder.Byte private var bufferSize: Int = 0 private val boundaryMatches: Array[Boolean] = new Array[Boolean](boundary.closingBoundaryBytes.size) From 07edc59d07f041025c8399e7369e3f24d2b28cfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Mon, 10 Jun 2024 13:09:24 +0200 Subject: [PATCH 08/13] fmt --- .../src/main/scala/zio/http/internal/FormState.scala | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala index c20c7a26dc..897404be19 100644 --- a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala +++ b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala @@ -17,6 +17,7 @@ package zio.http.internal import zio._ + import zio.http.Boundary import zio.http.internal.FormAST._ @@ -56,13 +57,12 @@ private[http] object FormState { val crlf = byte == '\n' && !lastByte.isEmpty && lastByte.get == '\r' var boundaryDetected = false; - val addThis = if (this.lastByte.isEmpty) 0 else 1 - val pos = bufferSize + addThis - if (pos < boundary.closingBoundaryBytes.size) { - boundaryMatches.update(pos, boundary.closingBoundaryBytes(pos) == byte) + val posInLine = bufferSize + (if (this.lastByte.isEmpty) 0 else 1) + if (posInLine < boundary.closingBoundaryBytes.size) { + boundaryMatches.update(posInLine, boundary.closingBoundaryBytes(posInLine) == byte) } - if (pos == boundary.closingBoundaryBytes.size - 1) { + if (posInLine == boundary.closingBoundaryBytes.size - 1) { boundaryDetected = boundaryMatches.forall(_ == true) } From 995da2f28ed25d8246bec3df6aede415523b1ad9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Mon, 10 Jun 2024 13:11:42 +0200 Subject: [PATCH 09/13] fmt --- zio-http/jvm/src/test/scala/zio/http/FormSpec.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala b/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala index d91f018ffe..2fce5654f4 100644 --- a/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala +++ b/zio-http/jvm/src/test/scala/zio/http/FormSpec.scala @@ -17,13 +17,16 @@ package zio.http import java.nio.charset.StandardCharsets + import scala.annotation.nowarn + import zio._ import zio.test.Assertion._ import zio.test.TestAspect._ import zio.test._ -import zio.stream.ZStream -import zio.stream.ZStreamAspect + +import zio.stream.{ZStream, ZStreamAspect} + import zio.http.Header.ContentTransferEncoding import zio.http.forms.Fixtures._ From 106ba2ab90402ee52958dcc9aa437c855d8af4b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 12 Jun 2024 06:51:52 +0200 Subject: [PATCH 10/13] Update zio-http/shared/src/main/scala/zio/http/internal/FormState.scala Co-authored-by: kyri-petrou <67301607+kyri-petrou@users.noreply.github.com> --- .../shared/src/main/scala/zio/http/internal/FormState.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala index 897404be19..539324f126 100644 --- a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala +++ b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala @@ -106,7 +106,7 @@ private[http] object FormState { if (!lastByte.isEmpty) { if (isBufferEmpty) isBufferEmpty = false buffer += lastByte.get - bufferSize = bufferSize + 1 + bufferSize += 1 } lastByte = OptionalByte.Some(byte) self From b338c6e347ace6ea3cb0bd5d918e3ce4dcdfa16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 12 Jun 2024 06:56:05 +0200 Subject: [PATCH 11/13] Extract closingBoundaryBytesSize and reset boundaryMatches with a new Array[Boolean](closingBoundaryBytesSize) --- .../src/main/scala/zio/http/internal/FormState.scala | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala index 539324f126..dfd5672268 100644 --- a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala +++ b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala @@ -32,7 +32,8 @@ private[http] object FormState { private val tree0: ChunkBuilder[FormAST] = ChunkBuilder.make[FormAST]() private val buffer: ChunkBuilder[Byte] = new ChunkBuilder.Byte private var bufferSize: Int = 0 - private val boundaryMatches: Array[Boolean] = new Array[Boolean](boundary.closingBoundaryBytes.size) + private val closingBoundaryBytesSize = boundary.closingBoundaryBytes.size + private var boundaryMatches: Array[Boolean] = new Array[Boolean](closingBoundaryBytesSize) private var lastByte: OptionalByte = OptionalByte.None private var isBufferEmpty = true @@ -58,18 +59,18 @@ private[http] object FormState { var boundaryDetected = false; val posInLine = bufferSize + (if (this.lastByte.isEmpty) 0 else 1) - if (posInLine < boundary.closingBoundaryBytes.size) { + if (posInLine < closingBoundaryBytesSize) { boundaryMatches.update(posInLine, boundary.closingBoundaryBytes(posInLine) == byte) } - if (posInLine == boundary.closingBoundaryBytes.size - 1) { + if (posInLine == closingBoundaryBytesSize - 1) { boundaryDetected = boundaryMatches.forall(_ == true) } def flush(ast: FormAST): Unit = { buffer.clear() bufferSize = 0 - boundaryMatches.map(_ => false) + boundaryMatches = new Array[Boolean](closingBoundaryBytesSize) lastByte = OptionalByte.None if (crlf && isBufferEmpty && (phase eq Phase.Part1)) phase0 = Phase.Part2 if (ast.isContent && dropContents) () else addToTree(ast) @@ -123,7 +124,7 @@ private[http] object FormState { tree0.clear() buffer.clear() bufferSize = 0 - boundaryMatches.map(_ => false) + boundaryMatches = new Array[Boolean](closingBoundaryBytesSize) isBufferEmpty = true dropContents = false phase0 = Phase.Part1 From 736b5042e9736b75fbb38361e1712dda51007805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 12 Jun 2024 07:01:48 +0200 Subject: [PATCH 12/13] Update zio-http/shared/src/main/scala/zio/http/internal/FormState.scala Co-authored-by: kyri-petrou <67301607+kyri-petrou@users.noreply.github.com> --- .../shared/src/main/scala/zio/http/internal/FormState.scala | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala index dfd5672268..9ffdb25395 100644 --- a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala +++ b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala @@ -89,9 +89,8 @@ private[http] object FormState { } else if ((crlf || boundaryDetected) && (phase eq Phase.Part2)) { if (boundaryDetected) { - val hyphen = "-".getBytes().head - buffer += hyphen - buffer += hyphen + buffer += '-' + buffer += '-' } val ast = FormAST.makePart2(buffer.result(), boundary) From f1f33ba98abc12714d706d0f3a89496c62b082c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Kleinb=C3=B6lting?= Date: Wed, 12 Jun 2024 13:15:23 +0200 Subject: [PATCH 13/13] Replace Array[Boolean] with single boolean --- .../scala/zio/http/internal/FormState.scala | 24 +++++++------------ 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala index 9ffdb25395..e7b09752af 100644 --- a/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala +++ b/zio-http/shared/src/main/scala/zio/http/internal/FormState.scala @@ -29,11 +29,11 @@ private[http] object FormState { final class FormStateBuffer(boundary: Boundary) extends FormState { self => - private val tree0: ChunkBuilder[FormAST] = ChunkBuilder.make[FormAST]() - private val buffer: ChunkBuilder[Byte] = new ChunkBuilder.Byte - private var bufferSize: Int = 0 - private val closingBoundaryBytesSize = boundary.closingBoundaryBytes.size - private var boundaryMatches: Array[Boolean] = new Array[Boolean](closingBoundaryBytesSize) + private val tree0: ChunkBuilder[FormAST] = ChunkBuilder.make[FormAST]() + private val buffer: ChunkBuilder[Byte] = new ChunkBuilder.Byte + private var bufferSize: Int = 0 + private val closingBoundaryBytesSize = boundary.closingBoundaryBytes.size + private var boundaryMatches: Boolean = true private var lastByte: OptionalByte = OptionalByte.None private var isBufferEmpty = true @@ -57,20 +57,14 @@ private[http] object FormState { val crlf = byte == '\n' && !lastByte.isEmpty && lastByte.get == '\r' - var boundaryDetected = false; val posInLine = bufferSize + (if (this.lastByte.isEmpty) 0 else 1) - if (posInLine < closingBoundaryBytesSize) { - boundaryMatches.update(posInLine, boundary.closingBoundaryBytes(posInLine) == byte) - } - - if (posInLine == closingBoundaryBytesSize - 1) { - boundaryDetected = boundaryMatches.forall(_ == true) - } + boundaryMatches &&= posInLine < closingBoundaryBytesSize && boundary.closingBoundaryBytes(posInLine) == byte + val boundaryDetected = boundaryMatches && posInLine == closingBoundaryBytesSize - 1 def flush(ast: FormAST): Unit = { buffer.clear() bufferSize = 0 - boundaryMatches = new Array[Boolean](closingBoundaryBytesSize) + boundaryMatches = true lastByte = OptionalByte.None if (crlf && isBufferEmpty && (phase eq Phase.Part1)) phase0 = Phase.Part2 if (ast.isContent && dropContents) () else addToTree(ast) @@ -123,7 +117,7 @@ private[http] object FormState { tree0.clear() buffer.clear() bufferSize = 0 - boundaryMatches = new Array[Boolean](closingBoundaryBytesSize) + boundaryMatches = true isBufferEmpty = true dropContents = false phase0 = Phase.Part1