+htc #18898 modeledCustomHeader to ease matching on headers

This commit is contained in:
Konrad Malawski 2015-11-24 11:59:57 +01:00
parent 644555a93f
commit 6f5d449bd0
6 changed files with 212 additions and 20 deletions

View file

@ -0,0 +1,113 @@
/*
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.scaladsl.server
import akka.http.scaladsl.model.{ HttpHeader, StatusCodes }
import akka.http.scaladsl.model.headers._
import scala.concurrent.Future
import scala.util.{ Success, Failure, Try }
object ModeledCustomHeaderSpec {
//#modeled-api-key-custom-header
object ApiTokenHeader extends ModeledCustomHeaderCompanion[ApiTokenHeader] {
override val name = "apiKey"
override def parse(value: String) = Try(new ApiTokenHeader(value))
}
final class ApiTokenHeader(token: String) extends ModeledCustomHeader[ApiTokenHeader] {
override val companion = ApiTokenHeader
override def value: String = token
}
//#modeled-api-key-custom-header
object DifferentHeader extends ModeledCustomHeaderCompanion[DifferentHeader] {
override val name = "different"
override def parse(value: String) =
if (value contains " ") Failure(new Exception("Contains illegal whitespace!"))
else Success(new DifferentHeader(value))
}
final class DifferentHeader(token: String) extends ModeledCustomHeader[DifferentHeader] {
override val companion = DifferentHeader
override def value = token
}
}
class ModeledCustomHeaderSpec extends RoutingSpec {
import ModeledCustomHeaderSpec._
"CustomHeader" should {
"be able to be extracted using expected syntax" in {
//#matching-examples
val ApiTokenHeader(t1) = ApiTokenHeader("token")
t1 should ===("token")
val RawHeader(k2, v2) = ApiTokenHeader("token")
k2 should ===("apiKey")
v2 should ===("token")
// will match, header keys are case insensitive
val ApiTokenHeader(v3) = RawHeader("APIKEY", "token")
v3 should ===("token")
intercept[MatchError] {
// won't match, different header name
val ApiTokenHeader(v4) = DifferentHeader("token")
}
intercept[MatchError] {
// won't match, different header name
val RawHeader("something", v5) = DifferentHeader("token")
}
intercept[MatchError] {
// won't match, different header name
val ApiTokenHeader(v6) = RawHeader("different", "token")
}
//#matching-examples
}
"be able to match from RawHeader" in {
//#matching-in-routes
def extractFromCustomHeader = headerValuePF {
case t @ ApiTokenHeader(token) s"extracted> $t"
case raw: RawHeader s"raw> $raw"
}
val routes = extractFromCustomHeader { s
complete(s)
}
Get().withHeaders(RawHeader("apiKey", "TheKey")) ~> routes ~> check {
status should ===(StatusCodes.OK)
responseAs[String] should ===("extracted> apiKey: TheKey")
}
Get().withHeaders(RawHeader("somethingElse", "TheKey")) ~> routes ~> check {
status should ===(StatusCodes.OK)
responseAs[String] should ===("raw> somethingElse: TheKey")
}
Get().withHeaders(ApiTokenHeader("TheKey")) ~> routes ~> check {
status should ===(StatusCodes.OK)
responseAs[String] should ===("extracted> apiKey: TheKey")
}
//#matching-in-routes
}
"fail with useful message when unable to parse" in {
val ex = intercept[Exception] {
DifferentHeader("Hello world") // illegal " "
}
ex.getMessage should ===("Unable to construct custom header by parsing: 'Hello world'")
ex.getCause.getMessage should include("whitespace")
}
}
}

View file

@ -92,9 +92,7 @@ class MultipartUnmarshallersSpec extends FreeSpec with Matchers with BeforeAndAf
|filecontent
|--12345--""".stripMarginWithNewline("\r\n"))).to[Multipart.General] should haveParts(
Multipart.General.BodyPart.Strict(HttpEntity(ContentTypes.`text/plain(UTF-8)`, "first part, with a trailing newline\r\n")),
Multipart.General.BodyPart.Strict(
HttpEntity(`application/octet-stream`, "filecontent"),
List(RawHeader("Content-Transfer-Encoding", "binary"))))
Multipart.General.BodyPart.Strict(HttpEntity(`application/octet-stream`, "filecontent"), List(RawHeader("Content-Transfer-Encoding", "binary"))))
}
"illegal headers" in (
Unmarshal(HttpEntity(`multipart/form-data` withBoundary "XYZABC",