Skip to content

backport: use Locale.ROOT for toLowerCase/toUpperCase (#1086) and RouteTest header lookup (#1223) - #1224

Open
pjfanning wants to merge 5 commits into
apache:1.4.xfrom
pjfanning:backport/locale-root-1.4.x
Open

backport: use Locale.ROOT for toLowerCase/toUpperCase (#1086) and RouteTest header lookup (#1223)#1224
pjfanning wants to merge 5 commits into
apache:1.4.xfrom
pjfanning:backport/locale-root-1.4.x

Conversation

@pjfanning

@pjfanning pjfanning commented Aug 24, 2026

Copy link
Copy Markdown
Member

Motivation

1.4.x is missing #1086 (7def2274d, on main since 2026-06-23), so a dozen call sites still lowercase/uppercase with the JVM default locale. Under a Turkish default locale (tr-TR) 'I'.toLowerCase is a dotless ı, which breaks paths that compare against root-lowercased values:

  • HttpHeader.parse - header names containing a capital I (If-Match, If-None-Match, If-Modified-Since, If-Unmodified-Since, If-Range) fail to resolve to modelled headers and fall back to RawHeader.
  • headerValueByName - never matches such a header, producing a spurious MissingHeaderRejection. optionalHeaderValueByName in the same file already used toRootLowerCase.
  • MediaTypes.forExtension / forExtensionOption - extension lookup misses.
  • MediaType.hashCode - equals uses equalsIgnoreCase (locale independent) while hashCode was locale dependent, so equal instances could hash differently.
  • overrideMethodWithParameter - "options" uppercases to OPTİONS and yields 501 Not Implemented.
  • Uri scheme normalization, ErrorInfo.withErrorHeaderName, ignoreIllegalHeaderFor (config and both DSL setters), the HTTP/2 frame-log alias lookup and the boolean unmarshaller.

RouteTest#header(name) in the Scala testkit had the same problem and was fixed on main in #1223.

Separately, workflow runs on 1.4.x branches ended in startup_failure before any job was created, so this PR had no usable CI.

Modification

Four commits:

  1. ci: bump cache actions - pin coursier/cache-action to 95e5b10 (v8.1.1) and actions/cache to 55cc834 (v6.1.0) across all workflows, matching main. 1.4.x was on v8.1.0 / v5.0.4, and two docs workflows were still on the floating coursier/cache-action@v6. Workflows now start and run.
  2. Cherry-pick of 7def2274d (use Locale.ROOT for toLowerCase/toUpperCase #1086). Conflicts were confined to import blocks (main has since re-sorted imports in these files) and were resolved by keeping the 1.4.x import order and adding pekko.util.Helpers.toRootLowerCase. The now-unused java.util.Locale import in ConnectHttp.scala was dropped, matching main.
  3. Regression tests extending the existing TurkishISpec, since use Locale.ROOT for toLowerCase/toUpperCase #1086 landed without tests.
  4. Cherry-pick of 08e174c4c (fix: use root locale when looking up a response header by name in RouteTest #1223) - the RouteTest#header(name) fix and its tr-TR test. Same import-block-only conflicts, resolved the same way.

Result

1.4.x behaves identically to main for these paths regardless of the JVM default locale, and CI runs on the branch again.

Tests

  • sbt "http-core / Test / testOnly org.apache.pekko.http.scaladsl.model.TurkishISpec" - 5 passed (JDK 21). Against 1.4.x without the cherry-picked commit, all 4 new cases fail.
  • sbt "http-testkit / Test / testOnly org.apache.pekko.http.scaladsl.testkit.ScalatestRouteTestSpec" - 12 passed (JDK 21).
  • scalafmt --mode diff-ref=upstream/1.4.x - clean.
  • sbt +mimaReportBinaryIssues - not run; both cherry-picks change method bodies and imports only, with no public API or binary shape change (both are already on main).
  • CI configuration change verified by the workflow runs on this PR.

References

Refs #1086, Refs #1223

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 apache#1086
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 apache#1224
…teTest (apache#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 08e174c)
@pjfanning pjfanning changed the title backport: use Locale.ROOT for toLowerCase/toUpperCase (#1086) backport: use Locale.ROOT for toLowerCase/toUpperCase (#1086) and RouteTest header lookup (#1223) Aug 24, 2026
Motivation:
Jobs on this branch fail during "Set up job" with

  The action carabiner-dev/actions@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 apache#1224
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant