=htc #16839 don't accept headers with trailing garbage
This commit is contained in:
parent
8527e0347e
commit
47bd4c3382
5 changed files with 23 additions and 9 deletions
|
|
@ -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"))
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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._
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue