diff --git a/akka-docs-dev/rst/java/http/http-model.rst b/akka-docs-dev/rst/java/http/http-model.rst index ce52320bfb..9be44c5f3b 100644 --- a/akka-docs-dev/rst/java/http/http-model.rst +++ b/akka-docs-dev/rst/java/http/http-model.rst @@ -254,3 +254,14 @@ Parsing / Rendering Parsing and rendering of HTTP data structures is heavily optimized and for most types there's currently no public API 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. + + 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. \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/common/http-model.rst b/akka-docs-dev/rst/scala/http/common/http-model.rst index 39dbd13d79..6dce80d3d9 100644 --- a/akka-docs-dev/rst/scala/http/common/http-model.rst +++ b/akka-docs-dev/rst/scala/http/common/http-model.rst @@ -250,3 +250,14 @@ Parsing / Rendering Parsing and rendering of HTTP data structures is heavily optimized and for most types there's currently no public API 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. + + 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. \ No newline at end of file diff --git a/akka-http-core/src/main/resources/reference.conf b/akka-http-core/src/main/resources/reference.conf index 4cd7e35465..558c216f99 100644 --- a/akka-http-core/src/main/resources/reference.conf +++ b/akka-http-core/src/main/resources/reference.conf @@ -88,7 +88,77 @@ akka.http { } # Modify to tweak parsing settings on the server-side only. - parsing = ${akka.http.parsing} + # + # 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 + } + } } client { @@ -142,7 +212,77 @@ akka.http { } # Modify to tweak parsing settings on the client-side only. - parsing = ${akka.http.parsing} + # + # 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 + } + } } host-connection-pool { @@ -179,81 +319,131 @@ akka.http { idle-timeout = 30 s # Modify to tweak client settings for host connection pools only. - client = ${akka.http.client} - } - - # The (default) configuration of the HTTP message parser for the server and the client. - # IMPORTANT: These settings (i.e. children of `akka.http.parsing`) can't be directly - # overridden in `application.conf` to change the parser settings for client and server - # at the same time. Instead, override the concrete settings beneath - # `akka.http.server.parsing` and `akka.http.client.parsing` - # where these settings are copied to. - 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 + # IMPORTANT: + # Please note that this section mirrors `akka.http.client` however is used only for pool-based APIs, + # such as `Http().superPool` or `Http().singleRequest`. + client = { + # The default value of the `User-Agent` header to produce if no + # explicit `User-Agent`-header was included in a request. + # If this value is the empty string and no header was included in + # the request, no `User-Agent` header will be rendered at all. + user-agent-header = akka-http/${akka.version} - # 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 + # The time period within which the TCP connecting process must be completed. + connecting-timeout = 10s - # 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 + # The time after which an idle connection will be automatically closed. + # Set to `infinite` to completely disable idle timeouts. + idle-timeout = 60 s - # 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 + # The initial size of the buffer to render the request headers in. + # Can be used for fine-tuning request rendering performance but probably + # doesn't have to be fiddled with in most applications. + request-header-size-hint = 512 + + # The proxy configurations to be used for requests with the specified + # scheme. + proxy { + # Proxy settings for unencrypted HTTP requests + # Set to 'none' to always connect directly, 'default' to use the system + # settings as described in http://docs.oracle.com/javase/6/docs/technotes/guides/net/proxies.html + # or specify the proxy host, port and non proxy hosts as demonstrated + # in the following example: + # http { + # host = myproxy.com + # port = 8080 + # non-proxy-hosts = ["*.direct-access.net"] + # } + http = default + + # Proxy settings for HTTPS requests (currently unsupported) + https = default + } + + # Socket options to set for the listening socket. If a setting is left + # undefined, it will use whatever the default on the system is. + socket-options { + so-receive-buffer-size = undefined + so-send-buffer-size = undefined + so-reuse-address = undefined + so-traffic-class = undefined + tcp-keep-alive = undefined + tcp-oob-inline = undefined + tcp-no-delay = undefined + } + + + # IMPORTANT: Please note that this section is replicated in `client` and `server`. + 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 + } + } } } + } diff --git a/akka-http-core/src/main/scala/akka/http/ConnectionPoolSettings.scala b/akka-http-core/src/main/scala/akka/http/ConnectionPoolSettings.scala index 7161854889..95f8a2c0b3 100644 --- a/akka-http-core/src/main/scala/akka/http/ConnectionPoolSettings.scala +++ b/akka-http-core/src/main/scala/akka/http/ConnectionPoolSettings.scala @@ -42,7 +42,7 @@ final case class ConnectionPoolSettings( require(maxRetries >= 0, "max-retries must be >= 0") require(maxOpenRequests > 0 && (maxOpenRequests & (maxOpenRequests - 1)) == 0, "max-open-requests must be a power of 2 > 0") require(pipeliningLimit > 0, "pipelining-limit must be > 0") - require(idleTimeout >= Duration.Zero, "idleTimeout must be >= 0") + require(idleTimeout >= Duration.Zero, "idle-timeout must be >= 0") } object ConnectionPoolSettings extends SettingsCompanion[ConnectionPoolSettings]("akka.http.host-connection-pool") { diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala index b94926ecd8..e231f5013a 100644 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala +++ b/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala @@ -37,6 +37,8 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E // SYNCHRONIZED ACCESS ONLY! private[this] var _defaultClientHttpsContext: HttpsContext = _ + // ** SERVER ** // + /** * Creates a [[Source]] of [[IncomingConnection]] instances which represents a prospective HTTP server binding * on the given `endpoint`. @@ -52,6 +54,9 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E * * If no ``port`` is explicitly given (or the port value is negative) the protocol's default port will be used, * which is 80 for HTTP and 443 for HTTPS. + * + * To configure additional settings for a server started using this method, + * use the `akka.http.server` config section or pass in a [[ServerSettings]] explicitly. */ def bind(interface: String, port: Int = -1, settings: ServerSettings = ServerSettings(system), @@ -76,6 +81,9 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E * * The number of concurrently accepted connections can be configured by overriding * the `akka.http.server.max-connections` setting. + * + * To configure additional settings for a server started using this method, + * use the `akka.http.server` config section or pass in a [[ServerSettings]] explicitly. */ def bindAndHandle(handler: Flow[HttpRequest, HttpResponse, Any], interface: String, port: Int = -1, @@ -114,6 +122,9 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E * * The number of concurrently accepted connections can be configured by overriding * the `akka.http.server.max-connections` setting. + * + * To configure additional settings for a server started using this method, + * use the `akka.http.server` config section or pass in a [[ServerSettings]] explicitly. */ def bindAndHandleSync(handler: HttpRequest ⇒ HttpResponse, interface: String, port: Int = -1, @@ -128,6 +139,9 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E * * The number of concurrently accepted connections can be configured by overriding * the `akka.http.server.max-connections` setting. + * + * To configure additional settings for a server started using this method, + * use the `akka.http.server` config section or pass in a [[ServerSettings]] explicitly. */ def bindAndHandleAsync(handler: HttpRequest ⇒ Future[HttpResponse], interface: String, port: Int = -1, @@ -140,8 +154,10 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E type ServerLayer = Http.ServerLayer /** - * Constructs a [[ServerLayer]] stage using the configured default [[ServerSettings]]. The returned [[BidiFlow]] - * can only be materialized once. + * Constructs a [[ServerLayer]] stage using the configured default [[ServerSettings]], + * configured using the `akka.http.server` config section. + * + * The returned [[BidiFlow]] can only be materialized once. */ def serverLayer()(implicit mat: Materializer): ServerLayer = serverLayer(ServerSettings(system)) @@ -155,9 +171,14 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E log: LoggingAdapter = system.log)(implicit mat: Materializer): ServerLayer = HttpServerBluePrint(settings, remoteAddress, log) + // ** CLIENT ** // + /** * Creates a [[Flow]] representing a prospective HTTP client connection to the given endpoint. * Every materialization of the produced flow will attempt to establish a new outgoing connection. + * + * To configure additional settings for requests made using this method, + * use the `akka.http.client` config section or pass in a [[ClientConnectionSettings]] explicitly. */ def outgoingConnection(host: String, port: Int = 80, localAddress: Option[InetSocketAddress] = None, @@ -170,6 +191,9 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E * * If an explicit [[HttpsContext]] is given then it rather than the configured default [[HttpsContext]] will be used * for encryption on the connection. + * + * To configure additional settings for requests made using this method, + * use the `akka.http.client` config section or pass in a [[ClientConnectionSettings]] explicitly. */ def outgoingConnectionTls(host: String, port: Int = 443, localAddress: Option[InetSocketAddress] = None, @@ -196,7 +220,8 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E type ClientLayer = Http.ClientLayer /** - * Constructs a [[ClientLayer]] stage using the configured default [[ClientConnectionSettings]]. + * Constructs a [[ClientLayer]] stage using the configured default [[ClientConnectionSettings]], + * configured using the `akka.http.client` config section. */ def clientLayer(hostHeader: Host): ClientLayer = clientLayer(hostHeader, ClientConnectionSettings(system)) @@ -209,6 +234,8 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E log: LoggingAdapter = system.log): ClientLayer = OutgoingConnectionBlueprint(hostHeader, settings, log) + // ** CONNECTION POOL ** // + /** * Starts a new connection pool to the given host and configuration and returns a [[Flow]] which dispatches * the requests from all its materializations across this pool. @@ -222,6 +249,9 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E * response for A. * In order to allow for easy response-to-request association the flow takes in a custom, opaque context * object of type ``T`` from the application which is emitted together with the corresponding response. + * + * To configure additional settings for the pool (and requests made using it), + * use the `akka.http.host-connection-pool` config section or pass in a [[ConnectionPoolSettings]] explicitly. */ def newHostConnectionPool[T](host: String, port: Int = 80, settings: ConnectionPoolSettings = ConnectionPoolSettings(system), @@ -235,6 +265,9 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E * * If an explicit [[HttpsContext]] is given then it rather than the configured default [[HttpsContext]] will be used * for encryption on the connections. + * + * To configure additional settings for the pool (and requests made using it), + * use the `akka.http.host-connection-pool` config section or pass in a [[ConnectionPoolSettings]] explicitly. */ def newHostConnectionPoolTls[T](host: String, port: Int = 443, settings: ConnectionPoolSettings = ConnectionPoolSettings(system), @@ -280,6 +313,9 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E * response for A. * In order to allow for easy response-to-request association the flow takes in a custom, opaque context * object of type ``T`` from the application which is emitted together with the corresponding response. + * + * To configure additional settings for the pool (and requests made using it), + * use the `akka.http.host-connection-pool` config section or pass in a [[ConnectionPoolSettings]] explicitly. */ def cachedHostConnectionPool[T](host: String, port: Int = 80, settings: ConnectionPoolSettings = ConnectionPoolSettings(system), @@ -294,6 +330,9 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E * * If an explicit [[HttpsContext]] is given then it rather than the configured default [[HttpsContext]] will be used * for encryption on the connections. + * + * To configure additional settings for the pool (and requests made using it), + * use the `akka.http.host-connection-pool` config section or pass in a [[ConnectionPoolSettings]] explicitly. */ def cachedHostConnectionPoolTls[T](host: String, port: Int = 443, settings: ConnectionPoolSettings = ConnectionPoolSettings(system), @@ -338,6 +377,9 @@ class HttpExt(config: Config)(implicit system: ActorSystem) extends akka.actor.E * response for A. * In order to allow for easy response-to-request association the flow takes in a custom, opaque context * object of type ``T`` from the application which is emitted together with the corresponding response. + * + * To configure additional settings for the pool (and requests made using it), + * use the `akka.http.host-connection-pool` config section or pass in a [[ConnectionPoolSettings]] explicitly. */ def superPool[T](settings: ConnectionPoolSettings = ConnectionPoolSettings(system), httpsContext: Option[HttpsContext] = None, diff --git a/akka-http-core/src/test/scala/akka/http/impl/engine/client/ClientConfigurationSpec.scala b/akka-http-core/src/test/scala/akka/http/impl/engine/client/ClientConfigurationSpec.scala new file mode 100644 index 0000000000..3966940314 --- /dev/null +++ b/akka-http-core/src/test/scala/akka/http/impl/engine/client/ClientConfigurationSpec.scala @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +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)) + } + } +} \ No newline at end of file