=htc #16839 don't accept headers with trailing garbage

This commit is contained in:
Johannes Rudolph 2015-06-05 16:17:04 +02:00
parent 8527e0347e
commit 47bd4c3382
5 changed files with 23 additions and 9 deletions

View file

@ -451,7 +451,7 @@ private[http] object HttpHeaderParser {
// TODO: optimize by running the header value parser directly on the input ByteString (rather than an extracted String)
val (headerValue, endIx) = scanHeaderValue(input, valueStart, valueStart + maxHeaderValueLength)()
val trimmedHeaderValue = headerValue.trim
val header = HeaderParser.dispatch(new HeaderParser(trimmedHeaderValue), headerName) match {
val header = HeaderParser.parseFull(headerName, trimmedHeaderValue) match {
case Right(h) h
case Left(error)
onIllegalHeader(error.withSummaryPrepended(s"Illegal '$headerName' header"))

View file

@ -67,6 +67,19 @@ private[http] class HeaderParser(val input: ParserInput) extends Parser with Dyn
private[http] object HeaderParser {
object RuleNotFoundException extends SingletonException
def parseFull(headerName: String, value: String): HeaderParser#Result = {
import akka.parboiled2.EOI
val v = value + EOI // this makes sure the parser isn't broken even if there's no trailing garbage in this value
val parser = new HeaderParser(v)
dispatch(parser, headerName) match {
case r @ Right(_) if parser.cursor == v.length r
case r @ Right(_)
Left(ErrorInfo("Header parsing error",
s"Rule for $headerName accepted trailing garbage. Is the parser missing a trailing EOI?"))
case Left(e) Left(e.copy(summary = e.summary.filterNot(_ == EOI), detail = e.detail.filterNot(_ == EOI)))
}
}
val (dispatch, ruleNames) = DynamicRuleDispatch[HeaderParser, HttpHeader :: HNil](
"accept",
"accept-charset",

View file

@ -37,7 +37,7 @@ private[parser] trait SimpleHeaders { this: Parser with CommonRules with CommonA
// http://www.w3.org/TR/cors/#access-control-allow-origin-response-header
def `access-control-allow-origin` = rule(
ws('*') ~ push(`Access-Control-Allow-Origin`.`*`)
ws('*') ~ EOI ~ push(`Access-Control-Allow-Origin`.`*`)
| `origin-list-or-null` ~ EOI ~> (origins `Access-Control-Allow-Origin`.forRange(HttpOriginRange(origins: _*))))
// http://www.w3.org/TR/cors/#access-control-expose-headers-response-header
@ -108,7 +108,7 @@ private[parser] trait SimpleHeaders { this: Parser with CommonRules with CommonA
// http://tools.ietf.org/html/rfc7231#section-5.1.1
def `expect` = rule {
ignoreCase("100-continue") ~ OWS ~ push(Expect.`100-continue`)
ignoreCase("100-continue") ~ OWS ~ EOI ~ push(Expect.`100-continue`)
}
// http://tools.ietf.org/html/rfc7234#section-5.3
@ -126,7 +126,7 @@ private[parser] trait SimpleHeaders { this: Parser with CommonRules with CommonA
// http://tools.ietf.org/html/rfc7232#section-3.1
def `if-match` = rule(
ws('*') ~ push(`If-Match`.`*`)
ws('*') ~ EOI ~ push(`If-Match`.`*`)
| oneOrMore(`entity-tag`).separatedBy(listSep) ~ EOI ~> (tags `If-Match`(EntityTagRange(tags: _*))))
// http://tools.ietf.org/html/rfc7232#section-3.3
@ -134,7 +134,7 @@ private[parser] trait SimpleHeaders { this: Parser with CommonRules with CommonA
// http://tools.ietf.org/html/rfc7232#section-3.2
def `if-none-match` = rule {
ws('*') ~ push(`If-None-Match`.`*`) |
ws('*') ~ EOI ~ push(`If-None-Match`.`*`) |
oneOrMore(`entity-tag`).separatedBy(listSep) ~ EOI ~> (tags `If-None-Match`(EntityTagRange(tags: _*)))
}
@ -175,7 +175,7 @@ private[parser] trait SimpleHeaders { this: Parser with CommonRules with CommonA
}
// http://tools.ietf.org/html/rfc7231#section-7.4.2
def server = rule { products ~> (Server(_)) }
def server = rule { products ~ EOI ~> (Server(_)) }
// http://tools.ietf.org/html/rfc7230#section-3.3.1
def `transfer-encoding` = rule {
@ -189,7 +189,7 @@ private[parser] trait SimpleHeaders { this: Parser with CommonRules with CommonA
// http://tools.ietf.org/html/rfc7230#section-6.7
def upgrade = rule {
oneOrMore(protocol).separatedBy(listSep) ~> (Upgrade(_))
oneOrMore(protocol).separatedBy(listSep) ~ EOI ~> (Upgrade(_))
}
def protocol = rule {
@ -197,7 +197,7 @@ private[parser] trait SimpleHeaders { this: Parser with CommonRules with CommonA
}
// http://tools.ietf.org/html/rfc7231#section-5.5.3
def `user-agent` = rule { products ~> (`User-Agent`(_)) }
def `user-agent` = rule { products ~ EOI ~> (`User-Agent`(_)) }
// http://tools.ietf.org/html/rfc7235#section-4.1
def `www-authenticate` = rule {

View file

@ -60,7 +60,7 @@ object HttpHeader {
parser.`header-field-value`.run() match {
case Success(preProcessedValue)
try {
HeaderParser.dispatch(new HeaderParser(preProcessedValue), name.toLowerCase) match {
HeaderParser.parseFull(name.toLowerCase, preProcessedValue) match {
case Right(header) ParsingResult.Ok(header, Nil)
case Left(info)
val errors = info.withSummaryPrepended(s"Illegal HTTP header '$name'") :: Nil

View file

@ -4,6 +4,7 @@
package akka.http.impl.model.parser
import akka.http.scaladsl.model.HttpHeader.ParsingResult
import org.scalatest.{ Matchers, FreeSpec }
import org.scalatest.matchers.{ Matcher, MatchResult }
import akka.http.impl.util._