=str #16927 preserve header order in body parts

This commit is contained in:
Konrad Malawski 2015-11-06 16:14:34 +01:00
parent e50a5ee6fc
commit f908a6a9dc
2 changed files with 18 additions and 10 deletions

View file

@ -14,6 +14,8 @@ import akka.http.scaladsl.model._
import akka.http.impl.util._
import headers._
import scala.collection.mutable.ListBuffer
/**
* INTERNAL API
*
@ -114,7 +116,7 @@ private[http] final class BodyPartParser(defaultContentType: ContentType,
case NotEnoughDataException continue(input.takeRight(needle.length + 2), 0)(parsePreamble)
}
@tailrec def parseHeaderLines(input: ByteString, lineStart: Int, headers: List[HttpHeader] = Nil,
@tailrec def parseHeaderLines(input: ByteString, lineStart: Int, headers: ListBuffer[HttpHeader] = ListBuffer[HttpHeader](),
headerCount: Int = 0, cth: Option[`Content-Type`] = None): StateResult = {
def contentType =
cth match {
@ -136,27 +138,28 @@ private[http] final class BodyPartParser(defaultContentType: ContentType,
case null continue(input, lineStart)(parseHeaderLinesAux(headers, headerCount, cth))
case BoundaryHeader
emit(BodyPartStart(headers, _ HttpEntity.empty(contentType)))
emit(BodyPartStart(headers.toList, _ HttpEntity.empty(contentType)))
val ix = lineStart + boundaryLength
if (crlf(input, ix)) parseHeaderLines(input, ix + 2)
else if (doubleDash(input, ix)) terminate()
else fail("Illegal multipart boundary in message content")
case HttpHeaderParser.EmptyHeader parseEntity(headers, contentType)(input, lineEnd)
case HttpHeaderParser.EmptyHeader parseEntity(headers.toList, contentType)(input, lineEnd)
case h: `Content-Type`
if (cth.isEmpty) parseHeaderLines(input, lineEnd, headers, headerCount + 1, Some(h))
else if (cth.get == h) parseHeaderLines(input, lineEnd, headers, headerCount, cth)
else fail("multipart part must not contain more than one Content-Type header")
case h if headerCount < maxHeaderCount parseHeaderLines(input, lineEnd, h :: headers, headerCount + 1, cth)
case h if headerCount < maxHeaderCount
parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, cth)
case _ fail(s"multipart part contains more than the configured limit of $maxHeaderCount headers")
case _ fail(s"multipart part contains more than the configured limit of $maxHeaderCount headers")
}
}
// work-around for compiler complaining about non-tail-recursion if we inline this method
def parseHeaderLinesAux(headers: List[HttpHeader], headerCount: Int,
def parseHeaderLinesAux(headers: ListBuffer[HttpHeader], headerCount: Int,
cth: Option[`Content-Type`])(input: ByteString, lineStart: Int): StateResult =
parseHeaderLines(input, lineStart, headers, headerCount, cth)

View file

@ -106,8 +106,8 @@ class MultipartUnmarshallersSpec extends FreeSpec with Matchers with BeforeAndAf
|--XYZABC--""".stripMarginWithNewline("\r\n"))).to[Multipart.General] should haveParts(
Multipart.General.BodyPart.Strict(
HttpEntity(ContentTypes.`text/plain(UTF-8)`, "test@there.com"),
List(`Content-Disposition`(ContentDispositionTypes.`form-data`, Map("name" -> "email")),
RawHeader("date", "unknown")))))
List(RawHeader("date", "unknown"),
`Content-Disposition`(ContentDispositionTypes.`form-data`, Map("name" -> "email"))))))
"a full example (Strict)" in {
Unmarshal(HttpEntity(`multipart/mixed` withBoundary "12345",
"""preamble and
@ -246,6 +246,8 @@ class MultipartUnmarshallersSpec extends FreeSpec with Matchers with BeforeAndAf
"""sition: form-data; name="userfile"; filename="test.dat"
|Content-Type: application/pdf
|Content-Transfer-Encoding: binary
|Content-Additional-1: anything
|Content-Additional-2: really-anything
|
|filecontent
|--XYZABC--""".stripMarginWithNewline("\r\n")
@ -253,8 +255,11 @@ class MultipartUnmarshallersSpec extends FreeSpec with Matchers with BeforeAndAf
})
}.to[Multipart.FormData].flatMap(_.toStrict(1.second)) should haveParts(
Multipart.FormData.BodyPart.Strict("email", HttpEntity(ContentTypes.`application/octet-stream`, "test@there.com")),
Multipart.FormData.BodyPart.Strict("userfile", HttpEntity(MediaTypes.`application/pdf`, "filecontent"),
Map("filename" -> "test.dat"), List(RawHeader("Content-Transfer-Encoding", "binary"))))
Multipart.FormData.BodyPart.Strict("userfile", HttpEntity(MediaTypes.`application/pdf`, "filecontent"), Map("filename" -> "test.dat"),
List(
RawHeader("Content-Transfer-Encoding", "binary"),
RawHeader("Content-Additional-1", "anything"),
RawHeader("Content-Additional-2", "really-anything")))) // verifies order of headers is preserved
}
// TODO: reactivate after multipart/form-data unmarshalling integrity verification is implemented
//