in http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpMessageParser.scala [148:229]
@tailrec protected final def parseHeaderLines(input: ByteString, lineStart: Int,
headers: ListBuffer[HttpHeader] = initialHeaderBuffer,
headerCount: Int = 0, ch: Option[Connection] = None,
clh: Option[`Content-Length`] = None, cth: Option[`Content-Type`] = None,
isChunked: Boolean = false, e100c: Boolean = false,
hh: Boolean = false): StateResult =
if (headerCount < settings.maxHeaderCount) {
var lineEnd = 0
val resultHeader =
try {
lineEnd = headerParser.parseHeaderLine(input, lineStart)()
headerParser.resultHeader
} catch {
case NotEnoughDataException => null
}
resultHeader match {
case null =>
continue(input, lineStart)(parseHeaderLinesAux(headers, headerCount, ch, clh, cth, isChunked, e100c, hh))
case EmptyHeader =>
val close = HttpMessage.connectionCloseExpected(protocol, ch)
setCompletionHandling(CompletionIsEntityStreamError)
parseEntity(headers.toList, protocol, input, lineEnd, clh, cth, isChunked, e100c, hh, close, lastSession)
case h: `Content-Length` => clh match {
case None =>
parseHeaderLines(input, lineEnd, headers, headerCount + 1, ch, Some(h), cth, isChunked, e100c, hh)
case Some(`h`) => parseHeaderLines(input, lineEnd, headers, headerCount, ch, clh, cth, isChunked, e100c, hh)
case _ => failMessageStart("HTTP message must not contain more than one Content-Length header")
}
case h: `Content-Type` => cth match {
case None =>
parseHeaderLines(input, lineEnd, headers, headerCount + 1, ch, clh, Some(h), isChunked, e100c, hh)
case Some(`h`) =>
parseHeaderLines(input, lineEnd, headers, headerCount, ch, clh, cth, isChunked, e100c, hh)
case Some(`Content-Type`(ContentTypes.`NoContentType`)) => // never encountered except when parsing conflicting headers (see below)
parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, ch, clh, cth, isChunked, e100c, hh)
case Some(x) =>
import ConflictingContentTypeHeaderProcessingMode._
settings.conflictingContentTypeHeaderProcessingMode match {
case Error => failMessageStart("HTTP message must not contain more than one Content-Type header")
case First =>
parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, ch, clh, cth, isChunked, e100c, hh)
case Last => parseHeaderLines(input, lineEnd, headers += x, headerCount + 1, ch, clh, Some(h),
isChunked, e100c, hh)
case NoContentType => parseHeaderLines(input, lineEnd, headers += x += h, headerCount + 1, ch, clh,
Some(`Content-Type`(ContentTypes.`NoContentType`)), isChunked, e100c, hh)
}
}
case h: `Transfer-Encoding` =>
if (!isChunked) {
h.encodings match {
case Seq(TransferEncodings.chunked) =>
// A single chunked is the only one we support
parseHeaderLines(input, lineEnd, headers, headerCount + 1, ch, clh, cth, isChunked = true, e100c, hh)
case Seq(unknown) =>
failMessageStart(s"Unsupported Transfer-Encoding '${unknown.name}'")
case _ =>
failMessageStart("Multiple Transfer-Encoding entries not supported")
}
} else {
// only allow one 'chunked'
failMessageStart("Multiple Transfer-Encoding entries not supported")
}
case h: Connection => ch match {
case None =>
parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, Some(h), clh, cth, isChunked, e100c, hh)
case Some(x) => parseHeaderLines(input, lineEnd, headers, headerCount, Some(x.append(h.tokens)), clh, cth,
isChunked, e100c, hh)
}
case h: Host =>
if (!hh || isResponseParser)
parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, ch, clh, cth, isChunked, e100c, hh = true)
else failMessageStart("HTTP message must not contain more than one Host header")
case h: Expect =>
parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, ch, clh, cth, isChunked, e100c = true, hh)
case h => parseHeaderLines(input, lineEnd, headers += h, headerCount + 1, ch, clh, cth, isChunked, e100c, hh)
}
} else