+htc #16834 apply uri-parsing-mode setting to header parsing as well
This commit is contained in:
parent
880a6b64d0
commit
9907bdad19
8 changed files with 40 additions and 15 deletions
|
|
@ -270,7 +270,8 @@ private[http] object BodyPartParser {
|
|||
maxHeaderValueLength: Int,
|
||||
maxHeaderCount: Int,
|
||||
illegalHeaderWarnings: Boolean,
|
||||
headerValueCacheLimit: Int) extends HttpHeaderParser.Settings {
|
||||
headerValueCacheLimit: Int,
|
||||
uriParsingMode: Uri.ParsingMode) extends HttpHeaderParser.Settings {
|
||||
require(maxHeaderNameLength > 0, "maxHeaderNameLength must be > 0")
|
||||
require(maxHeaderValueLength > 0, "maxHeaderValueLength must be > 0")
|
||||
require(maxHeaderCount > 0, "maxHeaderCount must be > 0")
|
||||
|
|
@ -284,5 +285,6 @@ private[http] object BodyPartParser {
|
|||
maxHeaderValueLength = 8192,
|
||||
maxHeaderCount = 64,
|
||||
illegalHeaderWarnings = true,
|
||||
headerValueCacheLimit = 8)
|
||||
headerValueCacheLimit = 8,
|
||||
uriParsingMode = Uri.ParsingMode.Relaxed)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import scala.annotation.tailrec
|
|||
import akka.parboiled2.CharUtils
|
||||
import akka.util.ByteString
|
||||
import akka.http.impl.util._
|
||||
import akka.http.scaladsl.model.{ IllegalHeaderException, StatusCodes, HttpHeader, ErrorInfo }
|
||||
import akka.http.scaladsl.model.{ IllegalHeaderException, StatusCodes, HttpHeader, ErrorInfo, Uri }
|
||||
import akka.http.scaladsl.model.headers.RawHeader
|
||||
import akka.http.impl.model.parser.HeaderParser
|
||||
import akka.http.impl.model.parser.CharacterClasses._
|
||||
|
|
@ -376,7 +376,7 @@ private[engine] final class HttpHeaderParser private (
|
|||
private[http] object HttpHeaderParser {
|
||||
import SpecializedHeaderValueParsers._
|
||||
|
||||
trait Settings {
|
||||
trait Settings extends HeaderParser.Settings {
|
||||
def maxHeaderNameLength: Int
|
||||
def maxHeaderValueLength: Int
|
||||
def headerValueCacheLimit(headerName: String): Int
|
||||
|
|
@ -410,7 +410,7 @@ private[http] object HttpHeaderParser {
|
|||
def prime(parser: HttpHeaderParser): HttpHeaderParser = {
|
||||
val valueParsers: Seq[HeaderValueParser] =
|
||||
HeaderParser.ruleNames.map { name ⇒
|
||||
new ModelledHeaderValueParser(name, parser.settings.maxHeaderValueLength, parser.settings.headerValueCacheLimit(name))
|
||||
new ModelledHeaderValueParser(name, parser.settings.maxHeaderValueLength, parser.settings.headerValueCacheLimit(name), parser.settings)
|
||||
}(collection.breakOut)
|
||||
def insertInGoodOrder(items: Seq[Any])(startIx: Int = 0, endIx: Int = items.size): Unit =
|
||||
if (endIx - startIx > 0) {
|
||||
|
|
@ -445,13 +445,13 @@ private[http] object HttpHeaderParser {
|
|||
def cachingEnabled = maxValueCount > 0
|
||||
}
|
||||
|
||||
class ModelledHeaderValueParser(headerName: String, maxHeaderValueLength: Int, maxValueCount: Int)
|
||||
class ModelledHeaderValueParser(headerName: String, maxHeaderValueLength: Int, maxValueCount: Int, settings: HeaderParser.Settings)
|
||||
extends HeaderValueParser(headerName, maxValueCount) {
|
||||
def apply(input: ByteString, valueStart: Int, onIllegalHeader: ErrorInfo ⇒ Unit): (HttpHeader, Int) = {
|
||||
// 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.parseFull(headerName, trimmedHeaderValue) match {
|
||||
val header = HeaderParser.parseFull(headerName, trimmedHeaderValue, settings) match {
|
||||
case Right(h) ⇒ h
|
||||
case Left(error) ⇒
|
||||
onIllegalHeader(error.withSummaryPrepended(s"Illegal '$headerName' header"))
|
||||
|
|
|
|||
|
|
@ -420,5 +420,8 @@ private[parser] trait CommonRules { this: Parser with StringBuilding ⇒
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
def newUriParser(input: ParserInput): UriParser
|
||||
def uriReference: Rule1[Uri] = rule { runSubParser(newUriParser(_).`URI-reference-pushed`) }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import akka.http.scaladsl.model._
|
|||
/**
|
||||
* INTERNAL API.
|
||||
*/
|
||||
private[http] class HeaderParser(val input: ParserInput) extends Parser with DynamicRuleHandler[HeaderParser, HttpHeader :: HNil]
|
||||
private[http] class HeaderParser(val input: ParserInput, settings: HeaderParser.Settings = HeaderParser.DefaultSettings) extends Parser with DynamicRuleHandler[HeaderParser, HttpHeader :: HNil]
|
||||
with CommonRules
|
||||
with AcceptCharsetHeader
|
||||
with AcceptEncodingHeader
|
||||
|
|
@ -59,6 +59,8 @@ private[http] class HeaderParser(val input: ParserInput) extends Parser with Dyn
|
|||
case NonFatal(e) ⇒ Left(ErrorInfo.fromCompoundString(e.getMessage))
|
||||
}
|
||||
def ruleNotFound(ruleName: String): Result = throw HeaderParser.RuleNotFoundException
|
||||
|
||||
def newUriParser(input: ParserInput): UriParser = new UriParser(input, uriParsingMode = settings.uriParsingMode)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -67,10 +69,10 @@ 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 = {
|
||||
def parseFull(headerName: String, value: String, settings: Settings = DefaultSettings): 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)
|
||||
val parser = new HeaderParser(v, settings)
|
||||
dispatch(parser, headerName) match {
|
||||
case r @ Right(_) if parser.cursor == v.length ⇒ r
|
||||
case r @ Right(_) ⇒
|
||||
|
|
@ -136,4 +138,15 @@ private[http] object HeaderParser {
|
|||
"user-agent",
|
||||
"www-authenticate",
|
||||
"x-forwarded-for")
|
||||
|
||||
trait Settings {
|
||||
def uriParsingMode: Uri.ParsingMode
|
||||
}
|
||||
def Settings(uriParsingMode: Uri.ParsingMode): Settings = {
|
||||
val _uriParsingMode = uriParsingMode
|
||||
new Settings {
|
||||
def uriParsingMode: Uri.ParsingMode = _uriParsingMode
|
||||
}
|
||||
}
|
||||
val DefaultSettings: Settings = Settings(Uri.ParsingMode.Relaxed)
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ private[parser] trait LinkHeader { this: Parser with CommonRules with CommonActi
|
|||
////////////////////////////// helpers ///////////////////////////////////
|
||||
|
||||
def UriReference(terminationChar: Char) = rule {
|
||||
capture(oneOrMore(!terminationChar ~ VCHAR)) ~> (new UriParser(_).parseUriReference())
|
||||
capture(oneOrMore(!terminationChar ~ VCHAR)) ~> (newUriParser(_).parseUriReference())
|
||||
}
|
||||
|
||||
def URI = rule {
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ private[parser] trait SimpleHeaders { this: Parser with CommonRules with CommonA
|
|||
// Also: an empty hostnames with a non-empty port value (as in `Host: :8080`) are *allowed*,
|
||||
// see http://trac.tools.ietf.org/wg/httpbis/trac/ticket/92
|
||||
def host = rule {
|
||||
runSubParser(new UriParser(_).`hostAndPort-pushed`) ~ EOI ~> (Host(_, _))
|
||||
runSubParser(newUriParser(_).`hostAndPort-pushed`) ~ EOI ~> (Host(_, _))
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/rfc7232#section-3.1
|
||||
|
|
@ -150,7 +150,7 @@ private[parser] trait SimpleHeaders { this: Parser with CommonRules with CommonA
|
|||
|
||||
// http://tools.ietf.org/html/rfc7231#section-7.1.2
|
||||
def location = rule {
|
||||
runSubParser(new UriParser(_).`URI-reference-pushed`) ~ EOI ~> (Location(_))
|
||||
uriReference ~ EOI ~> (Location(_))
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/rfc6454#section-7
|
||||
|
|
@ -171,7 +171,7 @@ private[parser] trait SimpleHeaders { this: Parser with CommonRules with CommonA
|
|||
def referer = rule {
|
||||
// we are bit more relaxed than the spec here by also parsing a potential fragment
|
||||
// but catch it in the `Referer` instance validation (with a `require` in the constructor)
|
||||
runSubParser(new UriParser(_).`URI-reference-pushed`) ~ EOI ~> (Referer(_))
|
||||
uriReference ~ EOI ~> (Referer(_))
|
||||
}
|
||||
|
||||
// http://tools.ietf.org/html/rfc7231#section-7.4.2
|
||||
|
|
|
|||
|
|
@ -574,6 +574,12 @@ class HttpHeaderSpec extends FreeSpec with Matchers {
|
|||
"resolve obs-fold occurrences" in {
|
||||
parse("Foo", "b\r\n\ta \r\n r") shouldEqual ParsingResult.Ok(RawHeader("Foo", "b a r"), Nil)
|
||||
}
|
||||
|
||||
"parse with custom uri parsing mode" in {
|
||||
val targetUri = Uri("http://example.org/?abc=def=ghi", Uri.ParsingMode.RelaxedWithRawQuery)
|
||||
HeaderParser.parseFull("location", "http://example.org/?abc=def=ghi", HeaderParser.Settings(uriParsingMode = Uri.ParsingMode.RelaxedWithRawQuery)) shouldEqual
|
||||
Right(Location(targetUri))
|
||||
}
|
||||
}
|
||||
|
||||
implicit class TestLine(line: String) {
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@
|
|||
|
||||
package akka.http.scaladsl.model.headers
|
||||
|
||||
import akka.http.scaladsl.model._
|
||||
import org.scalatest.{ FreeSpec, MustMatchers }
|
||||
|
||||
import akka.http.scaladsl.model._
|
||||
|
||||
class HeaderSpec extends FreeSpec with MustMatchers {
|
||||
"ModeledCompanion should" - {
|
||||
"provide parseFromValueString method" - {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue