Merge pull request #18752 from 2beaucoup/missing-reason-phrase
=htp #17971 parse responses with missing reason phrase
This commit is contained in:
commit
e331e390d3
2 changed files with 30 additions and 17 deletions
|
|
@ -34,8 +34,7 @@ private[http] class HttpResponseParser(_settings: ParserSettings, _headerParser:
|
|||
if (requestMethodForCurrentResponse.isDefined) {
|
||||
var cursor = parseProtocol(input, offset)
|
||||
if (byteChar(input, cursor) == ' ') {
|
||||
cursor = parseStatusCode(input, cursor + 1)
|
||||
cursor = parseReason(input, cursor)()
|
||||
cursor = parseStatus(input, cursor + 1)
|
||||
parseHeaderLines(input, cursor)
|
||||
} else badProtocol
|
||||
} else {
|
||||
|
|
@ -50,13 +49,13 @@ private[http] class HttpResponseParser(_settings: ParserSettings, _headerParser:
|
|||
|
||||
def badProtocol = throw new ParsingException("The server-side HTTP version is not supported")
|
||||
|
||||
def parseStatusCode(input: ByteString, cursor: Int): Int = {
|
||||
def parseStatus(input: ByteString, cursor: Int): Int = {
|
||||
def badStatusCode = throw new ParsingException("Illegal response status code")
|
||||
def intValue(offset: Int): Int = {
|
||||
val c = byteChar(input, cursor + offset)
|
||||
if (CharacterClasses.DIGIT(c)) c - '0' else badStatusCode
|
||||
}
|
||||
if (byteChar(input, cursor + 3) == ' ') {
|
||||
def parseStatusCode() = {
|
||||
def intValue(offset: Int): Int = {
|
||||
val c = byteChar(input, cursor + offset)
|
||||
if (CharacterClasses.DIGIT(c)) c - '0' else badStatusCode
|
||||
}
|
||||
val code = intValue(0) * 100 + intValue(1) * 10 + intValue(2)
|
||||
statusCode = code match {
|
||||
case 200 ⇒ StatusCodes.OK
|
||||
|
|
@ -65,17 +64,22 @@ private[http] class HttpResponseParser(_settings: ParserSettings, _headerParser:
|
|||
case None ⇒ customStatusCodes(code) getOrElse badStatusCode
|
||||
}
|
||||
}
|
||||
cursor + 4
|
||||
}
|
||||
if (byteChar(input, cursor + 3) == ' ') {
|
||||
parseStatusCode()
|
||||
val startIdx = cursor + 4
|
||||
@tailrec def skipReason(idx: Int): Int =
|
||||
if (idx - startIdx <= maxResponseReasonLength)
|
||||
if (byteChar(input, idx) == '\r' && byteChar(input, idx + 1) == '\n') idx + 2
|
||||
else skipReason(idx + 1)
|
||||
else throw new ParsingException("Response reason phrase exceeds the configured limit of " +
|
||||
maxResponseReasonLength + " characters")
|
||||
skipReason(startIdx)
|
||||
} else if (byteChar(input, cursor + 3) == '\r' && byteChar(input, cursor + 4) == '\n') {
|
||||
throw new ParsingException("Status code misses trailing space")
|
||||
} else badStatusCode
|
||||
}
|
||||
|
||||
@tailrec private def parseReason(input: ByteString, startIx: Int)(cursor: Int = startIx): Int =
|
||||
if (cursor - startIx <= maxResponseReasonLength)
|
||||
if (byteChar(input, cursor) == '\r' && byteChar(input, cursor + 1) == '\n') cursor + 2
|
||||
else parseReason(input, startIx)(cursor + 1)
|
||||
else throw new ParsingException("Response reason phrase exceeds the configured limit of " +
|
||||
maxResponseReasonLength + " characters")
|
||||
|
||||
// http://tools.ietf.org/html/rfc7230#section-3.3
|
||||
def parseEntity(headers: List[HttpHeader], protocol: HttpProtocol, input: ByteString, bodyStart: Int,
|
||||
clh: Option[`Content-Length`], cth: Option[`Content-Type`], teh: Option[`Transfer-Encoding`],
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import org.scalatest.matchers.Matcher
|
|||
import akka.util.ByteString
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.scaladsl._
|
||||
import akka.stream.scaladsl.FlattenStrategy
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.http.scaladsl.util.FastFuture._
|
||||
import akka.http.impl.util._
|
||||
|
|
@ -79,6 +78,11 @@ class ResponseParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|
|||
closeAfterResponseCompletion shouldEqual Seq(false)
|
||||
}
|
||||
|
||||
"a response with a missing reason phrase" in new Test {
|
||||
"HTTP/1.1 200 \r\nContent-Length: 0\r\n\r\n" should parseTo(HttpResponse(OK))
|
||||
closeAfterResponseCompletion shouldEqual Seq(false)
|
||||
}
|
||||
|
||||
"a response funky `Transfer-Encoding` header" in new Test {
|
||||
override def parserSettings: ParserSettings =
|
||||
super.parserSettings.withCustomStatusCodes(ServerOnTheMove)
|
||||
|
|
@ -227,6 +231,11 @@ class ResponseParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|
|||
MessageStartError(400: StatusCode, ErrorInfo("Response reason phrase exceeds the configured limit of 21 characters"))))
|
||||
}
|
||||
|
||||
"with a missing reason phrase and no trailing space" in new Test {
|
||||
Seq("HTTP/1.1 200\r\nContent-Length: 0\r\n\r\n") should generalMultiParseTo(Left(MessageStartError(
|
||||
400: StatusCode, ErrorInfo("Status code misses trailing space"))))
|
||||
}
|
||||
|
||||
"with entity length > max-content-length" - {
|
||||
def response(dataElements: ByteString*) = HttpResponse(200, Nil,
|
||||
HttpEntity.Chunked(`application/octet-stream`, Source(dataElements.map(ChunkStreamPart(_)).toVector)))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue