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) {
|
if (requestMethodForCurrentResponse.isDefined) {
|
||||||
var cursor = parseProtocol(input, offset)
|
var cursor = parseProtocol(input, offset)
|
||||||
if (byteChar(input, cursor) == ' ') {
|
if (byteChar(input, cursor) == ' ') {
|
||||||
cursor = parseStatusCode(input, cursor + 1)
|
cursor = parseStatus(input, cursor + 1)
|
||||||
cursor = parseReason(input, cursor)()
|
|
||||||
parseHeaderLines(input, cursor)
|
parseHeaderLines(input, cursor)
|
||||||
} else badProtocol
|
} else badProtocol
|
||||||
} else {
|
} 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 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 badStatusCode = throw new ParsingException("Illegal response status code")
|
||||||
def intValue(offset: Int): Int = {
|
def parseStatusCode() = {
|
||||||
val c = byteChar(input, cursor + offset)
|
def intValue(offset: Int): Int = {
|
||||||
if (CharacterClasses.DIGIT(c)) c - '0' else badStatusCode
|
val c = byteChar(input, cursor + offset)
|
||||||
}
|
if (CharacterClasses.DIGIT(c)) c - '0' else badStatusCode
|
||||||
if (byteChar(input, cursor + 3) == ' ') {
|
}
|
||||||
val code = intValue(0) * 100 + intValue(1) * 10 + intValue(2)
|
val code = intValue(0) * 100 + intValue(1) * 10 + intValue(2)
|
||||||
statusCode = code match {
|
statusCode = code match {
|
||||||
case 200 ⇒ StatusCodes.OK
|
case 200 ⇒ StatusCodes.OK
|
||||||
|
|
@ -65,17 +64,22 @@ private[http] class HttpResponseParser(_settings: ParserSettings, _headerParser:
|
||||||
case None ⇒ customStatusCodes(code) getOrElse badStatusCode
|
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
|
} 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
|
// http://tools.ietf.org/html/rfc7230#section-3.3
|
||||||
def parseEntity(headers: List[HttpHeader], protocol: HttpProtocol, input: ByteString, bodyStart: Int,
|
def parseEntity(headers: List[HttpHeader], protocol: HttpProtocol, input: ByteString, bodyStart: Int,
|
||||||
clh: Option[`Content-Length`], cth: Option[`Content-Type`], teh: Option[`Transfer-Encoding`],
|
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.util.ByteString
|
||||||
import akka.actor.ActorSystem
|
import akka.actor.ActorSystem
|
||||||
import akka.stream.scaladsl._
|
import akka.stream.scaladsl._
|
||||||
import akka.stream.scaladsl.FlattenStrategy
|
|
||||||
import akka.stream.ActorMaterializer
|
import akka.stream.ActorMaterializer
|
||||||
import akka.http.scaladsl.util.FastFuture._
|
import akka.http.scaladsl.util.FastFuture._
|
||||||
import akka.http.impl.util._
|
import akka.http.impl.util._
|
||||||
|
|
@ -79,6 +78,11 @@ class ResponseParserSpec extends FreeSpec with Matchers with BeforeAndAfterAll {
|
||||||
closeAfterResponseCompletion shouldEqual Seq(false)
|
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 {
|
"a response funky `Transfer-Encoding` header" in new Test {
|
||||||
override def parserSettings: ParserSettings =
|
override def parserSettings: ParserSettings =
|
||||||
super.parserSettings.withCustomStatusCodes(ServerOnTheMove)
|
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"))))
|
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" - {
|
"with entity length > max-content-length" - {
|
||||||
def response(dataElements: ByteString*) = HttpResponse(200, Nil,
|
def response(dataElements: ByteString*) = HttpResponse(200, Nil,
|
||||||
HttpEntity.Chunked(`application/octet-stream`, Source(dataElements.map(ChunkStreamPart(_)).toVector)))
|
HttpEntity.Chunked(`application/octet-stream`, Source(dataElements.map(ChunkStreamPart(_)).toVector)))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue