This is an automated email from the ASF dual-hosted git repository.
fanningpj pushed a commit to branch 1.3.x
in repository https://gitbox.apache.org/repos/asf/pekko-http.git
The following commit(s) were added to refs/heads/1.3.x by this push:
new 30f6d5a17 use byteAt instead of byteChar where it is easy to avoid the
char conversion (#801) (#827)
30f6d5a17 is described below
commit 30f6d5a172eb1dead92d0fc5d13a68258c2014d0
Author: PJ Fanning <[email protected]>
AuthorDate: Sun Oct 5 18:27:41 2025 +0100
use byteAt instead of byteChar where it is easy to avoid the char
conversion (#801) (#827)
* more performant boundary check
* try to fix tests
* Update BodyPartParser.scala
* Update BodyPartParser.scala
* Update BodyPartParser.scala
* Update BodyPartParser.scala
* add HttpConstants
* use ByteAt to avoid some char conversions
more changes
more changes
* add dash constant
revert LineParser changes
---
.../http/impl/engine/parsing/BodyPartParser.scala | 15 +++++-----
.../impl/engine/parsing/HttpHeaderParser.scala | 13 +++++----
.../impl/engine/parsing/HttpMessageParser.scala | 32 ++++++++++----------
.../impl/engine/parsing/HttpRequestParser.scala | 25 ++++++++--------
.../impl/engine/parsing/HttpResponseParser.scala | 12 ++++----
.../parsing/SpecializedHeaderValueParsers.scala | 3 +-
.../http/impl/model/parser/CharacterClasses.scala | 10 +++----
.../pekko/http/impl/util/HttpConstants.scala | 34 ++++++++++++++++++++++
.../scaladsl/unmarshalling/sse/LineParser.scala | 5 ++--
9 files changed, 96 insertions(+), 53 deletions(-)
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/BodyPartParser.scala
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/BodyPartParser.scala
index 28a69e872..0be9fc946 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/BodyPartParser.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/BodyPartParser.scala
@@ -16,18 +16,19 @@ package org.apache.pekko.http.impl.engine.parsing
import org.apache.pekko
import pekko.NotUsed
import pekko.annotation.InternalApi
-
-import scala.annotation.tailrec
import pekko.event.LoggingAdapter
-import org.parboiled2.CharPredicate
+import pekko.http.impl.util._
+import pekko.http.impl.util.HttpConstants._
+import pekko.http.scaladsl.model._
+import pekko.http.scaladsl.model.headers._
+import pekko.stream.{ Attributes, FlowShape, Inlet, Outlet }
import pekko.stream.scaladsl.Source
import pekko.stream.stage._
import pekko.util.ByteString
-import pekko.http.scaladsl.model._
-import pekko.http.impl.util._
-import pekko.stream.{ Attributes, FlowShape, Inlet, Outlet }
import headers._
+import org.parboiled2.CharPredicate
+import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer
/**
@@ -282,7 +283,7 @@ private[http] final class BodyPartParser(
def done(): StateResult = null // StateResult is a phantom type
def doubleDash(input: ByteString, offset: Int): Boolean =
- byteChar(input, offset) == '-' && byteChar(input, offset + 1) == '-'
+ byteAt(input, offset) == DASH_BYTE && byteAt(input, offset + 1) ==
DASH_BYTE
}
}
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 8e00dd545..c7bdbda34 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,21 +20,22 @@ 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.scaladsl.settings.ParserSettings.{
+ ErrorLoggingVerbosity,
IllegalResponseHeaderNameProcessingMode,
IllegalResponseHeaderValueProcessingMode
}
-import pekko.http.scaladsl.settings.ParserSettings.ErrorLoggingVerbosity
-import pekko.http.scaladsl.settings.ParserSettings
-
-import scala.annotation.tailrec
-import pekko.util.ByteString
import pekko.http.ccompat._
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
/**
* INTERNAL API
@@ -600,7 +601,7 @@ private[http] object HttpHeaderParser {
if (ix < limit)
byteChar(input, ix) match {
case '\t' => scanHeaderValue(hhp, input, start, limit, log,
mode)(appended(' '), ix + 1)
- case '\r' if byteChar(input, ix + 1) == '\n' =>
+ case '\r' if byteAt(input, ix + 1) == LF_BYTE =>
if (WSP(byteChar(input, ix + 2))) scanHeaderValue(hhp, input, start,
limit, log, mode)(appended(' '), ix + 3)
else (if (sb != null) sb.toString else asciiString(input, start,
ix), ix + 2)
case '\n' =>
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpMessageParser.scala
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpMessageParser.scala
index 374f3c3e2..40803632d 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpMessageParser.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpMessageParser.scala
@@ -15,21 +15,23 @@ package org.apache.pekko.http.impl.engine.parsing
import javax.net.ssl.SSLSession
-import org.apache.pekko
-import pekko.stream.TLSProtocol._
-
import scala.annotation.tailrec
import scala.collection.mutable.ListBuffer
+
import org.parboiled2.CharUtils
-import pekko.util.ByteString
+
+import org.apache.pekko
+import pekko.annotation.InternalApi
import pekko.http.impl.model.parser.CharacterClasses
-import pekko.http.scaladsl.settings.ParserSettings
+import pekko.http.impl.util.HttpConstants._
import pekko.http.scaladsl.model.{ ParsingException => _, _ }
import headers._
import HttpProtocols._
import ParserOutput._
-import pekko.annotation.InternalApi
+import pekko.http.scaladsl.settings.ParserSettings
import
pekko.http.scaladsl.settings.ParserSettings.ConflictingContentTypeHeaderProcessingMode
+import pekko.stream.TLSProtocol._
+import pekko.util.ByteString
/**
* INTERNAL API
@@ -288,20 +290,20 @@ private[http] trait HttpMessageParser[Output >:
MessageOutput <: ParserOutput] {
emit(EntityChunk(HttpEntity.Chunk(input.slice(cursor,
chunkBodyEnd).compact, extension)))
Trampoline(_ => parseChunk(input, chunkBodyEnd + terminatorLen,
isLastMessage, totalBytesRead + chunkSize))
}
- byteChar(input, chunkBodyEnd) match {
- case '\r' if byteChar(input, chunkBodyEnd + 1) == '\n' => result(2)
- case '\n' => result(1)
- case x =>
failEntityStream("Illegal chunk termination")
+ byteAt(input, chunkBodyEnd) match {
+ case CR_BYTE if byteAt(input, chunkBodyEnd + 1) == LF_BYTE =>
result(2)
+ case LF_BYTE =>
result(1)
+ case x =>
failEntityStream("Illegal chunk termination")
}
} else parseTrailer(extension, cursor)
@tailrec def parseChunkExtensions(chunkSize: Int, cursor: Int)(startIx:
Int = cursor): StateResult =
if (cursor - startIx <= settings.maxChunkExtLength) {
def extension = asciiString(input, startIx, cursor)
- byteChar(input, cursor) match {
- case '\r' if byteChar(input, cursor + 1) == '\n' =>
parseChunkBody(chunkSize, extension, cursor + 2)
- case '\n' =>
parseChunkBody(chunkSize, extension, cursor + 1)
- case _ =>
parseChunkExtensions(chunkSize, cursor + 1)(startIx)
+ byteAt(input, cursor) match {
+ case CR_BYTE if byteAt(input, cursor + 1) == LF_BYTE =>
parseChunkBody(chunkSize, extension, cursor + 2)
+ case LF_BYTE =>
parseChunkBody(chunkSize, extension, cursor + 1)
+ case _ =>
parseChunkExtensions(chunkSize, cursor + 1)(startIx)
}
} else failEntityStream(
s"HTTP chunk extension length exceeds configured limit of
${settings.maxChunkExtLength} characters")
@@ -314,7 +316,7 @@ private[http] trait HttpMessageParser[Output >:
MessageOutput <: ParserOutput] {
failEntityStream(
s"HTTP chunk of $size bytes exceeds the configured limit of
${settings.maxChunkSize} bytes")
case ';' if cursor > offset => parseChunkExtensions(size.toInt,
cursor + 1)()
- case '\r' if cursor > offset && byteChar(input, cursor + 1) == '\n'
=>
+ case '\r' if cursor > offset && byteAt(input, cursor + 1) == LF_BYTE
=>
parseChunkBody(size.toInt, "", cursor + 2)
case '\n' if cursor > offset => parseChunkBody(size.toInt, "",
cursor + 1)
case c if CharacterClasses.WSP(c) => parseSize(cursor + 1, size) //
illegal according to the spec but can happen, see issue #1812
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpRequestParser.scala
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpRequestParser.scala
index eda2d7705..4ff31c0bd 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpRequestParser.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpRequestParser.scala
@@ -18,22 +18,23 @@ import javax.net.ssl.SSLSession
import scala.annotation.{ switch, tailrec }
import org.apache.pekko
-import pekko.http.scaladsl.settings.{ ParserSettings, WebSocketSettings }
-import pekko.util.ByteString
-import pekko.util.OptionVal
+import pekko.annotation.InternalApi
+import pekko.http.impl.engine.server.HttpAttributes
+import pekko.http.impl.util.ByteStringParserInput
+import pekko.http.impl.util.HttpConstants._
import pekko.http.impl.engine.ws.Handshake
import pekko.http.impl.model.parser.{ CharacterClasses, UriParser }
import pekko.http.scaladsl.model.{ ParsingException => _, _ }
-import headers._
-import StatusCodes._
+import pekko.http.scaladsl.model.headers._
+import pekko.http.scaladsl.model.StatusCodes._
+import pekko.http.scaladsl.settings.{ ParserSettings, WebSocketSettings }
import ParserOutput._
-import pekko.annotation.InternalApi
-import pekko.http.impl.engine.server.HttpAttributes
-import pekko.http.impl.util.ByteStringParserInput
-import org.parboiled2.ParserInput
import pekko.stream.{ Attributes, FlowShape, Inlet, Outlet }
import pekko.stream.TLSProtocol.SessionBytes
import pekko.stream.stage.{ GraphStage, GraphStageLogic, InHandler, OutHandler
}
+import pekko.util.ByteString
+import pekko.util.OptionVal
+import org.parboiled2.ParserInput
/**
* INTERNAL API
@@ -90,9 +91,9 @@ private[http] final class HttpRequestParser(
var cursor = parseMethod(input, offset)
cursor = parseRequestTarget(input, cursor)
cursor = parseProtocol(input, cursor)
- if (byteChar(input, cursor) == '\r' && byteChar(input, cursor + 1)
== '\n')
+ if (byteAt(input, cursor) == CR_BYTE && byteAt(input, cursor + 1) ==
LF_BYTE)
parseHeaderLines(input, cursor + 2)
- else if (byteChar(input, cursor) == '\n')
+ else if (byteAt(input, cursor) == LF_BYTE)
parseHeaderLines(input, cursor + 1)
else onBadProtocol(input.drop(cursor))
} else
@@ -124,7 +125,7 @@ private[http] final class HttpRequestParser(
@tailrec def parseMethod(meth: HttpMethod, ix: Int = 1): Int =
if (ix == meth.value.length)
- if (byteChar(input, cursor + ix) == ' ') {
+ if (byteAt(input, cursor + ix) == SPACE_BYTE) {
method = meth
cursor + ix + 1
} else parseCustomMethod()
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpResponseParser.scala
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpResponseParser.scala
index 95a893432..b022dcc4f 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpResponseParser.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/HttpResponseParser.scala
@@ -17,14 +17,16 @@ import javax.net.ssl.SSLSession
import scala.annotation.tailrec
import scala.concurrent.Promise
import scala.util.control.{ NoStackTrace, NonFatal }
+
import org.apache.pekko
+import pekko.annotation.InternalApi
import pekko.http.scaladsl.settings.ParserSettings
import pekko.http.impl.model.parser.CharacterClasses
+import pekko.http.impl.util.HttpConstants._
import pekko.util.ByteString
import pekko.http.scaladsl.model.{ ParsingException => _, _ }
import pekko.http.scaladsl.model.headers._
import ParserOutput._
-import pekko.annotation.InternalApi
import pekko.http.impl.util.LogByteStringTools
import pekko.stream.scaladsl.Source
@@ -61,7 +63,7 @@ private[http] class HttpResponseParser(protected val
settings: ParserSettings,
override protected def parseMessage(input: ByteString, offset: Int):
StateResult =
if (contextForCurrentResponse.isDefined) {
var cursor = parseProtocol(input, offset)
- if (byteChar(input, cursor) == ' ') {
+ if (byteAt(input, cursor) == SPACE_BYTE) {
cursor = parseStatus(input, cursor + 1)
parseHeaderLines(input, cursor)
} else onBadProtocol(input.drop(cursor))
@@ -104,8 +106,8 @@ private[http] class HttpResponseParser(protected val
settings: ParserSettings,
}
}
- def isLF(idx: Int) = byteChar(input, idx) == '\n'
- def isCRLF(idx: Int) = byteChar(input, idx) == '\r' && isLF(idx + 1)
+ def isLF(idx: Int) = byteAt(input, idx) == LF_BYTE
+ def isCRLF(idx: Int) = byteAt(input, idx) == CR_BYTE && isLF(idx + 1)
def isNewLine(idx: Int) = isLF(idx) || isCRLF(idx)
def skipNewLine(idx: Int) = {
@@ -114,7 +116,7 @@ private[http] class HttpResponseParser(protected val
settings: ParserSettings,
else idx
}
- if (byteChar(input, cursor + 3) == ' ') {
+ if (byteAt(input, cursor + 3) == SPACE_BYTE) {
val startIdx = cursor + 4
@tailrec def scanNewLineIdx(idx: Int): Int =
if (idx - startIdx <= maxResponseReasonLength)
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/SpecializedHeaderValueParsers.scala
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/SpecializedHeaderValueParsers.scala
index 2cde6dd8a..3b2977217 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/SpecializedHeaderValueParsers.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/impl/engine/parsing/SpecializedHeaderValueParsers.scala
@@ -19,6 +19,7 @@ import pekko.annotation.InternalApi
import scala.annotation.tailrec
import pekko.util.ByteString
import pekko.http.impl.model.parser.CharacterClasses._
+import pekko.http.impl.util.HttpConstants._
import pekko.http.scaladsl.model.{ ErrorInfo, HttpHeader }
import pekko.http.scaladsl.model.headers.`Content-Length`
@@ -39,7 +40,7 @@ private[parsing] object SpecializedHeaderValueParsers {
if (result < 0) fail("`Content-Length` header value must not exceed
63-bit integer range")
else if (DIGIT(c)) recurse(ix + 1, result * 10 + c - '0')
else if (WSP(c)) recurse(ix + 1, result)
- else if (c == '\r' && byteChar(input, ix + 1) == '\n')
(`Content-Length`(result), ix + 2)
+ else if (c == '\r' && byteAt(input, ix + 1) == LF_BYTE)
(`Content-Length`(result), ix + 2)
else if (c == '\n') (`Content-Length`(result), ix + 1)
else fail("Illegal `Content-Length` header value")
}
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/CharacterClasses.scala
b/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/CharacterClasses.scala
index 35bbde9ae..f7f1ca542 100644
---
a/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/CharacterClasses.scala
+++
b/http-core/src/main/scala/org/apache/pekko/http/impl/model/parser/CharacterClasses.scala
@@ -23,15 +23,15 @@ private[http] object CharacterClasses {
def ALPHA = CharPredicate.Alpha
def LOWER_ALPHA = CharPredicate.LowerAlpha
def UPPER_ALPHA = CharPredicate.UpperAlpha
- def CR = '\r'
+ final val CR = '\r'
val CTL = CharPredicate('\u0000' to '\u001F', '\u007F')
def DIGIT = CharPredicate.Digit
def ALPHANUM = CharPredicate.AlphaNum
- def DQUOTE = '"'
+ final val DQUOTE = '"'
def HEXDIG = CharPredicate.HexDigit
- def HTAB = '\t'
- def LF = '\n'
- def SP = ' '
+ final val HTAB = '\t'
+ final val LF = '\n'
+ final val SP = ' '
def VCHAR = CharPredicate.Visible
val WSP = CharPredicate(SP, HTAB)
val WSPCRLF = WSP ++ CR ++ LF
diff --git
a/http-core/src/main/scala/org/apache/pekko/http/impl/util/HttpConstants.scala
b/http-core/src/main/scala/org/apache/pekko/http/impl/util/HttpConstants.scala
new file mode 100644
index 000000000..0252474fd
--- /dev/null
+++
b/http-core/src/main/scala/org/apache/pekko/http/impl/util/HttpConstants.scala
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package org.apache.pekko.http.impl.util
+
+import org.apache.pekko.annotation.InternalApi
+
+/**
+ * INTERNAL API
+ *
+ * This object contains HTTP related constants that are used in various places.
+ * It is not intended to be used outside of the HTTP implementation.
+ */
+@InternalApi
+private[http] object HttpConstants {
+ final val CR_BYTE: Byte = 13
+ final val LF_BYTE: Byte = 10
+ final val SPACE_BYTE: Byte = 32
+ final val DASH_BYTE: Byte = 45 // '-' (minus, dash, hyphen)
+}
diff --git
a/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/sse/LineParser.scala
b/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/sse/LineParser.scala
index 71bcb9ae3..17680994f 100644
---
a/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/sse/LineParser.scala
+++
b/http/src/main/scala/org/apache/pekko/http/scaladsl/unmarshalling/sse/LineParser.scala
@@ -18,6 +18,7 @@ package sse
import org.apache.pekko
import pekko.annotation.InternalApi
+import pekko.http.impl.util.HttpConstants
import pekko.stream.stage.{ GraphStage, GraphStageLogic, InHandler, OutHandler
}
import pekko.stream.{ Attributes, FlowShape, Inlet, Outlet }
import pekko.util.ByteString
@@ -26,8 +27,8 @@ import scala.annotation.tailrec
/** INTERNAL API */
@InternalApi
private object LineParser {
- val CR = '\r'.toByte
- val LF = '\n'.toByte
+ val CR = HttpConstants.CR_BYTE
+ val LF = HttpConstants.LF_BYTE
}
/** INTERNAL API */
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]