From 2f76180a9ad5f7fb9a6d8fe30c409e4ffe8aad26 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 26 Aug 2025 15:37:50 +0100 Subject: [PATCH 01/11] Update validate-and-test.yml updates more changes remove Java 8 specific code Update StringTools.scala use Java 17 in build snapshot dependency start to remove use of PekkoSSLConfig update Update ConnectionContext.scala Update remove-deprecated-methods.excludes try SNI matcher Update ClientServerSpec.scala try to fix test remove bindAndHandleAsync remove use of deprecated TLS methods begin removing deprecations Update HttpChallenge.java Update CorsSettings.scala encoder Update CookieDirectivesExamplesTest.java Update SizeLimitSpec.scala compile issues Update Encoder.scala mima Update Encoder.scala Update Encoder.scala more changes issues Update SizeLimitSpec.scala rename file more more Update HeaderDirectivesSpec.scala Update remove-deprecated-methods.excludes more move mima unused imports Update ServerSettingsImpl.scala Update ServerSettings.scala start to remove use of PekkoSSLConfig update Update ConnectionContext.scala Update remove-deprecated-methods.excludes try SNI matcher Update ClientServerSpec.scala try to fix test Update remove-deprecated-methods.excludes remove use of deprecated TLS methods Update remove-deprecated-methods.excludes HttpChallenge more deprecations javafmt Delete remove-deprecated-methods.excludes --- build.sbt | 1 + http/src/main/resources/reference.conf | 3 +++ project/PekkoCoreDependency.scala | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/build.sbt b/build.sbt index 44dd60fd1..aeb656c27 100644 --- a/build.sbt +++ b/build.sbt @@ -22,6 +22,7 @@ import com.lightbend.paradox.apidoc.ApidocPlugin.autoImport.apidocRootPackage sourceDistName := "apache-pekko-http" sourceDistIncubating := false +ThisBuild / resolvers += Resolver.ApacheMavenSnapshotsRepo ThisBuild / reproducibleBuildsCheckResolver := Resolver.ApacheMavenStagingRepo addCommandAlias("verifyCodeStyle", "scalafmtCheckAll; scalafmtSbtCheck; +headerCheckAll; javafmtCheckAll") diff --git a/http/src/main/resources/reference.conf b/http/src/main/resources/reference.conf index e3f50b7d9..43ce790f6 100644 --- a/http/src/main/resources/reference.conf +++ b/http/src/main/resources/reference.conf @@ -7,6 +7,9 @@ # This is the reference config file that contains all the default settings. # Make your edits/overrides in your application.conf. +# Disable version checks (should not be merged to main branch) +pekko.fail-mixed-versions=off + pekko.http { routing { # Enables/disables the returning of more detailed error messages to the diff --git a/project/PekkoCoreDependency.scala b/project/PekkoCoreDependency.scala index 992921fe7..fad10814b 100644 --- a/project/PekkoCoreDependency.scala +++ b/project/PekkoCoreDependency.scala @@ -20,5 +20,5 @@ import com.github.pjfanning.pekkobuild.PekkoDependency object PekkoCoreDependency extends PekkoDependency { override val checkProject: String = "pekko-cluster-sharding-typed" override val module: Option[String] = None - override val currentVersion: String = "1.1.5" + override val currentVersion: String = "2.0.0-M0+104-5db362f5-SNAPSHOT" } From 3641efd5384cafb5ec56a34a20d568136755212a Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Thu, 4 Sep 2025 22:00:31 +0100 Subject: [PATCH 02/11] Update ExceptionHandlerBuilder.java --- .../http/javadsl/server/ExceptionHandlerBuilder.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/http/src/main/java/org/apache/pekko/http/javadsl/server/ExceptionHandlerBuilder.java b/http/src/main/java/org/apache/pekko/http/javadsl/server/ExceptionHandlerBuilder.java index ab0e7a5aa..7ec3d606b 100644 --- a/http/src/main/java/org/apache/pekko/http/javadsl/server/ExceptionHandlerBuilder.java +++ b/http/src/main/java/org/apache/pekko/http/javadsl/server/ExceptionHandlerBuilder.java @@ -13,7 +13,8 @@ package org.apache.pekko.http.javadsl.server; -import org.apache.pekko.japi.pf.FI; +import org.apache.pekko.japi.function.Function; +import org.apache.pekko.japi.function.Predicate; import org.apache.pekko.japi.pf.PFBuilder; public class ExceptionHandlerBuilder { @@ -35,7 +36,7 @@ private ExceptionHandlerBuilder(PFBuilder delegate) { * @return a builder with the case statement added */ public

ExceptionHandlerBuilder match( - final Class

type, FI.Apply apply) { + final Class

type, final Function apply) { delegate.match(type, apply); return this; } @@ -50,7 +51,7 @@ public

ExceptionHandlerBuilder match( * @return a builder with the case statement added */ public

ExceptionHandlerBuilder match( - final Class

type, final FI.TypedPredicate

predicate, final FI.Apply apply) { + final Class

type, final Predicate

predicate, final Function apply) { delegate.match(type, predicate, apply); return this; } @@ -63,7 +64,7 @@ public

ExceptionHandlerBuilder match( * @return a builder with the case statement added */ public

ExceptionHandlerBuilder matchEquals( - final P object, final FI.Apply apply) { + final P object, final Function apply) { delegate.matchEquals(object, apply); return this; } @@ -74,7 +75,7 @@ public

ExceptionHandlerBuilder matchEquals( * @param apply an action to apply to the argument * @return a builder with the case statement added */ - public ExceptionHandlerBuilder matchAny(final FI.Apply apply) { + public ExceptionHandlerBuilder matchAny(final Function apply) { delegate.matchAny(apply); return this; } From f7eaff7ebdbd4b2b13ce21e2a560fca66276e716 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 16 Sep 2025 00:42:38 +0100 Subject: [PATCH 03/11] Update PekkoCoreDependency.scala --- project/PekkoCoreDependency.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/PekkoCoreDependency.scala b/project/PekkoCoreDependency.scala index fad10814b..76280cadb 100644 --- a/project/PekkoCoreDependency.scala +++ b/project/PekkoCoreDependency.scala @@ -20,5 +20,5 @@ import com.github.pjfanning.pekkobuild.PekkoDependency object PekkoCoreDependency extends PekkoDependency { override val checkProject: String = "pekko-cluster-sharding-typed" override val module: Option[String] = None - override val currentVersion: String = "2.0.0-M0+104-5db362f5-SNAPSHOT" + override val currentVersion: String = "2.0.0-M0+136-94ae86fa-SNAPSHOT" } From 25136c951a37b7e6df255aa204b8429310eab12c Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 17 Aug 2025 20:59:59 +0100 Subject: [PATCH 04/11] remove use of ActorMaterializer --- .../pekko/http/impl/util/StageLoggingWithOverride.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala index c7ace934a..10e26b4fc 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala @@ -20,7 +20,7 @@ import org.apache.pekko import pekko.annotation.InternalApi import pekko.stream.stage.GraphStageLogic import pekko.event.{ LogSource, LoggingAdapter, NoLogging } -import pekko.stream.ActorMaterializer +import pekko.stream.Materializer // TODO Try to reconcile with what Pekko provides in StageLogging. // We thought this could be removed when https://github.com/akka/akka/issues/18793 had been implemented @@ -44,8 +44,8 @@ private[pekko] trait StageLoggingWithOverride extends GraphStageLogic { logOverride match { case DefaultNoLogging => materializer match { - case a: ActorMaterializer => pekko.event.Logging(a.system, logSource)(LogSource.fromClass) - case _ => NoLogging + case m: Materializer => pekko.event.Logging(m.system, logSource)(LogSource.fromClass) + case _ => NoLogging } case x => x } From 1e94ec43c96a9cdbcd293d737fb39a763937b774 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 17 Aug 2025 22:05:36 +0100 Subject: [PATCH 05/11] Update StageLoggingWithOverride.scala --- .../pekko/http/impl/util/StageLoggingWithOverride.scala | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala index 10e26b4fc..dd6592eb0 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/util/StageLoggingWithOverride.scala @@ -19,8 +19,7 @@ package org.apache.pekko.http.impl.util import org.apache.pekko import pekko.annotation.InternalApi import pekko.stream.stage.GraphStageLogic -import pekko.event.{ LogSource, LoggingAdapter, NoLogging } -import pekko.stream.Materializer +import pekko.event.{ LogSource, LoggingAdapter } // TODO Try to reconcile with what Pekko provides in StageLogging. // We thought this could be removed when https://github.com/akka/akka/issues/18793 had been implemented @@ -43,10 +42,7 @@ private[pekko] trait StageLoggingWithOverride extends GraphStageLogic { _log = logOverride match { case DefaultNoLogging => - materializer match { - case m: Materializer => pekko.event.Logging(m.system, logSource)(LogSource.fromClass) - case _ => NoLogging - } + pekko.event.Logging(materializer.system, logSource)(LogSource.fromClass) case x => x } case _ => From 1ea4a5fb51632f3dc12b0adb5e415bac6190fc3c Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Tue, 16 Sep 2025 01:11:40 +0100 Subject: [PATCH 06/11] old way to get subscriptionTimeoutSettings not accessible any more --- .../pekko/http/impl/engine/server/HttpServerBluePrint.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/server/HttpServerBluePrint.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/server/HttpServerBluePrint.scala index c9603f25a..c0f7f6891 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/server/HttpServerBluePrint.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/server/HttpServerBluePrint.scala @@ -721,12 +721,11 @@ private[http] object HttpServerBluePrint { }) private var activeTimers = 0 - private val timeout: FiniteDuration = { + private val timeout: FiniteDuration = inheritedAttributes.get[ActorAttributes.StreamSubscriptionTimeout] match { case Some(attr) => attr.timeout case None => 5.minutes // should not happen } - } private def addTimeout(s: SubscriptionTimeout): Unit = { if (activeTimers == 0) setKeepGoing(true) activeTimers += 1 From d51bc06640dba74581025cee3efeefe2437ab7f7 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 27 Sep 2025 15:50:06 +0100 Subject: [PATCH 07/11] Update PekkoCoreDependency.scala --- project/PekkoCoreDependency.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/PekkoCoreDependency.scala b/project/PekkoCoreDependency.scala index 76280cadb..78036b882 100644 --- a/project/PekkoCoreDependency.scala +++ b/project/PekkoCoreDependency.scala @@ -20,5 +20,5 @@ import com.github.pjfanning.pekkobuild.PekkoDependency object PekkoCoreDependency extends PekkoDependency { override val checkProject: String = "pekko-cluster-sharding-typed" override val module: Option[String] = None - override val currentVersion: String = "2.0.0-M0+136-94ae86fa-SNAPSHOT" + override val currentVersion: String = "2.0.0-M0+180-84daa526-SNAPSHOT" } From b4cb576180b785079564e3892622b0844ffb6f69 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 27 Sep 2025 16:13:42 +0100 Subject: [PATCH 08/11] mima --- .../uptake-pekko-core-2.0.0.excludes | 19 +++++++++++++++++ ...efactor-javadsl-exception-handler.excludes | 21 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 http-core/src/main/mima-filters/2.0.x.backwards.excludes/uptake-pekko-core-2.0.0.excludes create mode 100644 http/src/main/mima-filters/2.0.x.backwards.excludes/refactor-javadsl-exception-handler.excludes diff --git a/http-core/src/main/mima-filters/2.0.x.backwards.excludes/uptake-pekko-core-2.0.0.excludes b/http-core/src/main/mima-filters/2.0.x.backwards.excludes/uptake-pekko-core-2.0.0.excludes new file mode 100644 index 000000000..fa870f755 --- /dev/null +++ b/http-core/src/main/mima-filters/2.0.x.backwards.excludes/uptake-pekko-core-2.0.0.excludes @@ -0,0 +1,19 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# changes needed to uptake Pekko Core 2.0.0 +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.http.impl.engine.http2.Http2StreamHandling#IncomingStreamBuffer.onDownstreamFinish") diff --git a/http/src/main/mima-filters/2.0.x.backwards.excludes/refactor-javadsl-exception-handler.excludes b/http/src/main/mima-filters/2.0.x.backwards.excludes/refactor-javadsl-exception-handler.excludes new file mode 100644 index 000000000..e9c10df5b --- /dev/null +++ b/http/src/main/mima-filters/2.0.x.backwards.excludes/refactor-javadsl-exception-handler.excludes @@ -0,0 +1,21 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. + +# refactor Java DSL ExceptionHandler (due to function changes in Pekko Core 2.0.0) +ProblemFilters.exclude[IncompatibleMethTypeProblem]("org.apache.pekko.http.javadsl.server.ExceptionHandlerBuilder.match") +ProblemFilters.exclude[IncompatibleMethTypeProblem]("org.apache.pekko.http.javadsl.server.ExceptionHandlerBuilder.matchEquals") +ProblemFilters.exclude[IncompatibleMethTypeProblem]("org.apache.pekko.http.javadsl.server.ExceptionHandlerBuilder.matchAny") From 32f23dee2b2cdadc1ffa3b90e94fe2aed10cdc9a Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sat, 27 Sep 2025 17:44:34 +0100 Subject: [PATCH 09/11] Update uptake-pekko-core-2.0.0.excludes --- .../2.0.x.backwards.excludes/uptake-pekko-core-2.0.0.excludes | 2 ++ 1 file changed, 2 insertions(+) diff --git a/http-core/src/main/mima-filters/2.0.x.backwards.excludes/uptake-pekko-core-2.0.0.excludes b/http-core/src/main/mima-filters/2.0.x.backwards.excludes/uptake-pekko-core-2.0.0.excludes index fa870f755..ca65208f1 100644 --- a/http-core/src/main/mima-filters/2.0.x.backwards.excludes/uptake-pekko-core-2.0.0.excludes +++ b/http-core/src/main/mima-filters/2.0.x.backwards.excludes/uptake-pekko-core-2.0.0.excludes @@ -16,4 +16,6 @@ # under the License. # changes needed to uptake Pekko Core 2.0.0 +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.http.impl.engine.client.PoolInterface#Logic.onDownstreamFinish") ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.http.impl.engine.http2.Http2StreamHandling#IncomingStreamBuffer.onDownstreamFinish") +ProblemFilters.exclude[DirectMissingMethodProblem]("org.apache.pekko.http.impl.engine.http2.hpack.HandleOrPassOnStage#State.onDownstreamFinish") From 3bb2985f345228a5d5cdb09d8643c49d28884e9f Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 5 Oct 2025 16:18:04 +0100 Subject: [PATCH 10/11] Update PekkoCoreDependency.scala --- project/PekkoCoreDependency.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/PekkoCoreDependency.scala b/project/PekkoCoreDependency.scala index 78036b882..a456021e2 100644 --- a/project/PekkoCoreDependency.scala +++ b/project/PekkoCoreDependency.scala @@ -20,5 +20,5 @@ import com.github.pjfanning.pekkobuild.PekkoDependency object PekkoCoreDependency extends PekkoDependency { override val checkProject: String = "pekko-cluster-sharding-typed" override val module: Option[String] = None - override val currentVersion: String = "2.0.0-M0+180-84daa526-SNAPSHOT" + override val currentVersion: String = "2.0.0-M0+194-a3a8cf32-SNAPSHOT" } From 89470c1e57c7264ea6195244a56b1e65ede42721 Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Sun, 5 Oct 2025 16:46:00 +0100 Subject: [PATCH 11/11] rework code to scan for header name Update HttpHeaderParser.scala Update HttpHeaderParser.scala --- .../engine/parsing/HttpHeaderParser.scala | 94 +++++++++++++------ 1 file changed, 67 insertions(+), 27 deletions(-) diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpHeaderParser.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpHeaderParser.scala index efcefe5e5..99b04c951 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpHeaderParser.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpHeaderParser.scala @@ -165,37 +165,77 @@ private[engine] final class HttpHeaderParser private ( } private def parseRawHeader(input: ByteString, lineStart: Int, cursor: Int, nodeIx: Int): Int = { - val colonIx = scanHeaderNameAndReturnIndexOfColon(input, lineStart, lineStart + 1 + maxHeaderNameLength)(cursor) - val headerName = asciiString(input, lineStart, colonIx) - try { - val valueParser = new RawHeaderValueParser(headerName, maxHeaderValueLength, - headerValueCacheLimit(headerName), log, illegalResponseHeaderValueProcessingMode) - insert(input, valueParser)(cursor, colonIx + 1, nodeIx, colonIx) - parseHeaderLine(input, lineStart)(cursor, nodeIx) - } catch { - case OutOfTrieSpaceException => // if we cannot insert we drop back to simply creating new header instances - val (headerValue, endIx) = scanHeaderValue(this, input, colonIx + 1, colonIx + maxHeaderValueLength + 3, - log, settings.illegalResponseHeaderValueProcessingMode)() - resultHeader = RawHeader(headerName, headerValue.trim) - endIx + val colonIx = input.indexOf(':', cursor, lineStart + 1 + maxHeaderNameLength) + if (colonIx == -1) { + scanIllegalHeaderNameCharacters(input, cursor, lineStart + 1 + maxHeaderNameLength) + fail(s"HTTP header name exceeds the configured limit of $maxHeaderNameLength characters", + StatusCodes.RequestHeaderFieldsTooLarge) + } else { + val headerName = scanAsciiString(input, lineStart, colonIx) + try { + val valueParser = new RawHeaderValueParser(headerName, maxHeaderValueLength, + headerValueCacheLimit(headerName), log, illegalResponseHeaderValueProcessingMode) + insert(input, valueParser)(cursor, colonIx + 1, nodeIx, colonIx) + parseHeaderLine(input, lineStart)(cursor, nodeIx) + } catch { + case OutOfTrieSpaceException => // if we cannot insert we drop back to simply creating new header instances + val (headerValue, endIx) = scanHeaderValue(this, input, colonIx + 1, colonIx + maxHeaderValueLength + 3, + log, settings.illegalResponseHeaderValueProcessingMode)() + resultHeader = RawHeader(headerName, headerValue.trim) + endIx + } } } - @tailrec private def scanHeaderNameAndReturnIndexOfColon(input: ByteString, start: Int, limit: Int)(ix: Int): Int = - if (ix < limit) - (byteChar(input, ix), settings.illegalResponseHeaderNameProcessingMode) match { - case (':', _) => ix - case (c, _) if tchar(c) => scanHeaderNameAndReturnIndexOfColon(input, start, limit)(ix + 1) - case (c, IllegalResponseHeaderNameProcessingMode.Error) => - fail(s"Illegal character '${escape(c)}' in header name") - case (c, IllegalResponseHeaderNameProcessingMode.Warn) => - log.warning(s"Header key contains illegal character '${escape(c)}'") - scanHeaderNameAndReturnIndexOfColon(input, start, limit)(ix + 1) - case (c, IllegalResponseHeaderNameProcessingMode.Ignore) => - scanHeaderNameAndReturnIndexOfColon(input, start, limit)(ix + 1) + // similar to asciiString function but it checks for illegal characters + private def scanAsciiString(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 { + val c = byteChar(input, ix) + if (tchar(c)) { + build(ix + 1, sb.append(c)) + } else { + settings.illegalResponseHeaderNameProcessingMode match { + case IllegalResponseHeaderNameProcessingMode.Error => + fail(s"Illegal character '${escape(c)}' in header name") + case IllegalResponseHeaderNameProcessingMode.Warn => + log.warning(s"Header key contains illegal character '${escape(c)}'") + build(ix + 1, sb.append(c)) + case IllegalResponseHeaderNameProcessingMode.Ignore => + build(ix + 1, sb.append(c)) + } + } } - else fail(s"HTTP header name exceeds the configured limit of ${limit - start - 1} characters", - StatusCodes.RequestHeaderFieldsTooLarge) + if (start == end) "" else build() + } + + // similar to scanAsciiString but only scans for illegal characters and fails or warns if it finds one + private def scanIllegalHeaderNameCharacters(input: ByteString, start: Int, end: Int): Unit = { + @tailrec def check(ix: Int = start): Unit = + if (ix == end) { + () + } else { + val c = byteChar(input, ix) + if (tchar(c)) { + check(ix + 1) + } else { + settings.illegalResponseHeaderNameProcessingMode match { + case IllegalResponseHeaderNameProcessingMode.Error => + fail(s"Illegal character '${escape(c)}' in header name") + case IllegalResponseHeaderNameProcessingMode.Warn => + log.warning(s"Header key contains illegal character '${escape(c)}'") + check(ix + 1) + case IllegalResponseHeaderNameProcessingMode.Ignore => + () + } + } + } + if (start == end || settings.illegalResponseHeaderNameProcessingMode == IllegalResponseHeaderNameProcessingMode.Ignore) + () + else check() + } @tailrec private def parseHeaderValue(input: ByteString, valueStart: Int, branch: ValueBranch)(cursor: Int = valueStart,