This is an automated email from the ASF dual-hosted git repository.
pjfanning pushed a commit to branch 1.4.x
in repository https://gitbox.apache.org/repos/asf/pekko-http.git
The following commit(s) were added to refs/heads/1.4.x by this push:
new df7b180de backport: use Locale.ROOT for toLowerCase/toUpperCase
(#1086) and RouteTest header lookup (#1223) (#1224)
df7b180de is described below
commit df7b180dedf855197479cfde2b64e0bf52a00e41
Author: PJ Fanning <[email protected]>
AuthorDate: Wed Aug 26 13:00:35 2026 +0100
backport: use Locale.ROOT for toLowerCase/toUpperCase (#1086) and RouteTest
header lookup (#1223) (#1224)
* use Locale.ROOT for toLowerCase/toUpperCase (#1086)
(cherry picked from commit 7def2274d7a0fb0f778ed80b791b8b4d51f834e6)
* test: add turkish-i regression tests for Locale.ROOT lowercasing
Motivation:
The backported Locale.ROOT change has no test coverage beyond the existing
HttpCharsets case, so a regression would go unnoticed.
Modification:
Extend TurkishISpec with cases that run under a tr-TR default locale and
cover
`HttpHeader.parse`, `Uri` scheme normalization, `MediaTypes.forExtension`
and
`ErrorInfo.withErrorHeaderName`.
Result:
The header-name, scheme, file-extension and error-header-name paths are
pinned
against locale sensitive lowercasing.
Tests:
- sbt "http-core / Test / testOnly
org.apache.pekko.http.scaladsl.model.TurkishISpec" - 5 passed; all 4 new cases
fail against 1.4.x without the backported commit
- scalafmt --mode diff-ref=upstream/1.4.x - clean
References:
Refs #1086
* ci: bump cache actions to the versions used on main
Motivation:
The 1.4.x workflows pin coursier/cache-action v8.1.0 (and v6 in two of the
docs
workflows) and actions/cache v5.0.4. Workflow runs on this branch end in
startup_failure before any job is created, while main, which pins newer
versions of both actions, runs fine.
Modification:
Pin coursier/cache-action to 95e5b1029b6b86e7bac033ee44a0697d8a527d2d
(v8.1.1)
and actions/cache to 55cc8345863c7cc4c66a329aec7e433d2d1c52a9 (v6.1.0) in
every
workflow, matching main.
Result:
CI on 1.4.x branches starts and runs as it does on main.
Tests:
- Not run - CI configuration only; verified by the workflow runs on this PR
References:
Refs #1224
* fix: use root locale when looking up a response header by name in
RouteTest (#1223)
Motivation:
`RouteTest#header(name: String)` lowercased the given header name with the
JVM default locale before comparing it to `HttpHeader#lowercaseName`, which
is built with `toRootLowerCase`. Under a Turkish locale
`"If-Match".toLowerCase`
yields a dotless i, so the lookup silently returned `None` for any header
name
containing a capital `I`. The javadsl equivalent (`TestRouteResult`) already
uses `toRootLowerCase`.
Modification:
Use `pekko.util.Helpers.toRootLowerCase` in `RouteTest#header(name)`.
Result:
Header lookup by name in the Scala testkit is locale independent and matches
the javadsl behaviour.
Tests:
- sbt "http-testkit / Test / testOnly
org.apache.pekko.http.scaladsl.testkit.ScalatestRouteTestSpec" - passes with
the fix; the new test fails without it
- scalafmt --mode diff-ref=origin/main - clean
- sbt +mimaReportBinaryIssues - not run; method body change only, no API or
binary shape change
References:
None - found while auditing the code base for locale sensitive lowercasing
(cherry picked from commit 08e174c4ce2a1bf6c8371bf75e572093840bbd54)
* ci: bump sbt/setup-sbt to v1.5.7
Motivation:
Jobs on this branch fail during "Set up job" with
The action carabiner-dev/actions/install/ampel@2a11d59 is not allowed in
apache/pekko-http because all actions must be from a repository owned by
your enterprise, created by GitHub, or match one of the patterns: ...
sbt/setup-sbt v1.5.0 pulls that action in transitively and it is not on the
ASF
allowlist. main pins v1.5.7 and its jobs run.
Modification:
Pin sbt/setup-sbt to 8feba82adc7f01ddcf8165b86f778bdb5b82cebc (v1.5.7) in
every
workflow, matching main. Two docs workflows were still on the floating v1
tag
and on v1.1.9.
Result:
Jobs get past action setup on 1.4.x branches.
Tests:
- Not run - CI configuration only; verified by the workflow runs on this PR
References:
Refs #1224
---
.github/workflows/dependency-graph.yml | 2 +-
.github/workflows/headers.yml | 4 +--
.github/workflows/link-validator.yml | 4 +--
.github/workflows/nightly.yml | 6 ++---
.github/workflows/publish-1.0-docs.yml | 4 +--
.github/workflows/publish-1.0-snapshots.yml | 4 +--
.github/workflows/publish-1.1-docs.yml | 4 +--
.github/workflows/publish-1.1-snapshots.yml | 4 +--
.github/workflows/publish-1.2-docs.yml | 4 +--
.github/workflows/publish-1.3-docs.yml | 4 +--
.github/workflows/publish.yml | 8 +++---
.github/workflows/stage-release-candidate.yml | 2 +-
.github/workflows/validate-and-test.yml | 12 ++++-----
.../http/impl/engine/http2/Http2Blueprint.scala | 6 ++---
.../http/impl/settings/ParserSettingsImpl.scala | 3 ++-
.../apache/pekko/http/javadsl/ConnectHttp.scala | 8 +++---
.../http/javadsl/settings/ParserSettings.scala | 3 ++-
.../pekko/http/scaladsl/model/ErrorInfo.scala | 6 +++--
.../pekko/http/scaladsl/model/HttpHeader.scala | 3 ++-
.../pekko/http/scaladsl/model/MediaType.scala | 9 ++++---
.../org/apache/pekko/http/scaladsl/model/Uri.scala | 3 ++-
.../http/scaladsl/settings/ParserSettings.scala | 3 ++-
.../pekko/http/scaladsl/model/TurkishISpec.scala | 29 ++++++++++++++++++++++
.../pekko/http/scaladsl/testkit/RouteTest.scala | 3 ++-
.../scaladsl/testkit/ScalatestRouteTestSpec.scala | 21 ++++++++++++++++
.../server/directives/HeaderDirectives.scala | 5 ++--
.../server/directives/MethodDirectives.scala | 4 ++-
.../PredefinedFromStringUnmarshallers.scala | 3 ++-
28 files changed, 117 insertions(+), 54 deletions(-)
diff --git a/.github/workflows/dependency-graph.yml
b/.github/workflows/dependency-graph.yml
index 46d7f5c40..eb8713101 100644
--- a/.github/workflows/dependency-graph.yml
+++ b/.github/workflows/dependency-graph.yml
@@ -28,7 +28,7 @@ jobs:
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd #
v6.0.2
- name: Install sbt
- uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- uses:
scalacenter/sbt-dependency-submission@f43202114d7522a4b233e052f82c2eea8d658134
# v3.2.1
with:
modules-ignore: pekko-http-tests_3 pekko-http-docs_3
diff --git a/.github/workflows/headers.yml b/.github/workflows/headers.yml
index e152f1f11..127f62627 100644
--- a/.github/workflows/headers.yml
+++ b/.github/workflows/headers.yml
@@ -37,10 +37,10 @@ jobs:
java-version: 8
- name: Install sbt
- uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- name: Cache Coursier cache
- uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 #
v8.1.0
+ uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d #
v8.1.1
- name: Enable jvm-opts
run: cp .jvmopts-ci .jvmopts
diff --git a/.github/workflows/link-validator.yml
b/.github/workflows/link-validator.yml
index 6a377689d..ae383c064 100644
--- a/.github/workflows/link-validator.yml
+++ b/.github/workflows/link-validator.yml
@@ -38,10 +38,10 @@ jobs:
java-version: 8
- name: Install sbt
- uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- name: Cache Coursier cache
- uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 #
v8.1.0
+ uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d #
v8.1.1
- name: Setup Coursier
uses: coursier/setup-action@fd1707a76b027efdfb66ca79318b4d29b72e5a02 #
v3.0.0
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
index d5e49b123..765138976 100644
--- a/.github/workflows/nightly.yml
+++ b/.github/workflows/nightly.yml
@@ -38,13 +38,13 @@ jobs:
java-version: ${{ matrix.JDK }}
- name: Install sbt
- uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- name: Cache Coursier cache
- uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 #
v8.1.0
+ uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d #
v8.1.1
- name: Cache Build Target
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
+ uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: project/**/target
key: build-target-${{ hashFiles('**/*.sbt',
'project/build.properties', 'project/**/*.scala') }}
diff --git a/.github/workflows/publish-1.0-docs.yml
b/.github/workflows/publish-1.0-docs.yml
index f6f04988a..1b70f9406 100644
--- a/.github/workflows/publish-1.0-docs.yml
+++ b/.github/workflows/publish-1.0-docs.yml
@@ -40,10 +40,10 @@ jobs:
java-version: 8
- name: Install sbt
- uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- name: Cache Coursier cache
- uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 #
v8.1.0
+ uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d #
v8.1.1
- name: Build Documentation
run: |-
diff --git a/.github/workflows/publish-1.0-snapshots.yml
b/.github/workflows/publish-1.0-snapshots.yml
index 5d96cd06a..ecc2b95e0 100644
--- a/.github/workflows/publish-1.0-snapshots.yml
+++ b/.github/workflows/publish-1.0-snapshots.yml
@@ -33,10 +33,10 @@ jobs:
java-version: 8
- name: Install sbt
- uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- name: Cache Coursier cache
- uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 #
v8.1.0
+ uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d #
v8.1.1
- name: Install graphviz
run: sudo apt-get install -y graphviz
diff --git a/.github/workflows/publish-1.1-docs.yml
b/.github/workflows/publish-1.1-docs.yml
index 2f8c0f76a..751585fff 100644
--- a/.github/workflows/publish-1.1-docs.yml
+++ b/.github/workflows/publish-1.1-docs.yml
@@ -40,10 +40,10 @@ jobs:
java-version: 8
- name: Install sbt
- uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- name: Cache Coursier cache
- uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 #
v8.1.0
+ uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d #
v8.1.1
- name: Build Documentation
run: |-
diff --git a/.github/workflows/publish-1.1-snapshots.yml
b/.github/workflows/publish-1.1-snapshots.yml
index b5e8e689e..f7688938c 100644
--- a/.github/workflows/publish-1.1-snapshots.yml
+++ b/.github/workflows/publish-1.1-snapshots.yml
@@ -33,10 +33,10 @@ jobs:
java-version: 8
- name: Install sbt
- uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- name: Cache Coursier cache
- uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 #
v8.1.0
+ uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d #
v8.1.1
- name: Install graphviz
run: sudo apt-get install -y graphviz
diff --git a/.github/workflows/publish-1.2-docs.yml
b/.github/workflows/publish-1.2-docs.yml
index 882394b27..7c73ec300 100644
--- a/.github/workflows/publish-1.2-docs.yml
+++ b/.github/workflows/publish-1.2-docs.yml
@@ -39,10 +39,10 @@ jobs:
java-version: 8
- name: Install sbt
- uses: sbt/setup-sbt@v1
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- name: Cache Coursier cache
- uses: coursier/cache-action@v6
+ uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d #
v8.1.1
- name: Build Documentation
run: |-
diff --git a/.github/workflows/publish-1.3-docs.yml
b/.github/workflows/publish-1.3-docs.yml
index d3e36c949..85de49687 100644
--- a/.github/workflows/publish-1.3-docs.yml
+++ b/.github/workflows/publish-1.3-docs.yml
@@ -40,10 +40,10 @@ jobs:
java-version: 8
- name: Install sbt
- uses: sbt/setup-sbt@6c68d2fe8dfbc0a0534d70101baa2e0420e1a506 # v1.1.9
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- name: Cache Coursier cache
- uses: coursier/cache-action@v6
+ uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d #
v8.1.1
- name: Build Documentation
run: |-
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 8332f8332..e303ecaa1 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -41,10 +41,10 @@ jobs:
java-version: 8
- name: Install sbt
- uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- name: Cache Coursier cache
- uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 #
v8.1.0
+ uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d #
v8.1.1
- name: Install graphviz
run: sudo apt-get install -y graphviz
@@ -74,10 +74,10 @@ jobs:
java-version: 8
- name: Install sbt
- uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- name: Cache Coursier cache
- uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 #
v8.1.0
+ uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d #
v8.1.1
- name: Build Documentation
run: |-
diff --git a/.github/workflows/stage-release-candidate.yml
b/.github/workflows/stage-release-candidate.yml
index d0abc8fdf..613cf1bf4 100644
--- a/.github/workflows/stage-release-candidate.yml
+++ b/.github/workflows/stage-release-candidate.yml
@@ -212,7 +212,7 @@ jobs:
java-version: 8
- name: Install sbt
- uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- name: Install Graphviz
run: |-
diff --git a/.github/workflows/validate-and-test.yml
b/.github/workflows/validate-and-test.yml
index fe48f6304..e440184c6 100644
--- a/.github/workflows/validate-and-test.yml
+++ b/.github/workflows/validate-and-test.yml
@@ -38,13 +38,13 @@ jobs:
java-version: 8
- name: Install sbt
- uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- name: Cache Coursier cache
- uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 #
v8.1.0
+ uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d #
v8.1.1
- name: Cache Build Target
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
+ uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: project/**/target
key: build-target-${{ hashFiles('**/*.sbt',
'project/build.properties', 'project/**/*.scala') }}
@@ -86,13 +86,13 @@ jobs:
java-version: ${{ matrix.JDK }}
- name: Install sbt
- uses: sbt/setup-sbt@66fb4376e81982c7d92a4074170846fff88e2e30 # v1.5.0
+ uses: sbt/setup-sbt@8feba82adc7f01ddcf8165b86f778bdb5b82cebc # v1.5.7
- name: Cache Coursier cache
- uses: coursier/cache-action@90c37294538be80a558fd665531fcdc2b467b475 #
v8.1.0
+ uses: coursier/cache-action@95e5b1029b6b86e7bac033ee44a0697d8a527d2d #
v8.1.1
- name: Cache Build Target
- uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
+ uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
with:
path: project/**/target
key: build-target-${{ hashFiles('**/*.sbt',
'project/build.properties', 'project/**/*.scala') }}
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/Http2Blueprint.scala
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/Http2Blueprint.scala
index 3b14c0b86..f732085b0 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/Http2Blueprint.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/http2/Http2Blueprint.scala
@@ -38,8 +38,8 @@ import pekko.http.scaladsl.settings.{
import pekko.stream.{ BidiShape, Graph, StreamTcpException, ThrottleMode }
import pekko.stream.TLSProtocol._
import pekko.stream.scaladsl.{ BidiFlow, Flow, Keep, Source }
-import pekko.util.ByteString
-import pekko.util.OptionVal
+import pekko.util.{ ByteString, OptionVal }
+import pekko.util.Helpers.toRootLowerCase
import scala.concurrent.duration.{ Duration, FiniteDuration }
import scala.concurrent.{ ExecutionContext, Future }
@@ -226,7 +226,7 @@ private[http] object Http2Blueprint {
}
private[http2] def frameTypeAliasToFrameTypeName(frameType: String):
Option[String] = {
- frameType.toLowerCase match {
+ toRootLowerCase(frameType) match {
case "reset" => Some("RstStreamFrame")
case "headers" => Some("HeadersFrame")
case "continuation" => Some("ContinuationFrame")
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/impl/settings/ParserSettingsImpl.scala
b/http-core/src/main/scala/org/apache/pekko/http/impl/settings/ParserSettingsImpl.scala
index c8999d0ce..3a47781d1 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/impl/settings/ParserSettingsImpl.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/impl/settings/ParserSettingsImpl.scala
@@ -23,6 +23,7 @@ import pekko.http.scaladsl.settings.ParserSettings.{
IllegalResponseHeaderValueProcessingMode
}
import pekko.util.ConstantFun
+import pekko.util.Helpers.toRootLowerCase
import com.typesafe.config.Config
import scala.collection.JavaConverters._
@@ -115,7 +116,7 @@ object ParserSettingsImpl extends
SettingsCompanionImpl[ParserSettingsImpl]("pek
Uri.ParsingMode(c.getString("uri-parsing-mode")),
CookieParsingMode(c.getString("cookie-parsing-mode")),
c.getBoolean("illegal-header-warnings"),
-
c.getStringList("ignore-illegal-header-for").asScala.map(_.toLowerCase).toSet,
+
c.getStringList("ignore-illegal-header-for").asScala.map(toRootLowerCase).toSet,
ErrorLoggingVerbosity(c.getString("error-logging-verbosity")),
IllegalResponseHeaderNameProcessingMode(c.getString("illegal-response-header-name-processing-mode")),
IllegalResponseHeaderValueProcessingMode(c.getString("illegal-response-header-value-processing-mode")),
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/javadsl/ConnectHttp.scala
b/http-core/src/main/scala/org/apache/pekko/http/javadsl/ConnectHttp.scala
index 7191d0182..27d3bc999 100644
--- a/http-core/src/main/scala/org/apache/pekko/http/javadsl/ConnectHttp.scala
+++ b/http-core/src/main/scala/org/apache/pekko/http/javadsl/ConnectHttp.scala
@@ -13,12 +13,12 @@
package org.apache.pekko.http.javadsl
-import java.util.Locale
import java.util.Optional
import org.apache.pekko
import pekko.annotation.{ DoNotInherit, InternalApi }
import pekko.http.javadsl.model.Uri
+import pekko.util.Helpers.toRootLowerCase
import pekko.util.OptionConverters._
@DoNotInherit
@@ -71,7 +71,7 @@ object ConnectHttp {
}
private def toHost(uriHost: Uri, port: Int): ConnectHttp = {
- val s = uriHost.scheme.toLowerCase(Locale.ROOT)
+ val s = toRootLowerCase(uriHost.scheme)
if (s == "https") new ConnectHttpsImpl(uriHost.host.address,
effectivePort(s, port), context = Optional.empty())
else new ConnectHttpImpl(uriHost.host.address, effectivePort(s, port))
}
@@ -114,7 +114,7 @@ object ConnectHttp {
}
private def toHostHttps(uriHost: Uri, port: Int): ConnectWithHttps = {
- val s = uriHost.scheme.toLowerCase(Locale.ROOT)
+ val s = toRootLowerCase(uriHost.scheme)
require(s == "" || s == "https", "toHostHttps used with non https scheme!
Was: " + uriHost)
new ConnectHttpsImpl(uriHost.host.address, effectivePort("https", port),
context = Optional.empty())
}
@@ -125,7 +125,7 @@ object ConnectHttp {
}
private def effectivePort(scheme: String, port: Int): Int = {
- val s = scheme.toLowerCase(Locale.ROOT)
+ val s = toRootLowerCase(scheme)
if (port >= 0) port
else if (s == "https" || s == "wss") 443
else if (s == "http" || s == "ws") 80
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/javadsl/settings/ParserSettings.scala
b/http-core/src/main/scala/org/apache/pekko/http/javadsl/settings/ParserSettings.scala
index 6eaa2aded..2b0b3240c 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/javadsl/settings/ParserSettings.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/javadsl/settings/ParserSettings.scala
@@ -23,6 +23,7 @@ import java.{ util => ju }
import pekko.annotation.DoNotInherit
import pekko.http.impl.util.JavaMapping.Implicits._
+import pekko.util.Helpers.toRootLowerCase
import scala.annotation.varargs
import scala.collection.JavaConverters._
@@ -90,7 +91,7 @@ abstract class ParserSettings private[pekko] () extends
BodyPartParser.Settings
self.copy(includeSslSessionAttribute = newValue)
def withModeledHeaderParsing(newValue: Boolean): ParserSettings =
self.copy(modeledHeaderParsing = newValue)
def withIgnoreIllegalHeaderFor(newValue: List[String]): ParserSettings =
- self.copy(ignoreIllegalHeaderFor = newValue.map(_.toLowerCase).toSet)
+ self.copy(ignoreIllegalHeaderFor = newValue.map(toRootLowerCase).toSet)
// special ---
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/ErrorInfo.scala
b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/ErrorInfo.scala
index 5d73947b5..2b552fe42 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/ErrorInfo.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/ErrorInfo.scala
@@ -14,7 +14,9 @@
package org.apache.pekko.http.scaladsl.model
import StatusCodes.ClientError
-import org.apache.pekko.annotation.InternalApi
+import org.apache.pekko
+import pekko.annotation.InternalApi
+import pekko.util.Helpers.toRootLowerCase
/**
* Two-level model of error information.
@@ -28,7 +30,7 @@ final class ErrorInfo(
val errorHeaderName: String = "") extends scala.Product with scala.Equals
with java.io.Serializable {
def withSummary(newSummary: String) = copy(summary = newSummary)
def withSummaryPrepended(prefix: String) = withSummary(if (summary.isEmpty)
prefix else prefix + ": " + summary)
- def withErrorHeaderName(headerName: String) = new ErrorInfo(summary, detail,
headerName.toLowerCase)
+ def withErrorHeaderName(headerName: String) = new ErrorInfo(summary, detail,
toRootLowerCase(headerName))
def withFallbackSummary(fallbackSummary: String) = if (summary.isEmpty)
withSummary(fallbackSummary) else this
def formatPretty = if (summary.isEmpty) detail else if (detail.isEmpty)
summary else summary + ": " + detail
def format(withDetail: Boolean): String = if (withDetail) formatPretty else
summary
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpHeader.scala
b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpHeader.scala
index 0434b557d..6a2a47a02 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpHeader.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpHeader.scala
@@ -23,6 +23,7 @@ import pekko.http.impl.model.parser.{ CharacterClasses,
HeaderParser }
import pekko.http.javadsl.{ model => jm }
import pekko.http.scaladsl.model.headers._
import pekko.util.OptionVal
+import pekko.util.Helpers.toRootLowerCase
import scala.collection.immutable
@@ -91,7 +92,7 @@ object HttpHeader {
val parser = new HeaderParser(value, settings)
parser.`header-field-value`.run() match {
case Success(preProcessedValue) =>
- HeaderParser.parseFull(name.toLowerCase, preProcessedValue,
settings) match {
+ HeaderParser.parseFull(toRootLowerCase(name), preProcessedValue,
settings) match {
case HeaderParser.Success(header) => ParsingResult.Ok(header, Nil)
case HeaderParser.Failure(info) =>
val errors = info.withSummaryPrepended(s"Illegal HTTP header
'$name'") :: Nil
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/MediaType.scala
b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/MediaType.scala
index 61643de19..7eef53b92 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/MediaType.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/MediaType.scala
@@ -18,6 +18,7 @@ import pekko.annotation.DoNotInherit
import pekko.http.impl.util._
import pekko.http.javadsl.{ model => jm }
import pekko.http.impl.util.JavaMapping.Implicits._
+import pekko.util.Helpers.toRootLowerCase
/**
* A MediaType describes the type of the content of an HTTP message entity.
@@ -74,7 +75,7 @@ sealed abstract class MediaType(_mainType: String, _subType:
String) extends jm.
case _ => false
}
- override def hashCode(): Int = value.toLowerCase.hashCode
+ override def hashCode(): Int = toRootLowerCase(value).hashCode
/**
* JAVA API
@@ -327,12 +328,12 @@ object MediaTypes extends ObjectRegistry[(String,
String), MediaType] {
private[this] var extensionMap = Map.empty[String, MediaType]
- def forExtensionOption(ext: String): Option[MediaType] =
extensionMap.get(ext.toLowerCase)
- def forExtension(ext: String): MediaType =
extensionMap.getOrElse(ext.toLowerCase, `application/octet-stream`)
+ def forExtensionOption(ext: String): Option[MediaType] =
extensionMap.get(toRootLowerCase(ext))
+ def forExtension(ext: String): MediaType =
extensionMap.getOrElse(toRootLowerCase(ext), `application/octet-stream`)
private def registerFileExtensions[T <: MediaType](mediaType: T): T = {
mediaType.fileExtensions.foreach { ext =>
- val lcExt = ext.toLowerCase
+ val lcExt = toRootLowerCase(ext)
require(!extensionMap.contains(lcExt),
s"Extension '$ext' clash: media-types '${extensionMap(lcExt)}' and
'$mediaType'")
extensionMap = extensionMap.updated(lcExt, mediaType)
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/Uri.scala
b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/Uri.scala
index 72e4e7f9d..135ac4cf9 100644
--- a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/Uri.scala
+++ b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/Uri.scala
@@ -28,6 +28,7 @@ import pekko.http.javadsl.{ model => jm }
import pekko.http.impl.model.parser.UriParser
import pekko.http.impl.model.parser.CharacterClasses._
import pekko.http.impl.util._
+import pekko.util.Helpers.toRootLowerCase
import Uri._
/**
@@ -868,7 +869,7 @@ object Uri {
} else if (allLower) -1
else -2
verify() match {
- case -2 => scheme.toLowerCase
+ case -2 => toRootLowerCase(scheme)
case -1 => scheme
case ix => fail(s"Invalid URI scheme, unexpected character at pos $ix
('${scheme.charAt(ix)}')")
}
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/settings/ParserSettings.scala
b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/settings/ParserSettings.scala
index 82b919b98..980d44c9a 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/settings/ParserSettings.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/settings/ParserSettings.scala
@@ -25,6 +25,7 @@ import pekko.http.impl.util._
import pekko.http.javadsl.model
import pekko.http.scaladsl.model._
import pekko.http.scaladsl.{ settings => js }
+import pekko.util.Helpers.toRootLowerCase
import pekko.util.OptionConverters._
import com.typesafe.config.Config
@@ -123,7 +124,7 @@ abstract class ParserSettings private[pekko] () extends
pekko.http.javadsl.setti
self.copy(includeSslSessionAttribute = newValue)
override def withModeledHeaderParsing(newValue: Boolean): ParserSettings =
self.copy(modeledHeaderParsing = newValue)
override def withIgnoreIllegalHeaderFor(newValue: List[String]):
ParserSettings =
- self.copy(ignoreIllegalHeaderFor = newValue.map(_.toLowerCase).toSet)
+ self.copy(ignoreIllegalHeaderFor = newValue.map(toRootLowerCase).toSet)
// overloads for idiomatic Scala use
def withUriParsingMode(newValue: Uri.ParsingMode): ParserSettings =
self.copy(uriParsingMode = newValue)
diff --git
a/http-core/src/test/scala/org/apache/pekko/http/scaladsl/model/TurkishISpec.scala
b/http-core/src/test/scala/org/apache/pekko/http/scaladsl/model/TurkishISpec.scala
index 58d6a89f6..76f9fdede 100644
---
a/http-core/src/test/scala/org/apache/pekko/http/scaladsl/model/TurkishISpec.scala
+++
b/http-core/src/test/scala/org/apache/pekko/http/scaladsl/model/TurkishISpec.scala
@@ -40,5 +40,34 @@ class TurkishISpec extends AnyWordSpec with Matchers {
Locale.setDefault(previousLocale)
}
}
+
+ "parse a header name containing a capital I in the turkish locale" in
withTurkishLocale {
+ HttpHeader.parse("If-Match", "\"xyzzy\"") match {
+ case HttpHeader.ParsingResult.Ok(header, Nil) => header shouldBe
a[headers.`If-Match`]
+ case other => fail(s"Expected a
modelled If-Match header but got $other")
+ }
+ }
+
+ "normalize a uri scheme containing a capital I in the turkish locale" in
withTurkishLocale {
+ Uri(scheme = "IPP", authority =
Uri.Authority(Uri.Host("example.com"))).scheme shouldEqual "ipp"
+ }
+
+ "resolve a media type for an upper case file extension in the turkish
locale" in withTurkishLocale {
+ MediaTypes.forExtension("TIFF") shouldEqual MediaTypes.`image/tiff`
+ }
+
+ "lowercase an error header name in the turkish locale" in
withTurkishLocale {
+ ErrorInfo("summary",
"detail").withErrorHeaderName("If-Match").errorHeaderName shouldEqual "if-match"
+ }
+ }
+
+ private def withTurkishLocale(body: => Any): Unit = {
+ val previousLocale = Locale.getDefault
+ try {
+ Locale.setDefault(new Locale("tr", "TR"))
+ body
+ } finally {
+ Locale.setDefault(previousLocale)
+ }
}
}
diff --git
a/http-testkit/src/main/scala/org/apache/pekko/http/scaladsl/testkit/RouteTest.scala
b/http-testkit/src/main/scala/org/apache/pekko/http/scaladsl/testkit/RouteTest.scala
index c33ca493a..bf961219f 100644
---
a/http-testkit/src/main/scala/org/apache/pekko/http/scaladsl/testkit/RouteTest.scala
+++
b/http-testkit/src/main/scala/org/apache/pekko/http/scaladsl/testkit/RouteTest.scala
@@ -30,6 +30,7 @@ import pekko.stream.{ Materializer, SystemMaterializer }
import pekko.stream.scaladsl.Source
import pekko.testkit.TestKit
import pekko.util.ConstantFun
+import pekko.util.Helpers.toRootLowerCase
import com.typesafe.config.{ Config, ConfigFactory }
import scala.collection.immutable
@@ -95,7 +96,7 @@ trait RouteTest extends RequestBuilding with
WSTestRequestBuilding with RouteTes
def charset: HttpCharset = charsetOption.getOrElse(sys.error("Binary entity
does not have charset"))
def headers: immutable.Seq[HttpHeader] = rawResponse.headers
def header[T >: Null <: HttpHeader: ClassTag]: Option[T] =
rawResponse.header[T](implicitly[ClassTag[T]])
- def header(name: String): Option[HttpHeader] =
rawResponse.headers.find(_.is(name.toLowerCase))
+ def header(name: String): Option[HttpHeader] =
rawResponse.headers.find(_.is(toRootLowerCase(name)))
def status: StatusCode = rawResponse.status
def closingExtension: String = chunks.lastOption match {
diff --git
a/http-testkit/src/test/scala/org/apache/pekko/http/scaladsl/testkit/ScalatestRouteTestSpec.scala
b/http-testkit/src/test/scala/org/apache/pekko/http/scaladsl/testkit/ScalatestRouteTestSpec.scala
index 63eb780ed..4ef079b49 100644
---
a/http-testkit/src/test/scala/org/apache/pekko/http/scaladsl/testkit/ScalatestRouteTestSpec.scala
+++
b/http-testkit/src/test/scala/org/apache/pekko/http/scaladsl/testkit/ScalatestRouteTestSpec.scala
@@ -13,6 +13,8 @@
package org.apache.pekko.http.scaladsl.testkit
+import java.util.Locale
+
import scala.concurrent.duration._
import org.apache.pekko
import pekko.testkit._
@@ -57,6 +59,25 @@ class ScalatestRouteTestSpec extends AnyFreeSpec with
Matchers with ScalatestRou
}
}
+ "a header lookup by name that is unaffected by the turkish-i problem" in {
+ val previousLocale = Locale.getDefault
+ try {
+ Locale.setDefault(new Locale("tr", "TR"))
+ // in the turkish locale 'I'.toLowerCase is a dotless i, so a
default-locale
+ // lowercasing of 'If-Match' would not match the header's lowercaseName
+ val ifMatchHeader = RawHeader("If-Match", "\"xyzzy\"")
+ Get() ~> {
+ respondWithHeader(ifMatchHeader) {
+ complete("abc")
+ }
+ } ~> check {
+ header("If-Match") shouldEqual Some(ifMatchHeader)
+ }
+ } finally {
+ Locale.setDefault(previousLocale)
+ }
+ }
+
"a test using ~!> and some checks" in {
// raw here, should have been parsed into modelled header when going
through an actual server when using `~!>`
val extraHeader = RawHeader("X-Forwarded-Proto", "abc")
diff --git
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/HeaderDirectives.scala
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/HeaderDirectives.scala
index 54c4d9fa1..29d94b920 100644
---
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/HeaderDirectives.scala
+++
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/HeaderDirectives.scala
@@ -18,6 +18,7 @@ import org.apache.pekko
import pekko.http.impl.util._
import pekko.http.scaladsl.model._
import pekko.http.scaladsl.model.headers._
+import pekko.util.Helpers.toRootLowerCase
import scala.reflect.ClassTag
import scala.util.control.NonFatal
@@ -92,7 +93,7 @@ trait HeaderDirectives {
* @group header
*/
def headerValueByName(headerName: String): Directive1[String] =
- headerValue(optionalValue(headerName.toLowerCase)) |
reject(MissingHeaderRejection(headerName))
+ headerValue(optionalValue(toRootLowerCase(headerName))) |
reject(MissingHeaderRejection(headerName))
/**
* Extracts the first HTTP request header of the given type.
@@ -147,7 +148,7 @@ trait HeaderDirectives {
* @group header
*/
def optionalHeaderValueByName(headerName: String):
Directive1[Option[String]] = {
- val lowerCaseName = headerName.toRootLowerCase
+ val lowerCaseName = toRootLowerCase(headerName)
extract(_.request.headers.collectFirst {
case h: HttpHeader if h.is(lowerCaseName) => h.value
})
diff --git
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/MethodDirectives.scala
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/MethodDirectives.scala
index 7edf7c6e0..3d609d957 100644
---
a/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/MethodDirectives.scala
+++
b/http/src/main/scala/org/apache/pekko/http/scaladsl/server/directives/MethodDirectives.scala
@@ -14,6 +14,8 @@
package org.apache.pekko.http.scaladsl.server
package directives
+import java.util.Locale
+
import org.apache.pekko
import pekko.http.scaladsl.model.{ HttpMethod, StatusCodes }
import pekko.http.scaladsl.model.HttpMethods._
@@ -111,7 +113,7 @@ trait MethodDirectives {
def overrideMethodWithParameter(paramName: String): Directive0 =
parameter(paramName.optional).flatMap {
case Some(method) =>
- getForKey(method.toUpperCase) match {
+ getForKey(method.toUpperCase(Locale.ROOT)) match {
case Some(m) => mapRequest(_.withMethod(m))
case _ => complete(StatusCodes.NotImplemented)
}
diff --git
a/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala
b/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala
index a865fcedb..e18eab4bb 100755
---
a/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala
+++
b/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala
@@ -19,6 +19,7 @@ import scala.collection.immutable
import org.apache.pekko
import pekko.http.scaladsl.util.FastFuture
import pekko.util.ByteString
+import pekko.util.Helpers.toRootLowerCase
trait PredefinedFromStringUnmarshallers {
@@ -48,7 +49,7 @@ trait PredefinedFromStringUnmarshallers {
implicit val booleanFromStringUnmarshaller: Unmarshaller[String, Boolean] =
Unmarshaller.strict[String, Boolean] { string =>
- string.toLowerCase match {
+ toRootLowerCase(string) match {
case "true" | "yes" | "on" | "1" => true
case "false" | "no" | "off" | "0" => false
case "" => throw
Unmarshaller.NoContentException
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]