diff --git a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/headers.scala b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/headers.scala index bf13d2a98e..e19385dafd 100644 --- a/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/headers.scala +++ b/akka-http-core/src/main/scala/akka/http/scaladsl/model/headers/headers.scala @@ -21,7 +21,7 @@ import akka.http.javadsl.{ model ⇒ jm } import akka.http.scaladsl.model._ sealed abstract class ModeledCompanion[T: ClassTag] extends Renderable { - val name = getClass.getSimpleName.replace("$minus", "-").dropRight(1) // trailing $ + val name = ModeledCompanion.nameFromClass(getClass) val lowercaseName = name.toRootLowerCase private[this] val nameBytes = name.asciiBytes final def render[R <: Rendering](r: R): r.type = r ~~ nameBytes ~~ ':' ~~ ' ' @@ -36,6 +36,14 @@ sealed abstract class ModeledCompanion[T: ClassTag] extends Renderable { case res ⇒ Left(res.errors) } } +/** INTERNAL API */ +private[akka] object ModeledCompanion { + def nameFromClass[T](clazz: Class[T]): String = { + val name = clazz.getSimpleName.replace("$minus", "-") + if (name.last == '$') name.dropRight(1) // trailing $ + else name + } +} sealed trait ModeledHeader extends HttpHeader with Serializable { def renderInRequests: Boolean = false // default implementation diff --git a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/HeaderDirectivesSpec.scala b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/HeaderDirectivesSpec.scala index 4fe55462cc..1c0b893b64 100644 --- a/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/HeaderDirectivesSpec.scala +++ b/akka-http-tests/src/test/scala/akka/http/scaladsl/server/directives/HeaderDirectivesSpec.scala @@ -34,7 +34,7 @@ class HeaderDirectivesSpec extends RoutingSpec with Inside { } "The headerValueByType directive" should { - lazy val route = + val route = headerValueByType[Origin]() { origin ⇒ complete(s"The first origin was ${origin.origins.head}") } @@ -51,6 +51,16 @@ class HeaderDirectivesSpec extends RoutingSpec with Inside { } } } + "reject a request for missing header, and format it properly when header included special characters (e.g. `-`)" in { + val route = headerValueByType[`User-Agent`]() { agent ⇒ + complete(s"Agent: ${agent}") + } + Get("abc") ~> route ~> check { + inside(rejection) { + case MissingHeaderRejection("User-Agent") ⇒ + } + } + } } "The headerValueByName directive" should { diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala index 87ae5a1910..0243e58990 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/server/directives/HeaderDirectives.scala @@ -7,7 +7,7 @@ package directives import akka.http.impl.util._ import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.{ HttpOriginRange, ModeledCustomHeader, ModeledCustomHeaderCompanion, Origin } +import akka.http.scaladsl.model.headers._ import scala.reflect.ClassTag import scala.util.control.NonFatal @@ -91,7 +91,7 @@ trait HeaderDirectives { * @group header */ def headerValueByType[T](magnet: HeaderMagnet[T]): Directive1[T] = - headerValuePF(magnet.extractPF) | reject(MissingHeaderRejection(magnet.runtimeClass.getSimpleName)) + headerValuePF(magnet.extractPF) | reject(MissingHeaderRejection(magnet.headerName)) //#optional-header /** @@ -159,6 +159,7 @@ object HeaderDirectives extends HeaderDirectives trait HeaderMagnet[T] { def classTag: ClassTag[T] def runtimeClass: Class[T] + def headerName = ModeledCompanion.nameFromClass(runtimeClass) /** * Returns a partial function that checks if the input value is of runtime type