Actually implement proper cascading

This commit is contained in:
Konrad Malawski 2015-09-25 15:50:11 +02:00
parent b1f9f77a17
commit cad852025d
12 changed files with 260 additions and 267 deletions

View file

@ -257,11 +257,14 @@ provided to parse (or render to) Strings or byte arrays.
.. note::
Various parsing and rendering settings are available to tweak in the configuration under ``akka.http.client[.parsing]``,
``akka.http.server[.parsing]`` and ``akka.http.host-connection-pool[.client.parsing]``. Please note that while each of
these sections expose the same parsing options, they will only be used for their respective APIs. For example, changing
a setting inside ``akka.http.client.parsing`` **does not** change the same value in ``akka.http.host-connection-pool.client.parsing``,
as they are different APIs.
``akka.http.server[.parsing]`` and ``akka.http.host-connection-pool[.client.parsing]``, with defaults for all of these
being defined in the ``akka.http.parsing`` configuration section.
You can check which settings to tweak for which method exposed on the Http object by inspecting their ScalaDoc.
In general though it's as simple as "pool" backed services use the ``host-connection-pool`` section, other clients
which expose ``Flow`` use the ``client`` section, and the server side uses the ``server`` configuration section.
For example, if you want to change a parsing setting for all components, you can set the ``akka.http.parsing.illegal-header-warnings = off``
value. However this setting can be stil overriden by the more specific sections, like for example ``akka.http.server.parsing.illegal-header-warnings = on``.
In this case both ``client`` and ``host-connection-pool`` APIs will see the setting ``off``, however the server will see ``on``.
In the case of ``akka.http.host-connection-pool.client`` settings, they default to settings set in ``akka.http.client``,
and can override them if needed. This is useful, since both ``client`` and ``host-connection-pool`` APIs,
such as the Client API ``Http.get(sys).outgoingConnection`` or the Host Connection Pool APIs ``Http.get(sys).singleRequest``
or ``Http.get(sys).superPool``, usually need the same settings, however the ``server`` most likely has a very different set of settings.

View file

@ -253,11 +253,14 @@ provided to parse (or render to) Strings or byte arrays.
.. note::
Various parsing and rendering settings are available to tweak in the configuration under ``akka.http.client[.parsing]``,
``akka.http.server[.parsing]`` and ``akka.http.host-connection-pool[.client.parsing]``. Please note that while each of
these sections expose the same parsing options, they will only be used for their respective APIs. For example, changing
a setting inside ``akka.http.client.parsing`` **does not** change the same value in ``akka.http.host-connection-pool.client.parsing``,
as they are different APIs.
``akka.http.server[.parsing]`` and ``akka.http.host-connection-pool[.client.parsing]``, with defaults for all of these
being defined in the ``akka.http.parsing`` configuration section.
You can check which settings to tweak for which method exposed on the Http object by inspecting their ScalaDoc.
In general though it's as simple as "pool" backed services use the ``host-connection-pool`` section, other clients
which expose ``Flow`` use the ``client`` section, and the server side uses the ``server`` configuration section.
For example, if you want to change a parsing setting for all components, you can set the ``akka.http.parsing.illegal-header-warnings = off``
value. However this setting can be stil overriden by the more specific sections, like for example ``akka.http.server.parsing.illegal-header-warnings = on``.
In this case both ``client`` and ``host-connection-pool`` APIs will see the setting ``off``, however the server will see ``on``.
In the case of ``akka.http.host-connection-pool.client`` settings, they default to settings set in ``akka.http.client``,
and can override them if needed. This is useful, since both ``client`` and ``host-connection-pool`` APIs,
such as the Client API ``Http().outgoingConnection`` or the Host Connection Pool APIs ``Http().singleRequest`` or ``Http().superPool``,
usually need the same settings, however the ``server`` most likely has a very different set of settings.

View file

@ -88,76 +88,8 @@ akka.http {
}
# Modify to tweak parsing settings on the server-side only.
#
# IMPORTANT:
# Please note that this section is replicated in `client` and `http-connection-pool`
parsing {
# The limits for the various parts of the HTTP message parser.
max-uri-length = 2k
max-method-length = 16
max-response-reason-length = 64
max-header-name-length = 64
max-header-value-length = 8k
max-header-count = 64
max-chunk-ext-length = 256
max-chunk-size = 1m
# Maximum content length which should not be exceeded by incoming HttpRequests.
# For file uploads which use the entityBytes Source of an incoming HttpRequest it is safe to
# set this to a very high value (or to `infinite` if feeling very adventurous) as the streaming
# upload will be back-pressured properly by Akka Streams.
# Please note however that this setting is a global property, and is applied to all incoming requests,
# not only file uploads consumed in a streaming fashion, so pick this limit wisely.
max-content-length = 8m
# Sets the strictness mode for parsing request target URIs.
# The following values are defined:
#
# `strict`: RFC3986-compliant URIs are required,
# a 400 response is triggered on violations
#
# `relaxed`: all visible 7-Bit ASCII chars are allowed
#
# `relaxed-with-raw-query`: like `relaxed` but additionally
# the URI query is not parsed, but delivered as one raw string
# as the `key` value of a single Query structure element.
#
uri-parsing-mode = strict
# Enables/disables the logging of warning messages in case an incoming
# message (request or response) contains an HTTP header which cannot be
# parsed into its high-level model class due to incompatible syntax.
# Note that, independently of this settings, akka-http will accept messages
# with such headers as long as the message as a whole would still be legal
# under the HTTP specification even without this header.
# If a header cannot be parsed into a high-level model instance it will be
# provided as a `RawHeader`.
# If logging is enabled it is performed with the configured
# `error-logging-verbosity`.
illegal-header-warnings = on
# Configures the verbosity with which message (request or response) parsing
# errors are written to the application log.
#
# Supported settings:
# `off` : no log messages are produced
# `simple`: a condensed single-line message is logged
# `full` : the full error details (potentially spanning several lines) are logged
error-logging-verbosity = full
# limits for the number of different values per header type that the
# header cache will hold
header-cache {
default = 12
Content-MD5 = 0
Date = 0
If-Match = 0
If-Modified-Since = 0
If-None-Match = 0
If-Range = 0
If-Unmodified-Since = 0
User-Agent = 32
}
# no overrides by default, see `akka.http.parsing` for default values
}
}
@ -212,76 +144,8 @@ akka.http {
}
# Modify to tweak parsing settings on the client-side only.
#
# IMPORTANT:
# Please note that this section is replicated in `server` and `http-connection-pool`.
parsing {
# The limits for the various parts of the HTTP message parser.
max-uri-length = 2k
max-method-length = 16
max-response-reason-length = 64
max-header-name-length = 64
max-header-value-length = 8k
max-header-count = 64
max-chunk-ext-length = 256
max-chunk-size = 1m
# Maximum content length which should not be exceeded by incoming HttpRequests.
# For file uploads which use the entityBytes Source of an incoming HttpRequest it is safe to
# set this to a very high value (or to `infinite` if feeling very adventurous) as the streaming
# upload will be back-pressured properly by Akka Streams.
# Please note however that this setting is a global property, and is applied to all incoming requests,
# not only file uploads consumed in a streaming fashion, so pick this limit wisely.
max-content-length = 8m
# Sets the strictness mode for parsing request target URIs.
# The following values are defined:
#
# `strict`: RFC3986-compliant URIs are required,
# a 400 response is triggered on violations
#
# `relaxed`: all visible 7-Bit ASCII chars are allowed
#
# `relaxed-with-raw-query`: like `relaxed` but additionally
# the URI query is not parsed, but delivered as one raw string
# as the `key` value of a single Query structure element.
#
uri-parsing-mode = strict
# Enables/disables the logging of warning messages in case an incoming
# message (request or response) contains an HTTP header which cannot be
# parsed into its high-level model class due to incompatible syntax.
# Note that, independently of this settings, akka-http will accept messages
# with such headers as long as the message as a whole would still be legal
# under the HTTP specification even without this header.
# If a header cannot be parsed into a high-level model instance it will be
# provided as a `RawHeader`.
# If logging is enabled it is performed with the configured
# `error-logging-verbosity`.
illegal-header-warnings = on
# Configures the verbosity with which message (request or response) parsing
# errors are written to the application log.
#
# Supported settings:
# `off` : no log messages are produced
# `simple`: a condensed single-line message is logged
# `full` : the full error details (potentially spanning several lines) are logged
error-logging-verbosity = full
# limits for the number of different values per header type that the
# header cache will hold
header-cache {
default = 12
Content-MD5 = 0
Date = 0
If-Match = 0
If-Modified-Since = 0
If-None-Match = 0
If-Range = 0
If-Unmodified-Since = 0
User-Agent = 32
}
# no overrides by default, see `akka.http.parsing` for default values
}
}
@ -375,6 +239,17 @@ akka.http {
# IMPORTANT: Please note that this section is replicated in `client` and `server`.
parsing {
# no overrides by default, see `akka.http.parsing` for default values
}
}
}
# Modify to tweak default parsing settings.
#
# IMPORTANT:
# Please note that this sections settings can be overriden by the corresponding settings in:
# `akka.http.server.parsing`, `akka.http.client.parsing` or `akka.http.http-connection-pool.client.parsing`.
parsing {
# The limits for the various parts of the HTTP message parser.
max-uri-length = 2k
@ -443,7 +318,4 @@ akka.http {
User-Agent = 32
}
}
}
}
}

View file

@ -29,14 +29,15 @@ final case class ClientConnectionSettings(
}
object ClientConnectionSettings extends SettingsCompanion[ClientConnectionSettings]("akka.http.client") {
def fromSubConfig(c: Config) = {
def fromSubConfig(root: Config, inner: Config) = {
val c = inner.withFallback(root.getConfig(prefix))
apply(
c.getString("user-agent-header").toOption.map(`User-Agent`(_)),
c getFiniteDuration "connecting-timeout",
c getPotentiallyInfiniteDuration "idle-timeout",
c getIntBytes "request-header-size-hint",
SocketOptionSettings fromSubConfig c.getConfig("socket-options"),
ParserSettings fromSubConfig c.getConfig("parsing"))
SocketOptionSettings.fromSubConfig(root, c.getConfig("socket-options")),
ParserSettings.fromSubConfig(root, c.getConfig("parsing")))
}
/**

View file

@ -5,15 +5,14 @@
package akka.http
import java.lang.{ Iterable JIterable }
import akka.http.scaladsl.HttpsContext
import com.typesafe.config.Config
import scala.collection.immutable
import scala.concurrent.duration.Duration
import akka.japi.Util._
import akka.actor.ActorSystem
import akka.event.LoggingAdapter
import akka.http.impl.util._
import akka.io.Inet
import akka.http.scaladsl.HttpsContext
import com.typesafe.config.Config
import scala.concurrent.duration.Duration
final case class HostConnectionPoolSetup(host: String, port: Int, setup: ConnectionPoolSetup)
@ -46,14 +45,14 @@ final case class ConnectionPoolSettings(
}
object ConnectionPoolSettings extends SettingsCompanion[ConnectionPoolSettings]("akka.http.host-connection-pool") {
def fromSubConfig(c: Config) = {
def fromSubConfig(root: Config, c: Config) = {
apply(
c getInt "max-connections",
c getInt "max-retries",
c getInt "max-open-requests",
c getInt "pipelining-limit",
c getPotentiallyInfiniteDuration "idle-timeout",
ClientConnectionSettings fromSubConfig c.getConfig("client"))
ClientConnectionSettings.fromSubConfig(root, c.getConfig("client")))
}
/**

View file

@ -55,7 +55,8 @@ final case class ParserSettings(
}
object ParserSettings extends SettingsCompanion[ParserSettings]("akka.http.parsing") {
def fromSubConfig(c: Config) = {
def fromSubConfig(root: Config, inner: Config) = {
val c = inner.withFallback(root.getConfig(prefix))
val cacheConfig = c getConfig "header-cache"
apply(

View file

@ -45,7 +45,7 @@ object ServerSettings extends SettingsCompanion[ServerSettings]("akka.http.serve
}
implicit def timeoutsShortcut(s: ServerSettings): Timeouts = s.timeouts
def fromSubConfig(c: Config) = apply(
def fromSubConfig(root: Config, c: Config) = apply(
c.getString("server-header").toOption.map(Server(_)),
Timeouts(
c getPotentiallyInfiniteDuration "idle-timeout",
@ -57,7 +57,7 @@ object ServerSettings extends SettingsCompanion[ServerSettings]("akka.http.serve
c getBoolean "verbose-error-messages",
c getIntBytes "response-header-size-hint",
c getInt "backlog",
SocketOptionSettings fromSubConfig c.getConfig("socket-options"),
SocketOptionSettings.fromSubConfig(root, c.getConfig("socket-options")),
defaultHostHeader =
HttpHeader.parse("Host", c getString "default-host-header") match {
case HttpHeader.ParsingResult.Ok(x: Host, Nil) x
@ -65,7 +65,7 @@ object ServerSettings extends SettingsCompanion[ServerSettings]("akka.http.serve
val info = result.errors.head.withSummary("Configured `default-host-header` is illegal")
throw new ConfigurationException(info.formatPretty)
},
ParserSettings fromSubConfig c.getConfig("parsing"))
ParserSettings.fromSubConfig(root, c.getConfig("parsing")))
def apply(optionalSettings: Option[ServerSettings])(implicit actorRefFactory: ActorRefFactory): ServerSettings =
optionalSettings getOrElse apply(actorSystem)

View file

@ -15,7 +15,7 @@ import akka.actor.ActorSystem
/**
* INTERNAL API
*/
private[http] abstract class SettingsCompanion[T](prefix: String) {
private[http] abstract class SettingsCompanion[T](protected val prefix: String) {
private final val MaxCached = 8
private[this] var cache = ListMap.empty[ActorSystem, T]
@ -41,9 +41,9 @@ private[http] abstract class SettingsCompanion[T](prefix: String) {
.withFallback(defaultReference(getClass.getClassLoader)))
def apply(config: Config): T =
fromSubConfig(config getConfig prefix)
fromSubConfig(config, config getConfig prefix)
def fromSubConfig(c: Config): T
def fromSubConfig(root: Config, c: Config): T
}
private[http] object SettingsCompanion {

View file

@ -12,11 +12,11 @@ import akka.io.Inet.SocketOption
import com.typesafe.config.Config
private[http] object SocketOptionSettings {
def fromSubConfig(config: Config): immutable.Traversable[SocketOption] = {
def fromSubConfig(root: Config, c: Config): immutable.Traversable[SocketOption] = {
def so[T](setting: String)(f: (Config, String) T)(cons: T SocketOption): List[SocketOption] =
config.getString(setting) match {
c.getString(setting) match {
case "undefined" Nil
case x cons(f(config, setting)) :: Nil
case x cons(f(c, setting)) :: Nil
}
so("so-receive-buffer-size")(_ getIntBytes _)(Inet.SO.ReceiveBufferSize) :::

View file

@ -1,27 +0,0 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.impl.engine.client
import akka.stream.testkit.AkkaSpec
class ClientConfigurationSpec extends AkkaSpec {
"Reference configurations" should {
for {
(first, second) "akka.http.client" -> "akka.http.host-connection-pool.client" ::
"akka.http.client.parsing" -> "akka.http.host-connection-pool.client.parsing" ::
"akka.http.server.parsing" -> "akka.http.client.parsing" ::
Nil
} s"be consistent for: `$first` and `$second`" in {
configShouldBeEqual("akka.http.client.parsing", "akka.http.host-connection-pool.client.parsing")
}
}
private def configShouldBeEqual(first: String, second: String): Unit = {
val config = system.settings.config
withClue(s"Config [$first] did not equal [$second]!") {
config.getConfig(first) should ===(config.getConfig(second))
}
}
}

View file

@ -0,0 +1,141 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.impl.engine.client
import akka.actor.ActorSystem
import akka.http.{ ClientConnectionSettings, ConnectionPoolSettings, ServerSettings }
import akka.stream.testkit.AkkaSpec
import com.typesafe.config.ConfigFactory
class HttpConfigurationSpec extends AkkaSpec {
val On = true
val Off = false
"Reference configurations" should {
"have default client and server `parsing` settings" in {
ServerSettings(system).parserSettings.toString should ===(ClientConnectionSettings(system).parserSettings.toString)
}
"have default client and pool `parsing` settings" in {
ServerSettings(system).parserSettings.toString should ===(ConnectionPoolSettings(system).connectionSettings.parserSettings.toString)
}
"have default client and pool `client` settings" in {
ClientConnectionSettings(system).toString should ===(ConnectionPoolSettings(system).connectionSettings.toString)
}
"override value from `akka.http.parsing` by setting `akka.http.client.parsing`" in {
configuredSystem("""akka.http.client.parsing.illegal-header-warnings = off""") { sys
val client = ClientConnectionSettings(sys)
client.parserSettings.illegalHeaderWarnings should ===(Off)
val pool = ConnectionPoolSettings(sys)
pool.connectionSettings.parserSettings.illegalHeaderWarnings should ===(Off)
val server = ServerSettings(sys)
server.parserSettings.illegalHeaderWarnings should ===(On)
}
}
"override `akka.http.parsing` by setting `akka.http.host-connection-pool.client.parsing` setting" in {
configuredSystem("""akka.http.host-connection-pool.client.parsing.illegal-header-warnings = off""") { sys
val client = ClientConnectionSettings(sys)
client.parserSettings.illegalHeaderWarnings should ===(On)
val pool = ConnectionPoolSettings(sys)
pool.connectionSettings.parserSettings.illegalHeaderWarnings should ===(Off)
val server = ServerSettings(sys)
server.parserSettings.illegalHeaderWarnings should ===(On)
}
}
"set `akka.http.host-connection-pool.client.idle-timeout` only" in {
configuredSystem("""akka.http.host-connection-pool.client.idle-timeout = 1337s""") { sys
import scala.concurrent.duration._
val client = ClientConnectionSettings(sys)
client.idleTimeout should ===(60.seconds)
val pool = ConnectionPoolSettings(sys)
pool.connectionSettings.idleTimeout should ===(1337.seconds)
val server = ServerSettings(sys)
server.idleTimeout should ===(60.seconds) // no change, default akka.http.server.idle-timeout
}
}
"set `akka.http.server.idle-timeout` only" in {
configuredSystem("""akka.http.server.idle-timeout = 1337s""") { sys
import scala.concurrent.duration._
val client = ClientConnectionSettings(sys)
client.idleTimeout should ===(60.seconds)
val pool = ConnectionPoolSettings(sys)
pool.connectionSettings.idleTimeout should ===(60.seconds)
val server = ServerSettings(sys)
server.idleTimeout should ===(1337.seconds)
}
}
"change parser settings for all by setting `akka.http.parsing`" in {
configuredSystem("""akka.http.parsing.illegal-header-warnings = off""") { sys
val client = ClientConnectionSettings(sys)
client.parserSettings.illegalHeaderWarnings should ===(Off)
val pool = ConnectionPoolSettings(sys)
pool.connectionSettings.parserSettings.illegalHeaderWarnings should ===(Off)
val server = ServerSettings(sys)
server.parserSettings.illegalHeaderWarnings should ===(Off)
}
}
"change parser settings for all by setting `akka.http.parsing`, unless client/server override it" in {
configuredSystem("""
akka.http {
parsing.illegal-header-warnings = off
server.parsing.illegal-header-warnings = on
client.parsing.illegal-header-warnings = on // also affects host-connection-pool.client
}""") { sys
val client = ClientConnectionSettings(sys)
client.parserSettings.illegalHeaderWarnings should ===(On)
val pool = ConnectionPoolSettings(sys)
pool.connectionSettings.parserSettings.illegalHeaderWarnings should ===(On)
val server = ServerSettings(sys)
server.parserSettings.illegalHeaderWarnings should ===(On)
}
}
"change parser settings for all by setting `akka.http.parsing`, unless all override it" in {
configuredSystem("""
akka.http {
parsing.illegal-header-warnings = off
server.parsing.illegal-header-warnings = on
client.parsing.illegal-header-warnings = on
host-connection-pool.client.parsing.illegal-header-warnings = off
}""") { sys
val client = ClientConnectionSettings(sys)
client.parserSettings.illegalHeaderWarnings should ===(On)
val pool = ConnectionPoolSettings(sys)
pool.connectionSettings.parserSettings.illegalHeaderWarnings should ===(Off)
val server = ServerSettings(sys)
server.parserSettings.illegalHeaderWarnings should ===(On)
}
}
}
def configuredSystem(overrides: String)(block: ActorSystem Unit) = {
val config = ConfigFactory.parseString(overrides).withFallback(ConfigFactory.load())
// we go via ActorSystem in order to hit the settings caching infrastructure
val sys = ActorSystem("config-testing", config)
try block(sys) finally sys.shutdown()
}
}

View file

@ -18,7 +18,7 @@ case class RoutingSettings(
fileIODispatcher: String)
object RoutingSettings extends SettingsCompanion[RoutingSettings]("akka.http.routing") {
def fromSubConfig(c: Config) = apply(
def fromSubConfig(root: Config, c: Config) = apply(
c getBoolean "verbose-error-messages",
c getBoolean "file-get-conditional",
c getBoolean "render-vanity-footer",