Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 clearly safe

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, in hindsight, this might be the riskiest change. CharUtils.toLowerCase only lower cases the 26 standard letters of the latin alphabet (the ones in 7 bit ASCII table) while java.lang.Character.toLowerCase seems to support other letters that appear in Unicode table. Let me re-review this change.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually with (char1 | char2) < 0x80 in the check, I think we can assume that it doesn't matter that Character.toLowerCase can handle non-ASCII chars.

stringEquals(at + 1, length)
} else true

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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))) }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we assume lastChar is 7-bit ascii here? or are we OK with letting other characters through?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Parboiled CharUtils only deals with the 26 standard letters of the latin alphabet (the ones in 7 bit ASCII table) - same as the pekko-http internal toLowerCase.


private def savePath() = rule { run(setPath(Path(sb.toString, uriParsingCharset))) }

Expand Down
Original file line number Diff line number Diff line change
@@ -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. <https://www.lightbend.com>
*/

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

}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down