=htp #17971 parse responses with missing reason phrase

This commit is contained in:
2beaucoup 2015-07-13 13:20:47 +02:00
parent c6a5864e25
commit 9ba7468d7e
6 changed files with 39 additions and 27 deletions

View file

@ -6,8 +6,8 @@ package docs.http.scaladsl.server
package directives
import akka.event.Logging
import akka.http.scaladsl.model.{HttpRequest, HttpResponse}
import akka.http.scaladsl.server.directives.{DebuggingDirectives, LogEntry, LoggingMagnet}
import akka.http.scaladsl.model.{ HttpRequest, HttpResponse }
import akka.http.scaladsl.server.directives.{ DebuggingDirectives, LogEntry, LoggingMagnet }
class DebuggingDirectivesExamplesSpec extends RoutingSpec {
"logRequest-0" in {

View file

@ -30,7 +30,6 @@ class RangeDirectivesExamplesSpec extends RoutingSpec {
responseAs[String] shouldEqual "DE"
}
// we set "akka.http.routing.range-coalescing-threshold = 2"
// above to make sure we get two BodyParts
Get() ~> addHeader(Range(ByteRange(0, 1), ByteRange(1, 2), ByteRange(6, 7))) ~> route ~> check {

View file

@ -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 parseStatusCode() = {
def intValue(offset: Int): Int = {
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)
statusCode = code match {
case 200 StatusCodes.OK
@ -65,16 +64,21 @@ private[http] class HttpResponseParser(_settings: ParserSettings, _headerParser:
case None customStatusCodes(code) getOrElse badStatusCode
}
}
cursor + 4
} 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)
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
}
// http://tools.ietf.org/html/rfc7230#section-3.3
def parseEntity(headers: List[HttpHeader], protocol: HttpProtocol, input: ByteString, bodyStart: Int,

View file

@ -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)))

View file

@ -381,9 +381,9 @@ class TlsSpec extends AkkaSpec("akka.loglevel=INFO\nakka.actor.debug.receive=off
"emit an error if the TLS handshake fails certificate checks" in assertAllStagesStopped {
val getError = Flow[SslTlsInbound]
.map[Either[SslTlsInbound, SSLException]](i => Left(i))
.recover { case e: SSLException => Right(e) }
.collect { case Right(e) => e }.toMat(Sink.head)(Keep.right)
.map[Either[SslTlsInbound, SSLException]](i Left(i))
.recover { case e: SSLException Right(e) }
.collect { case Right(e) e }.toMat(Sink.head)(Keep.right)
val simple = Flow.wrap(getError, Source.lazyEmpty[SslTlsOutbound])(Keep.left)
@ -399,8 +399,8 @@ class TlsSpec extends AkkaSpec("akka.loglevel=INFO\nakka.actor.debug.receive=off
val clientErr = simple.join(badClientTls(IgnoreBoth))
.join(Tcp().outgoingConnection(Await.result(server, 1.second).localAddress)).run()
Await.result(serverErr.flatMap(identity), 1.second).getMessage should include ("certificate_unknown")
Await.result(clientErr, 1.second).getMessage should equal ("General SSLEngine problem")
Await.result(serverErr.flatMap(identity), 1.second).getMessage should include("certificate_unknown")
Await.result(clientErr, 1.second).getMessage should equal("General SSLEngine problem")
}
"reliably cancel subscriptions when TransportIn fails early" in assertAllStagesStopped {

View file

@ -425,10 +425,10 @@ private[akka] class SslTlsCipherActor(settings: ActorMaterializerSettings, sslCo
initialPhase(2, bidirectional)
protected def fail(e: Throwable, closeTransport: Boolean=true): Unit = {
protected def fail(e: Throwable, closeTransport: Boolean = true): Unit = {
if (tracing) log.debug("fail {} due to: {}", self, e.getMessage)
inputBunch.cancel()
if(closeTransport) {
if (closeTransport) {
log.debug("closing output")
outputBunch.error(TransportOut, e)
}