=htc #16482 preserve header order during parse

This commit is contained in:
2beaucoup 2014-11-12 13:17:24 +01:00
parent bd3ee6b54f
commit f0c83631e1
4 changed files with 19 additions and 19 deletions

View file

@ -97,7 +97,7 @@ private[http] abstract class HttpMessageParser[Output >: MessageOutput <: Parser
def badProtocol: Nothing
@tailrec final def parseHeaderLines(input: ByteString, lineStart: Int, headers: List[HttpHeader] = Nil,
@tailrec final def parseHeaderLines(input: ByteString, lineStart: Int, headers: ListBuffer[HttpHeader] = ListBuffer[HttpHeader](),
headerCount: Int = 0, ch: Option[Connection] = None,
clh: Option[`Content-Length`] = None, cth: Option[`Content-Type`] = None,
teh: Option[`Transfer-Encoding`] = None, e100c: Boolean = false,
@ -117,7 +117,7 @@ private[http] abstract class HttpMessageParser[Output >: MessageOutput <: Parser
case HttpHeaderParser.EmptyHeader
val close = HttpMessage.connectionCloseExpected(protocol, ch)
setCompletionHandling(CompletionIsEntityStreamError)
parseEntity(headers, protocol, input, lineEnd, clh, cth, teh, e100c, hh, close)
parseEntity(headers.toList, protocol, input, lineEnd, clh, cth, teh, e100c, hh, close)
case h: `Content-Length` clh match {
case None parseHeaderLines(input, lineEnd, headers, headerCount + 1, ch, Some(h), cth, teh, e100c, hh)
@ -134,21 +134,21 @@ private[http] abstract class HttpMessageParser[Output >: MessageOutput <: Parser
case Some(x) parseHeaderLines(input, lineEnd, headers, headerCount, ch, clh, cth, Some(x append h.encodings), e100c, hh)
}
case h: Connection ch match {
case None parseHeaderLines(input, lineEnd, h :: headers, headerCount + 1, Some(h), clh, cth, teh, e100c, hh)
case None parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, Some(h), clh, cth, teh, e100c, hh)
case Some(x) parseHeaderLines(input, lineEnd, headers, headerCount, Some(x append h.tokens), clh, cth, teh, e100c, hh)
}
case h: Host
if (!hh) parseHeaderLines(input, lineEnd, h :: headers, headerCount + 1, ch, clh, cth, teh, e100c, hh = true)
if (!hh) parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, ch, clh, cth, teh, e100c, hh = true)
else failMessageStart("HTTP message must not contain more than one Host header")
case h: Expect parseHeaderLines(input, lineEnd, h :: headers, headerCount + 1, ch, clh, cth, teh, e100c = true, hh)
case h: Expect parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, ch, clh, cth, teh, e100c = true, hh)
case h parseHeaderLines(input, lineEnd, h :: headers, headerCount + 1, ch, clh, cth, teh, e100c, hh)
case h parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, ch, clh, cth, teh, e100c, hh)
}
} else failMessageStart(s"HTTP message 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, ch: Option[Connection],
def parseHeaderLinesAux(headers: ListBuffer[HttpHeader], headerCount: Int, ch: Option[Connection],
clh: Option[`Content-Length`], cth: Option[`Content-Type`], teh: Option[`Transfer-Encoding`],
e100c: Boolean, hh: Boolean)(input: ByteString, lineStart: Int): StateResult =
parseHeaderLines(input, lineStart, headers, headerCount, ch, clh, cth, teh, e100c, hh)

View file

@ -97,7 +97,7 @@ class ClientServerSpec extends WordSpec with Matchers with BeforeAndAfterAll {
val serverInSub = serverIn.expectSubscription()
serverInSub.request(1)
private val HttpRequest(POST, uri, List(`User-Agent`(_), Host(_, _), Accept(Vector(MediaRanges.`*/*`))),
private val HttpRequest(POST, uri, List(Accept(Seq(MediaRanges.`*/*`)), Host(_, _), `User-Agent`(_)),
Chunked(`chunkedContentType`, chunkStream), HttpProtocols.`HTTP/1.1`) = serverIn.expectNext()
uri shouldEqual Uri(s"http://$hostname:$port/chunked")
Await.result(chunkStream.grouped(4).runWith(Sink.head), 100.millis) shouldEqual chunks
@ -107,7 +107,7 @@ class ClientServerSpec extends WordSpec with Matchers with BeforeAndAfterAll {
val clientInSub = clientIn.expectSubscription()
clientInSub.request(1)
val HttpResponse(StatusCodes.PartialContent, List(Date(_), Server(_), RawHeader("Age", "42")),
val HttpResponse(StatusCodes.PartialContent, List(RawHeader("Age", "42"), Server(_), Date(_)),
Chunked(`chunkedContentType`, chunkStream2), HttpProtocols.`HTTP/1.1`) = clientIn.expectNext()
Await.result(chunkStream2.grouped(1000).runWith(Sink.head), 100.millis) shouldEqual chunks
}

View file

@ -76,7 +76,7 @@ class RequestParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|Content-length: 17
|
|Shake your BOODY!""" should parseTo {
HttpRequest(POST, "/resource/yes", List(Connection("keep-alive"), `User-Agent`("curl/7.19.7 xyz")),
HttpRequest(POST, "/resource/yes", List(`User-Agent`("curl/7.19.7 xyz"), Connection("keep-alive")),
"Shake your BOODY!", `HTTP/1.0`)
}
closeAfterResponseCompletion shouldEqual Seq(false)
@ -91,7 +91,7 @@ class RequestParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|Shake your BOODY!GET / HTTP/1.0
|
|""" should parseTo(
HttpRequest(POST, "/resource/yes", List(Connection("keep-alive"), `User-Agent`("curl/7.19.7 xyz")),
HttpRequest(POST, "/resource/yes", List(`User-Agent`("curl/7.19.7 xyz"), Connection("keep-alive")),
"Shake your BOODY!".getBytes, `HTTP/1.0`),
HttpRequest(protocol = `HTTP/1.0`))
closeAfterResponseCompletion shouldEqual Seq(false, true)
@ -107,8 +107,8 @@ class RequestParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
| fancy
|
|""" should parseTo {
HttpRequest(DELETE, "/abc", List(Connection("close", "fancy"), Accept(MediaRanges.`*/*`),
`User-Agent`("curl/7.19.7 abc xyz")), protocol = `HTTP/1.0`)
HttpRequest(DELETE, "/abc", List(`User-Agent`("curl/7.19.7 abc xyz"), Accept(MediaRanges.`*/*`),
Connection("close", "fancy")), protocol = `HTTP/1.0`)
}
closeAfterResponseCompletion shouldEqual Seq(true)
}
@ -165,7 +165,7 @@ class RequestParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|Host: ping
|
|"""
val baseRequest = HttpRequest(PATCH, "/data", List(Host("ping"), Connection("lalelu")))
val baseRequest = HttpRequest(PATCH, "/data", List(Connection("lalelu"), Host("ping")))
"request start" in new Test {
Seq(start, "rest") should generalMultiParseTo(
@ -217,7 +217,7 @@ class RequestParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|
|""") should generalMultiParseTo(
Right(baseRequest.withEntity(Chunked(`application/pdf`,
source(LastChunk("nice=true", List(RawHeader("Bar", "xyz"), RawHeader("Foo", "pip apo"))))))))
source(LastChunk("nice=true", List(RawHeader("Foo", "pip apo"), RawHeader("Bar", "xyz"))))))))
closeAfterResponseCompletion shouldEqual Seq(false)
}
@ -276,7 +276,7 @@ class RequestParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|Host: ping
|
|"""
val baseRequest = HttpRequest(PATCH, "/data", List(Host("ping"), Connection("lalelu")),
val baseRequest = HttpRequest(PATCH, "/data", List(Connection("lalelu"), Host("ping")),
HttpEntity.Chunked(`application/octet-stream`, source()))
"an illegal char after chunk size" in new Test {

View file

@ -105,7 +105,7 @@ class ResponseParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|Content-Type: text/plain; charset=UTF-8
|
|Sh""", "ake your BOODY!HTTP/1.") should generalMultiParseTo(
Right(HttpResponse(InternalServerError, List(Connection("close"), `User-Agent`("curl/7.19.7 xyz")),
Right(HttpResponse(InternalServerError, List(`User-Agent`("curl/7.19.7 xyz"), Connection("close")),
"Shake your BOODY!")))
closeAfterResponseCompletion shouldEqual Seq(true)
}
@ -130,7 +130,7 @@ class ResponseParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|Server: spray-can
|
|"""
val baseResponse = HttpResponse(headers = List(Server("spray-can"), Connection("lalelu")))
val baseResponse = HttpResponse(headers = List(Connection("lalelu"), Server("spray-can")))
"response start" in new Test {
Seq(start, "rest") should generalMultiParseTo(
@ -181,7 +181,7 @@ class ResponseParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|
|HT""") should generalMultiParseTo(
Right(baseResponse.withEntity(Chunked(`application/pdf`,
source(LastChunk("nice=true", List(RawHeader("Bar", "xyz"), RawHeader("Foo", "pip apo"))))))),
source(LastChunk("nice=true", List(RawHeader("Foo", "pip apo"), RawHeader("Bar", "xyz"))))))),
Left(MessageStartError(400: StatusCode, ErrorInfo("Illegal HTTP message start"))))
closeAfterResponseCompletion shouldEqual Seq(false)
}