From ba49e3bf0a0667e486325118d98e5c52b74b4fea Mon Sep 17 00:00:00 2001 From: PJ Fanning Date: Wed, 1 Oct 2025 16:06:49 +0100 Subject: [PATCH] shared character toLowerCase function Update CharUtils.scala Update CharUtils.scala use byteChar Remove unused import from HttpHeaderParser.scala revert change to use byteChar fn Update HttpHeaderParser.scala --- .../engine/parsing/HttpHeaderParser.scala | 27 ++++++---------- .../impl/model/parser/CommonActions.scala | 2 +- .../http/impl/model/parser/UriParser.scala | 10 +++--- .../pekko/http/impl/util/CharUtils.scala | 31 +++++++++++++++++++ .../http/scaladsl/model/HttpMessage.scala | 18 +++++------ 5 files changed, 55 insertions(+), 33 deletions(-) create mode 100644 http-core/src/main/scala/org/apache/pekko/http/impl/util/CharUtils.scala 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..7b697a440 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 @@ -20,18 +20,18 @@ import java.lang.{ StringBuilder => JStringBuilder } import org.apache.pekko import pekko.annotation.InternalApi import pekko.event.LoggingAdapter -import pekko.http.scaladsl.settings.ParserSettings +import pekko.http.impl.model.parser.CharacterClasses._ +import pekko.http.impl.model.parser.HeaderParser +import pekko.http.impl.util._ +import pekko.http.impl.util.CharUtils.toLowerCase +import pekko.http.impl.util.HttpConstants._ +import pekko.http.scaladsl.model.{ ErrorInfo, HttpHeader, MediaTypes, StatusCode, StatusCodes } +import pekko.http.scaladsl.model.headers.{ EmptyHeader, RawHeader } import pekko.http.scaladsl.settings.ParserSettings.{ ErrorLoggingVerbosity, IllegalResponseHeaderNameProcessingMode, IllegalResponseHeaderValueProcessingMode } -import pekko.http.impl.util._ -import pekko.http.impl.util.HttpConstants._ -import pekko.http.scaladsl.model.{ ErrorInfo, HttpHeader, MediaTypes, StatusCode, StatusCodes } -import pekko.http.scaladsl.model.headers.{ EmptyHeader, RawHeader } -import pekko.http.impl.model.parser.HeaderParser -import pekko.http.impl.model.parser.CharacterClasses._ import pekko.util.ByteString import scala.annotation.tailrec @@ -643,13 +643,13 @@ private[http] object HttpHeaderParser { } } else { mode match { - case ParserSettings.IllegalResponseHeaderValueProcessingMode.Error => + case IllegalResponseHeaderValueProcessingMode.Error => fail(s"Illegal character '${escape(c)}' in header value") - case ParserSettings.IllegalResponseHeaderValueProcessingMode.Warn => + case IllegalResponseHeaderValueProcessingMode.Warn => // ignore the illegal character and log a warning message log.warning(s"Illegal character '${escape(c)}' in header value") sb - case ParserSettings.IllegalResponseHeaderValueProcessingMode.Ignore => + case IllegalResponseHeaderValueProcessingMode.Ignore => // just ignore the illegal character sb } @@ -682,11 +682,4 @@ private[http] object HttpHeaderParser { def withValueCountIncreased = copy(valueCount = valueCount + 1) def spaceLeft = valueCount < parser.maxValueCount } - - /** - * Efficiently lower-cases the given character. - * Note: only works for 7-bit ASCII letters (which is enough for header names) - */ - private[HttpHeaderParser] def toLowerCase(c: Char): Char = - if (c >= 'A' && c <= 'Z') (c + 0x20 /* - 'A' + 'a' */ ).toChar else c } diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/CommonActions.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/CommonActions.scala index f3d2df709..51f032bbf 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/CommonActions.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/CommonActions.scala @@ -80,7 +80,7 @@ private[parser] trait CommonActions { val char2 = str2.charAt(at) (char1 | char2) < 0x80 && - Character.toLowerCase(char1) == Character.toLowerCase(char2) && + CharUtils.toLowerCase(char1) == CharUtils.toLowerCase(char2) && stringEquals(at + 1, length) } else true diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/UriParser.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/UriParser.scala index c2413685e..7d9e1c4ba 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/UriParser.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/UriParser.scala @@ -16,13 +16,13 @@ package org.apache.pekko.http.impl.model.parser import java.nio.charset.Charset import org.apache.pekko -import org.parboiled2._ -import pekko.http.impl.util.{ enhanceString_, StringRendering } +import pekko.annotation.InternalApi +import pekko.http.impl.util.{ enhanceString_, CharUtils => CU, StringRendering } import pekko.http.scaladsl.model.{ Uri, UriRendering } import pekko.http.scaladsl.model.headers.HttpOrigin -import Parser.DeliveryScheme.Either import Uri._ -import pekko.annotation.InternalApi +import org.parboiled2._ +import org.parboiled2.Parser.DeliveryScheme.Either /** * INTERNAL API @@ -365,7 +365,7 @@ private[http] final class UriParser( ///////////// helpers ///////////// - private def appendLowered(): Rule0 = rule { run(sb.append(CharUtils.toLowerCase(lastChar))) } + private def appendLowered(): Rule0 = rule { run(sb.append(CU.toLowerCase(lastChar))) } private def savePath() = rule { run(setPath(Path(sb.toString, uriParsingCharset))) } diff --git a/http-core/src/main/scala/org/apache/pekko/http/impl/util/CharUtils.scala b/http-core/src/main/scala/org/apache/pekko/http/impl/util/CharUtils.scala new file mode 100644 index 000000000..f15e4f23c --- /dev/null +++ b/http-core/src/main/scala/org/apache/pekko/http/impl/util/CharUtils.scala @@ -0,0 +1,31 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * license agreements; and to You under the Apache License, version 2.0: + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * This file is part of the Apache Pekko project, which was derived from Akka. + */ + +/* + * Copyright (C) 2009-2022 Lightbend Inc. + */ + +package org.apache.pekko.http.impl.util + +import org.apache.pekko +import pekko.annotation.InternalApi + +@InternalApi +private[http] object CharUtils { + + /** + * Internal Pekko HTTP Use only. + * + * Efficiently lower-cases the given character. + * Note: only works for 7-bit ASCII letters (which is enough for header names) + */ + final def toLowerCase(c: Char): Char = + if (c >= 'A' && c <= 'Z') (c + 0x20 /* - 'A' + 'a' */ ).toChar else c + +} diff --git a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpMessage.scala b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpMessage.scala index eb1034419..9e300ebe9 100644 --- a/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpMessage.scala +++ b/http-core/src/main/scala/org/apache/pekko/http/scaladsl/model/HttpMessage.scala @@ -13,31 +13,29 @@ package org.apache.pekko.http.scaladsl.model -import org.apache.pekko -import pekko.stream.scaladsl.Flow -import pekko.stream.{ FlowShape, Graph, Materializer, SystemMaterializer } import java.io.File import java.nio.file.Path import java.lang.{ Iterable => JIterable } import java.util.Optional import java.util.concurrent.{ CompletionStage, Executor } -import scala.concurrent.duration.FiniteDuration -import scala.concurrent.{ ExecutionContext, Future } +import scala.annotation.tailrec import scala.collection.immutable +import scala.concurrent.{ ExecutionContext, Future } +import scala.concurrent.duration._ +import scala.jdk.FutureConverters._ import scala.reflect.{ classTag, ClassTag } + +import org.apache.pekko import pekko.Done import pekko.actor.ClassicActorSystemProvider -import org.parboiled2.CharUtils import pekko.util.{ ByteString, HashCode, OptionVal } import pekko.http.impl.util._ import pekko.http.javadsl.{ model => jm } import pekko.http.scaladsl.util.FastFuture._ import pekko.http.scaladsl.model.headers._ - -import scala.annotation.tailrec -import scala.concurrent.duration._ -import scala.jdk.FutureConverters._ +import pekko.stream.scaladsl.Flow +import pekko.stream.{ FlowShape, Graph, Materializer, SystemMaterializer } /** * Common base class of HttpRequest and HttpResponse.