=htc #16807 introduce akka.http.parsing.error-logging-verbosity setting
This commit is contained in:
parent
37aa2cb886
commit
14399f71f5
5 changed files with 58 additions and 30 deletions
|
|
@ -147,8 +147,19 @@ akka.http {
|
|||
# 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 {
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import akka.stream.scaladsl._
|
|||
import akka.stream.scaladsl.OperationAttributes._
|
||||
import akka.http.model.{ IllegalResponseException, HttpMethod, HttpRequest, HttpResponse }
|
||||
import akka.http.engine.rendering.{ RequestRenderingContext, HttpRequestRendererFactory }
|
||||
import akka.http.engine.parsing.{ ParserOutput, HttpHeaderParser, HttpResponseParser }
|
||||
import akka.http.engine.parsing._
|
||||
import akka.http.util._
|
||||
|
||||
/**
|
||||
|
|
@ -58,10 +58,9 @@ private[http] object HttpClient {
|
|||
|
||||
// the initial header parser we initially use for every connection,
|
||||
// will not be mutated, all "shared copy" parsers copy on first-write into the header cache
|
||||
val rootParser = new HttpResponseParser(
|
||||
parserSettings,
|
||||
HttpHeaderParser(parserSettings) { errorInfo ⇒
|
||||
if (parserSettings.illegalHeaderWarnings) log.warning(errorInfo.withSummaryPrepended("Illegal response header").formatPretty)
|
||||
val rootParser = new HttpResponseParser(parserSettings, HttpHeaderParser(parserSettings) { info ⇒
|
||||
if (parserSettings.illegalHeaderWarnings)
|
||||
logParsingError(info withSummaryPrepended "Illegal response header", log, parserSettings.errorLoggingVerbosity)
|
||||
})
|
||||
|
||||
val requestRendererFactory = new HttpRequestRendererFactory(userAgentHeader, requestHeaderSizeHint, log)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
package akka.http.engine.parsing
|
||||
|
||||
import java.util.Locale
|
||||
import com.typesafe.config.Config
|
||||
import scala.collection.JavaConverters._
|
||||
import akka.http.model.{ StatusCode, HttpMethod, Uri }
|
||||
|
|
@ -21,6 +22,7 @@ final case class ParserSettings(
|
|||
maxChunkSize: Int,
|
||||
uriParsingMode: Uri.ParsingMode,
|
||||
illegalHeaderWarnings: Boolean,
|
||||
errorLoggingVerbosity: ParserSettings.ErrorLoggingVerbosity,
|
||||
headerValueCacheLimits: Map[String, Int],
|
||||
customMethods: String ⇒ Option[HttpMethod],
|
||||
customStatusCodes: Int ⇒ Option[StatusCode]) extends HttpHeaderParser.Settings {
|
||||
|
|
@ -66,9 +68,25 @@ object ParserSettings extends SettingsCompanion[ParserSettings]("akka.http.parsi
|
|||
c getIntBytes "max-chunk-size",
|
||||
Uri.ParsingMode(c getString "uri-parsing-mode"),
|
||||
c getBoolean "illegal-header-warnings",
|
||||
ErrorLoggingVerbosity(c getString "error-logging-verbosity"),
|
||||
cacheConfig.entrySet.asScala.map(kvp ⇒ kvp.getKey -> cacheConfig.getInt(kvp.getKey))(collection.breakOut),
|
||||
_ ⇒ None,
|
||||
_ ⇒ None)
|
||||
}
|
||||
|
||||
sealed trait ErrorLoggingVerbosity
|
||||
object ErrorLoggingVerbosity {
|
||||
case object Off extends ErrorLoggingVerbosity
|
||||
case object Simple extends ErrorLoggingVerbosity
|
||||
case object Full extends ErrorLoggingVerbosity
|
||||
|
||||
def apply(string: String): ErrorLoggingVerbosity =
|
||||
string.toLowerCase(Locale.ROOT) match {
|
||||
case "off" ⇒ Off
|
||||
case "simple" ⇒ Simple
|
||||
case "full" ⇒ Full
|
||||
case x ⇒ throw new IllegalArgumentException(s"[$x] is not a legal `error-logging-verbosity` setting")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,15 +6,16 @@ package akka.http.engine
|
|||
|
||||
import java.lang.{ StringBuilder ⇒ JStringBuilder }
|
||||
import scala.annotation.tailrec
|
||||
import akka.event.LoggingAdapter
|
||||
import akka.util.ByteString
|
||||
import akka.http.model.{ ErrorInfo, StatusCode, StatusCodes }
|
||||
import akka.http.util.SingletonException
|
||||
|
||||
package object parsing {
|
||||
|
||||
/**
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
package object parsing {
|
||||
|
||||
private[http] def escape(c: Char): String = c match {
|
||||
case '\t' ⇒ "\\t"
|
||||
case '\r' ⇒ "\\r"
|
||||
|
|
@ -23,25 +24,24 @@ package object parsing {
|
|||
case x ⇒ x.toString
|
||||
}
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[http] def byteChar(input: ByteString, ix: Int): Char = byteAt(input, ix).toChar
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[http] def byteAt(input: ByteString, ix: Int): Byte =
|
||||
if (ix < input.length) input(ix) else throw NotEnoughDataException
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
*/
|
||||
private[http] def asciiString(input: ByteString, start: Int, end: Int): String = {
|
||||
@tailrec def build(ix: Int = start, sb: JStringBuilder = new JStringBuilder(end - start)): String =
|
||||
if (ix == end) sb.toString else build(ix + 1, sb.append(input(ix).toChar))
|
||||
if (start == end) "" else build()
|
||||
}
|
||||
|
||||
private[http] def logParsingError(info: ErrorInfo, log: LoggingAdapter,
|
||||
setting: ParserSettings.ErrorLoggingVerbosity): Unit =
|
||||
setting match {
|
||||
case ParserSettings.ErrorLoggingVerbosity.Off ⇒ // nothing to do
|
||||
case ParserSettings.ErrorLoggingVerbosity.Simple ⇒ log.warning(info.summary)
|
||||
case ParserSettings.ErrorLoggingVerbosity.Full ⇒ log.warning(info.formatPretty)
|
||||
}
|
||||
}
|
||||
|
||||
package parsing {
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ import akka.stream.stage.PushPullStage
|
|||
import akka.stream.scaladsl.OperationAttributes._
|
||||
import akka.stream.scaladsl._
|
||||
import akka.stream._
|
||||
import akka.http.engine.parsing.{ HttpHeaderParser, HttpRequestParser }
|
||||
import akka.http.engine.parsing._
|
||||
import akka.http.engine.rendering.{ ResponseRenderingContext, HttpResponseRendererFactory }
|
||||
import akka.http.engine.parsing.ParserOutput._
|
||||
import akka.http.engine.TokenSourceActor
|
||||
import akka.http.model._
|
||||
import akka.http.util._
|
||||
import ParserOutput._
|
||||
|
||||
/**
|
||||
* INTERNAL API
|
||||
|
|
@ -50,14 +50,14 @@ private[http] object HttpServer {
|
|||
|
||||
def serverBlueprint(settings: ServerSettings,
|
||||
log: LoggingAdapter)(implicit mat: FlowMaterializer): Graph[HttpServerPorts, Unit] = {
|
||||
import settings._
|
||||
|
||||
// the initial header parser we initially use for every connection,
|
||||
// will not be mutated, all "shared copy" parsers copy on first-write into the header cache
|
||||
val rootParser = new HttpRequestParser(
|
||||
settings.parserSettings,
|
||||
settings.rawRequestUriHeader,
|
||||
HttpHeaderParser(settings.parserSettings) { errorInfo ⇒
|
||||
if (settings.parserSettings.illegalHeaderWarnings) log.warning(errorInfo.withSummaryPrepended("Illegal request header").formatPretty)
|
||||
val rootParser = new HttpRequestParser(parserSettings, rawRequestUriHeader,
|
||||
HttpHeaderParser(parserSettings) { info ⇒
|
||||
if (parserSettings.illegalHeaderWarnings)
|
||||
logParsingError(info withSummaryPrepended "Illegal request header", log, parserSettings.errorLoggingVerbosity)
|
||||
})
|
||||
|
||||
val responseRendererFactory = new HttpResponseRendererFactory(settings.serverHeader, settings.responseHeaderSizeHint, log)
|
||||
|
|
@ -149,7 +149,7 @@ private[http] object HttpServer {
|
|||
this.requestStart = requestStart
|
||||
ctx.changeCompletionHandling(waitingForApplicationResponseCompletionHandling)
|
||||
waitingForApplicationResponse
|
||||
case (ctx, _, MessageStartError(status, info)) ⇒ finishWithError(ctx, "request", status, info)
|
||||
case (ctx, _, MessageStartError(status, info)) ⇒ finishWithError(ctx, status, info)
|
||||
case _ ⇒ throw new IllegalStateException
|
||||
}
|
||||
|
||||
|
|
@ -181,12 +181,13 @@ private[http] object HttpServer {
|
|||
onUpstreamFailure = {
|
||||
case (ctx, _, EntityStreamException(errorInfo)) ⇒
|
||||
// the application has forwarded a request entity stream error to the response stream
|
||||
finishWithError(ctx, "request", StatusCodes.BadRequest, errorInfo)
|
||||
finishWithError(ctx, StatusCodes.BadRequest, errorInfo)
|
||||
case (ctx, _, error) ⇒ { ctx.fail(error); SameState }
|
||||
})
|
||||
|
||||
def finishWithError(ctx: MergeLogicContextBase, target: String, status: StatusCode, info: ErrorInfo): State[Any] = {
|
||||
log.warning("Illegal {}, responding with status '{}': {}", target, status, info.formatPretty)
|
||||
def finishWithError(ctx: MergeLogicContextBase, status: StatusCode, info: ErrorInfo): State[Any] = {
|
||||
logParsingError(info withSummaryPrepended s"Illegal request, responding with status '$status'",
|
||||
log, settings.parserSettings.errorLoggingVerbosity)
|
||||
val msg = if (settings.verboseErrorMessages) info.formatPretty else info.summary
|
||||
// FIXME this is a workaround that is supposed to be solved by issue #16753
|
||||
ctx match {
|
||||
|
|
@ -195,7 +196,6 @@ private[http] object HttpServer {
|
|||
fullCtx.emit(ResponseRenderingContext(HttpResponse(status, entity = msg), closeRequested = true))
|
||||
case other ⇒ throw new IllegalStateException(s"Unexpected MergeLogicContext [${other.getClass.getName}]")
|
||||
}
|
||||
//
|
||||
finish(ctx)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue