htc #20379 add mima filters for custom media type
This commit is contained in:
parent
313606eb1c
commit
d886a1d0b5
15 changed files with 172 additions and 19 deletions
|
|
@ -11,7 +11,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._
|
||||
import akka.http.scaladsl.model.headers.{ EmptyHeader, RawHeader }
|
||||
import akka.http.impl.model.parser.HeaderParser
|
||||
import akka.http.impl.model.parser.CharacterClasses._
|
||||
|
|
@ -412,6 +412,7 @@ private[http] object HttpHeaderParser {
|
|||
def maxHeaderNameLength: Int
|
||||
def maxHeaderValueLength: Int
|
||||
def headerValueCacheLimit(headerName: String): Int
|
||||
def customMediaTypes: MediaTypes.FindCustom
|
||||
}
|
||||
|
||||
private def predefinedHeaders = Seq(
|
||||
|
|
|
|||
|
|
@ -5,15 +5,20 @@
|
|||
package akka.http.impl.model.parser
|
||||
|
||||
import akka.http.impl.util._
|
||||
import akka.http.scaladsl.model.MediaType.Binary
|
||||
import akka.http.scaladsl.model._
|
||||
import MediaTypes._
|
||||
import akka.stream.impl.ConstantFun
|
||||
|
||||
/** INTERNAL API */
|
||||
private[parser] trait CommonActions {
|
||||
|
||||
def customMediaTypes: MediaTypes.FindCustom
|
||||
|
||||
type StringMapBuilder = scala.collection.mutable.Builder[(String, String), Map[String, String]]
|
||||
|
||||
def getMediaType(mainType: String, subType: String, charsetDefined: Boolean,
|
||||
params: Map[String, String]): MediaType = {
|
||||
import MediaTypes._
|
||||
val subLower = subType.toRootLowerCase
|
||||
mainType.toRootLowerCase match {
|
||||
case "multipart" ⇒ subLower match {
|
||||
|
|
@ -26,20 +31,29 @@ private[parser] trait CommonActions {
|
|||
case custom ⇒ MediaType.customMultipart(custom, params)
|
||||
}
|
||||
case mainLower ⇒
|
||||
MediaTypes.getForKey((mainLower, subLower)) match {
|
||||
// attempt fetching custom media type if configured
|
||||
if (areCustomMediaTypesDefined)
|
||||
customMediaTypes(mainLower, subType) getOrElse fallbackMediaType(subType, params, mainLower)
|
||||
else MediaTypes.getForKey((mainLower, subLower)) match {
|
||||
case Some(registered) ⇒ if (params.isEmpty) registered else registered.withParams(params)
|
||||
case None ⇒
|
||||
if (charsetDefined)
|
||||
MediaType.customWithOpenCharset(mainLower, subType, params = params, allowArbitrarySubtypes = true)
|
||||
else
|
||||
MediaType.customBinary(mainLower, subType, MediaType.Compressible, params = params,
|
||||
allowArbitrarySubtypes = true)
|
||||
fallbackMediaType(subType, params, mainLower)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Provide a generic MediaType when no known-ones matched. */
|
||||
private def fallbackMediaType(subType: String, params: Map[String, String], mainLower: String): Binary =
|
||||
MediaType.customBinary(mainLower, subType, MediaType.Compressible, params = params, allowArbitrarySubtypes = true)
|
||||
|
||||
def getCharset(name: String): HttpCharset =
|
||||
HttpCharsets
|
||||
.getForKeyCaseInsensitive(name)
|
||||
.getOrElse(HttpCharset.custom(name))
|
||||
|
||||
@inline private def areCustomMediaTypesDefined: Boolean = customMediaTypes ne ConstantFun.two2none
|
||||
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ package akka.http.impl.model.parser
|
|||
import akka.http.scaladsl.settings.ParserSettings
|
||||
import akka.http.scaladsl.settings.ParserSettings.CookieParsingMode
|
||||
import akka.http.scaladsl.model.headers.HttpCookiePair
|
||||
import akka.stream.impl.ConstantFun
|
||||
import scala.util.control.NonFatal
|
||||
import akka.http.impl.util.SingletonException
|
||||
import akka.parboiled2._
|
||||
|
|
@ -16,7 +17,10 @@ import akka.http.scaladsl.model._
|
|||
/**
|
||||
* INTERNAL API.
|
||||
*/
|
||||
private[http] class HeaderParser(val input: ParserInput, settings: HeaderParser.Settings = HeaderParser.DefaultSettings) 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
|
||||
|
|
@ -33,6 +37,8 @@ private[http] class HeaderParser(val input: ParserInput, settings: HeaderParser.
|
|||
with WebSocketHeaders {
|
||||
import CharacterClasses._
|
||||
|
||||
override def customMediaTypes = settings.customMediaTypes
|
||||
|
||||
// http://www.rfc-editor.org/errata_search.php?rfc=7230 errata id 4189
|
||||
def `header-field-value`: Rule1[String] = rule {
|
||||
FWS ~ clearSB() ~ `field-value` ~ FWS ~ EOI ~ push(sb.toString)
|
||||
|
|
@ -161,15 +167,19 @@ private[http] object HeaderParser {
|
|||
abstract class Settings {
|
||||
def uriParsingMode: Uri.ParsingMode
|
||||
def cookieParsingMode: ParserSettings.CookieParsingMode
|
||||
def customMediaTypes: MediaTypes.FindCustom
|
||||
}
|
||||
def Settings(uriParsingMode: Uri.ParsingMode = Uri.ParsingMode.Relaxed,
|
||||
cookieParsingMode: ParserSettings.CookieParsingMode = ParserSettings.CookieParsingMode.RFC6265): Settings = {
|
||||
cookieParsingMode: ParserSettings.CookieParsingMode = ParserSettings.CookieParsingMode.RFC6265,
|
||||
customMediaTypes: MediaTypes.FindCustom = ConstantFun.scalaAnyTwoToNone): Settings = {
|
||||
val _uriParsingMode = uriParsingMode
|
||||
val _cookieParsingMode = cookieParsingMode
|
||||
val _customMediaTypes = customMediaTypes
|
||||
|
||||
new Settings {
|
||||
def uriParsingMode: Uri.ParsingMode = _uriParsingMode
|
||||
def cookieParsingMode: CookieParsingMode = _cookieParsingMode
|
||||
def customMediaTypes: MediaTypes.FindCustom = _customMediaTypes
|
||||
}
|
||||
}
|
||||
val DefaultSettings: Settings = Settings()
|
||||
|
|
|
|||
|
|
@ -6,9 +6,10 @@ package akka.http.impl.settings
|
|||
|
||||
import akka.http.scaladsl.settings.ParserSettings
|
||||
import akka.http.scaladsl.settings.ParserSettings.{ ErrorLoggingVerbosity, CookieParsingMode }
|
||||
import akka.stream.impl.ConstantFun
|
||||
import com.typesafe.config.Config
|
||||
import scala.collection.JavaConverters._
|
||||
import akka.http.scaladsl.model.{ StatusCode, HttpMethod, Uri }
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.impl.util._
|
||||
|
||||
/** INTERNAL API */
|
||||
|
|
@ -29,7 +30,8 @@ private[akka] final case class ParserSettingsImpl(
|
|||
headerValueCacheLimits: Map[String, Int],
|
||||
includeTlsSessionInfoHeader: Boolean,
|
||||
customMethods: String ⇒ Option[HttpMethod],
|
||||
customStatusCodes: Int ⇒ Option[StatusCode])
|
||||
customStatusCodes: Int ⇒ Option[StatusCode],
|
||||
customMediaTypes: MediaTypes.FindCustom)
|
||||
extends akka.http.scaladsl.settings.ParserSettings {
|
||||
|
||||
require(maxUriLength > 0, "max-uri-length must be > 0")
|
||||
|
|
@ -52,9 +54,9 @@ private[akka] final case class ParserSettingsImpl(
|
|||
|
||||
object ParserSettingsImpl extends SettingsCompanion[ParserSettingsImpl]("akka.http.parsing") {
|
||||
|
||||
// for equality
|
||||
private[this] val noCustomMethods: String ⇒ Option[HttpMethod] = _ ⇒ None
|
||||
private[this] val noCustomStatusCodes: Int ⇒ Option[StatusCode] = _ ⇒ None
|
||||
private[this] val noCustomMethods: String ⇒ Option[HttpMethod] = ConstantFun.scalaAnyToNone
|
||||
private[this] val noCustomStatusCodes: Int ⇒ Option[StatusCode] = ConstantFun.scalaAnyToNone
|
||||
private[this] val noCustomMediaTypes: (String, String) ⇒ Option[MediaType] = ConstantFun.scalaAnyTwoToNone
|
||||
|
||||
def fromSubConfig(root: Config, inner: Config) = {
|
||||
val c = inner.withFallback(root.getConfig(prefix))
|
||||
|
|
@ -77,7 +79,8 @@ object ParserSettingsImpl extends SettingsCompanion[ParserSettingsImpl]("akka.ht
|
|||
cacheConfig.entrySet.asScala.map(kvp ⇒ kvp.getKey -> cacheConfig.getInt(kvp.getKey))(collection.breakOut),
|
||||
c getBoolean "tls-session-info-header",
|
||||
noCustomMethods,
|
||||
noCustomStatusCodes)
|
||||
noCustomStatusCodes,
|
||||
noCustomMediaTypes)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ object ServerSettingsImpl extends SettingsCompanion[ServerSettingsImpl]("akka.ht
|
|||
c getInt "backlog",
|
||||
SocketOptionSettings.fromSubConfig(root, c.getConfig("socket-options")),
|
||||
defaultHostHeader =
|
||||
HttpHeader.parse("Host", c getString "default-host-header") match {
|
||||
HttpHeader.parse("Host", c getString "default-host-header", ParserSettings(root)) match {
|
||||
case HttpHeader.ParsingResult.Ok(x: Host, Nil) ⇒ x
|
||||
case result ⇒
|
||||
val info = result.errors.head.withSummary("Configured `default-host-header` is illegal")
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import akka.http.impl.util.JavaMapping.Implicits._
|
|||
import scala.annotation.varargs
|
||||
import scala.collection.JavaConverters._
|
||||
|
||||
import akka.http.javadsl.model.{ HttpMethod, StatusCode, Uri }
|
||||
import akka.http.javadsl.model.{ MediaType, HttpMethod, StatusCode, Uri }
|
||||
import com.typesafe.config.Config
|
||||
|
||||
/**
|
||||
|
|
@ -37,6 +37,7 @@ abstract class ParserSettings private[akka] () extends BodyPartParser.Settings {
|
|||
def headerValueCacheLimits: Map[String, Int]
|
||||
def getCustomMethods: java.util.function.Function[String, Optional[HttpMethod]]
|
||||
def getCustomStatusCodes: java.util.function.Function[Int, Optional[StatusCode]]
|
||||
def getCustomMediaTypes: akka.japi.function.Function2[String, String, Optional[MediaType]]
|
||||
|
||||
// ---
|
||||
|
||||
|
|
@ -68,6 +69,11 @@ abstract class ParserSettings private[akka] () extends BodyPartParser.Settings {
|
|||
val map = codes.map(c ⇒ c.intValue -> c.asScala).toMap
|
||||
self.copy(customStatusCodes = map.get)
|
||||
}
|
||||
@varargs
|
||||
def withCustomMediaTypes(mediaTypes: MediaType*): ParserSettings = {
|
||||
val map = mediaTypes.map(c ⇒ (c.mainType, c.subType) -> c.asScala).toMap
|
||||
self.copy(customMediaTypes = (main, sub) ⇒ map.get(main -> sub))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
package akka.http.scaladsl.model
|
||||
|
||||
import akka.http.scaladsl.settings.ParserSettings
|
||||
|
||||
import scala.util.{ Success, Failure }
|
||||
import akka.parboiled2.ParseError
|
||||
import akka.http.impl.util.ToStringRenderable
|
||||
|
|
|
|||
|
|
@ -84,7 +84,12 @@ sealed trait HttpMessage extends jm.HttpMessage {
|
|||
/** Returns the first header of the given type if there is one */
|
||||
def header[T <: jm.HttpHeader: ClassTag]: Option[T] = {
|
||||
val erasure = classTag[T].runtimeClass
|
||||
headers.find(erasure.isInstance).asInstanceOf[Option[T]]
|
||||
headers.find(erasure.isInstance).asInstanceOf[Option[T]] match {
|
||||
case header: Some[T] => header
|
||||
case _ if erasure == classOf[`Content-Type`] => Some(entity.contentType).asInstanceOf[Option[T]]
|
||||
case _ => None
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -258,6 +258,8 @@ object MediaType {
|
|||
}
|
||||
|
||||
object MediaTypes extends ObjectRegistry[(String, String), MediaType] {
|
||||
type FindCustom = (String, String) => Option[MediaType]
|
||||
|
||||
private[this] var extensionMap = Map.empty[String, MediaType]
|
||||
|
||||
def forExtensionOption(ext: String): Option[MediaType] = extensionMap.get(ext.toLowerCase)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ import java.util.function.Function
|
|||
|
||||
import akka.http.impl.settings.ParserSettingsImpl
|
||||
import akka.http.impl.util._
|
||||
import akka.http.scaladsl.model.{ HttpMethod, StatusCode, Uri }
|
||||
import akka.http.javadsl.model
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.{ settings ⇒ js }
|
||||
import com.typesafe.config.Config
|
||||
|
||||
|
|
@ -37,6 +38,7 @@ abstract class ParserSettings private[akka] () extends akka.http.javadsl.setting
|
|||
def includeTlsSessionInfoHeader: Boolean
|
||||
def customMethods: String ⇒ Option[HttpMethod]
|
||||
def customStatusCodes: Int ⇒ Option[StatusCode]
|
||||
def customMediaTypes: MediaTypes.FindCustom
|
||||
|
||||
/* Java APIs */
|
||||
override def getCookieParsingMode: js.ParserSettings.CookieParsingMode = cookieParsingMode
|
||||
|
|
@ -61,6 +63,10 @@ abstract class ParserSettings private[akka] () extends akka.http.javadsl.setting
|
|||
override def getCustomStatusCodes = new Function[Int, Optional[akka.http.javadsl.model.StatusCode]] {
|
||||
override def apply(t: Int) = OptionConverters.toJava(customStatusCodes(t))
|
||||
}
|
||||
override def getCustomMediaTypes = new akka.japi.function.Function2[String, String, Optional[akka.http.javadsl.model.MediaType]] {
|
||||
override def apply(mainType: String, subType: String): Optional[model.MediaType] =
|
||||
OptionConverters.toJava(customMediaTypes(mainType, subType))
|
||||
}
|
||||
|
||||
// ---
|
||||
|
||||
|
|
@ -90,6 +96,10 @@ abstract class ParserSettings private[akka] () extends akka.http.javadsl.setting
|
|||
val map = codes.map(c ⇒ c.intValue -> c).toMap
|
||||
self.copy(customStatusCodes = map.get)
|
||||
}
|
||||
def withCustomMediaTypes(types: MediaType*): ParserSettings = {
|
||||
val map = types.map(c ⇒ (c.mainType, c.subType) -> c).toMap
|
||||
self.copy(customMediaTypes = (main, sub) ⇒ map.get((main, sub)))
|
||||
}
|
||||
}
|
||||
|
||||
object ParserSettings extends SettingsCompanion[ParserSettings] {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue