=htj #16436 add missing Java directives

This commit is contained in:
Johannes Rudolph 2015-06-18 16:44:16 +02:00 committed by Mathias
parent 10ea40b2f8
commit 83833ae4f8
59 changed files with 2595 additions and 236 deletions

View file

@ -4,7 +4,10 @@
package akka.http.impl.server
import akka.http.impl.util.JavaMapping
import akka.http.javadsl.server.values.{ PathMatcher, BasicUserCredentials }
import akka.http.scaladsl.model.StatusCodes.Redirection
import akka.http.scaladsl.server.util.TupleOps.Join
import scala.language.implicitConversions
import scala.annotation.tailrec
@ -13,8 +16,8 @@ import akka.http.javadsl.model.ContentType
import akka.http.scaladsl.server.directives.{ UserCredentials, ContentTypeResolver }
import akka.http.scaladsl.server.directives.FileAndResourceDirectives.DirectoryRenderer
import akka.http.scaladsl.model.HttpHeader
import akka.http.scaladsl.model.headers.CustomHeader
import akka.http.scaladsl.server.{ Route ScalaRoute, Directive0, Directives }
import akka.http.scaladsl.model.headers.{ HttpCookie, CustomHeader }
import akka.http.scaladsl.server.{ Route ScalaRoute, Directive ScalaDirective, PathMatcher ScalaPathMatcher, PathMatcher1, Directive0, Directive1, Directives }
import akka.http.impl.util.JavaMapping.Implicits._
import akka.http.scaladsl.server
import akka.http.javadsl.server._
@ -26,11 +29,13 @@ import RouteStructure._
private[http] trait ExtractionMap extends CustomHeader {
def get[T](key: RequestVal[T]): Option[T]
def set[T](key: RequestVal[T], value: T): ExtractionMap
def addAll(values: Map[RequestVal[_], Any]): ExtractionMap
}
/**
* INTERNAL API
*/
private[http] object ExtractionMap {
val Empty = ExtractionMap(Map.empty)
implicit def apply(map: Map[RequestVal[_], Any]): ExtractionMap =
new ExtractionMap {
def get[T](key: RequestVal[T]): Option[T] =
@ -39,6 +44,9 @@ private[http] object ExtractionMap {
def set[T](key: RequestVal[T], value: T): ExtractionMap =
ExtractionMap(map.updated(key, value))
def addAll(values: Map[RequestVal[_], Any]): ExtractionMap =
ExtractionMap(map ++ values)
// CustomHeader methods
override def suppressRendering: Boolean = true
def name(): String = "ExtractedValues"
@ -50,99 +58,146 @@ private[http] object ExtractionMap {
* INTERNAL API
*/
private[http] object RouteImplementation extends Directives with server.RouteConcatenation {
def apply(route: Route): ScalaRoute = route match {
case RouteAlternatives(children)
val converted = children.map(RouteImplementation.apply)
converted.reduce(_ ~ _)
case RawPathPrefix(elements, children)
val inner = apply(RouteAlternatives(children))
def apply(route: Route): ScalaRoute = {
def directiveFor(route: DirectiveRoute): Directive0 = route match {
case RouteAlternatives() ScalaDirective.Empty
case RawPathPrefix(elements) pathMatcherDirective[String](elements, rawPathPrefix)
case RawPathPrefixTest(elements) pathMatcherDirective[String](elements, rawPathPrefixTest)
case PathSuffix(elements) pathMatcherDirective[String](elements, pathSuffix)
case PathSuffixTest(elements) pathMatcherDirective[String](elements, pathSuffixTest)
case RedirectToTrailingSlashIfMissing(code) redirectToTrailingSlashIfMissing(code.asScala.asInstanceOf[Redirection])
case RedirectToNoTrailingSlashIfPresent(code) redirectToNoTrailingSlashIfPresent(code.asScala.asInstanceOf[Redirection])
def one[T](matcher: PathMatcher[T]): Directive0 =
rawPathPrefix(matcher.asInstanceOf[PathMatcherImpl[T]].matcher) flatMap { value
addExtraction(matcher, value)
case MethodFilter(m) method(m.asScala)
case Extract(extractions)
extractRequestContext.flatMap { ctx
extractions.map { e
e.directive.flatMap(addExtraction(e.asInstanceOf[RequestVal[Any]], _))
}.reduce(_ & _)
}
elements.map(one(_)).reduce(_ & _).apply(inner)
case GetFromResource(path, contentType, classLoader)
getFromResource(path, contentType.asScala, classLoader)
case GetFromResourceDirectory(path, classLoader, resolver)
getFromResourceDirectory(path, classLoader)(scalaResolver(resolver))
case GetFromFile(file, contentType)
getFromFile(file, contentType.asScala)
case GetFromDirectory(directory, true, resolver)
extractExecutionContext { implicit ec
getFromBrowseableDirectory(directory.getPath)(DirectoryRenderer.defaultDirectoryRenderer, scalaResolver(resolver))
}
case FileAndResourceRouteWithDefaultResolver(constructor)
RouteImplementation(constructor(new directives.ContentTypeResolver {
def resolve(fileName: String): ContentType = ContentTypeResolver.Default(fileName)
}))
case BasicAuthentication(authenticator)
authenticateBasicAsync(authenticator.realm, { creds
val javaCreds =
creds match {
case UserCredentials.Missing
new BasicUserCredentials {
def available: Boolean = false
def userName: String = throw new IllegalStateException("Credentials missing")
def verifySecret(secret: String): Boolean = throw new IllegalStateException("Credentials missing")
}
case p @ UserCredentials.Provided(name)
new BasicUserCredentials {
def available: Boolean = true
def userName: String = name
def verifySecret(secret: String): Boolean = p.verifySecret(secret)
}
}
case MethodFilter(m, children)
val inner = apply(RouteAlternatives(children))
method(m.asScala).apply(inner)
authenticator.authenticate(javaCreds)
}).flatMap { user
addExtraction(authenticator.asInstanceOf[RequestVal[Any]], user)
}
case Extract(extractions, children)
val inner = apply(RouteAlternatives(children))
extractRequestContext.flatMap { ctx
extractions.map { e
e.directive.flatMap(addExtraction(e.asInstanceOf[RequestVal[Any]], _))
}.reduce(_ & _)
}.apply(inner)
case EncodeResponse(coders)
val scalaCoders = coders.map(_._underlyingScalaCoder())
encodeResponseWith(scalaCoders.head, scalaCoders.tail: _*)
case BasicAuthentication(authenticator, children)
val inner = apply(RouteAlternatives(children))
authenticateBasicAsync(authenticator.realm, { creds
val javaCreds =
creds match {
case UserCredentials.Missing
new BasicUserCredentials {
def available: Boolean = false
def userName: String = throw new IllegalStateException("Credentials missing")
def verifySecret(secret: String): Boolean = throw new IllegalStateException("Credentials missing")
}
case p @ UserCredentials.Provided(name)
new BasicUserCredentials {
def available: Boolean = true
def userName: String = name
def verifySecret(secret: String): Boolean = p.verifySecret(secret)
}
}
case DecodeRequest(coders) decodeRequestWith(coders.map(_._underlyingScalaCoder()): _*)
case Conditional(eTag, lastModified) conditional(eTag.map(_.asScala), lastModified.map(_.asScala))
case h: HostFilter host(h.filter _)
case SchemeFilter(schemeName) scheme(schemeName)
authenticator.authenticate(javaCreds)
}).flatMap { user
addExtraction(authenticator.asInstanceOf[RequestVal[Any]], user)
}.apply(inner)
case HandleExceptions(handler)
val pf: akka.http.scaladsl.server.ExceptionHandler = akka.http.scaladsl.server.ExceptionHandler {
case e: RuntimeException apply(handler.handle(e))
}
handleExceptions(pf)
case EncodeResponse(coders, children)
val scalaCoders = coders.map(_._underlyingScalaCoder())
encodeResponseWith(scalaCoders.head, scalaCoders.tail: _*).apply(apply(RouteAlternatives(children)))
case Validated(isValid, errorMsg) validate(isValid, errorMsg)
case RangeSupport() withRangeSupport
case SetCookie(cookie) setCookie(cookie.asScala)
case DeleteCookie(name, domain, path) deleteCookie(HttpCookie(name, domain = domain, path = path, value = "deleted"))
}
case Conditional(eTag, lastModified, children)
conditional(eTag.asScala, lastModified.asScala).apply(apply(RouteAlternatives(children)))
route match {
case route: DirectiveRoute directiveFor(route).apply(fromAlternatives(route.children))
case GetFromResource(path, contentType, classLoader) getFromResource(path, contentType.asScala, classLoader)
case GetFromResourceDirectory(path, classLoader, resolver) getFromResourceDirectory(path, classLoader)(scalaResolver(resolver))
case GetFromFile(file, contentType) getFromFile(file, contentType.asScala)
case GetFromDirectory(directory, true, resolver)
extractExecutionContext { implicit ec
getFromBrowseableDirectory(directory.getPath)(DirectoryRenderer.defaultDirectoryRenderer, scalaResolver(resolver))
}
case FileAndResourceRouteWithDefaultResolver(constructor)
RouteImplementation(constructor(new directives.ContentTypeResolver {
def resolve(fileName: String): ContentType = ContentTypeResolver.Default(fileName)
}))
case HandleExceptions(handler, children)
val pf: akka.http.scaladsl.server.ExceptionHandler = akka.http.scaladsl.server.ExceptionHandler {
case e: RuntimeException apply(handler.handle(e))
}
handleExceptions(pf).apply(apply(RouteAlternatives(children)))
case HandleWebsocketMessages(handler) handleWebsocketMessages(JavaMapping.toScala(handler))
case Redirect(uri, code) redirect(uri.asScala, code.asScala.asInstanceOf[Redirection]) // guarded by require in Redirect
case o: OpaqueRoute
(ctx o.handle(new RequestContextImpl(ctx)).asInstanceOf[RouteResultImpl].underlying)
case dyn: DynamicDirectiveRoute1[t1Type]
def runToRoute(t1: t1Type): ScalaRoute =
apply(dyn.createDirective(t1).route(dyn.innerRoute, dyn.moreInnerRoutes: _*))
case p: Product extractExecutionContext { implicit ec complete(500, s"Not implemented: ${p.productPrefix}") }
requestValToDirective(dyn.value1)(runToRoute)
case dyn: DynamicDirectiveRoute2[t1Type, t2Type]
def runToRoute(t1: t1Type, t2: t2Type): ScalaRoute =
apply(dyn.createDirective(t1, t2).route(dyn.innerRoute, dyn.moreInnerRoutes: _*))
(requestValToDirective(dyn.value1) & requestValToDirective(dyn.value2))(runToRoute)
case o: OpaqueRoute (ctx o.handle(new RequestContextImpl(ctx)).asInstanceOf[RouteResultImpl].underlying)
case p: Product extractExecutionContext { implicit ec complete(500, s"Not implemented: ${p.productPrefix}") }
}
}
def pathMatcherDirective[T](matchers: immutable.Seq[PathMatcher[_]],
directive: PathMatcher1[T] Directive1[T] // this type is too specific and only a placeholder for a proper polymorphic function
): Directive0 = {
// Concatenating PathMatchers is a bit complicated as we don't want to build up a tuple
// but something which we can later split all the separate values and add them to the
// ExtractionMap.
//
// This is achieved by providing a specialized `Join` instance to use with PathMatcher
// which provides the desired behavior.
type ValMap = Tuple1[Map[RequestVal[_], Any]]
object AddToMapJoin extends Join[ValMap, ValMap] {
type Out = ValMap
def apply(prefix: ValMap, suffix: ValMap): AddToMapJoin.Out =
Tuple1(prefix._1 ++ suffix._1)
}
def toScala(matcher: PathMatcher[_]): ScalaPathMatcher[ValMap] =
matcher.asInstanceOf[PathMatcherImpl[_]].matcher.transform(_.map(v Tuple1(Map(matcher -> v._1))))
def addExtractions(valMap: T): Directive0 = transformExtractionMap(_.addAll(valMap.asInstanceOf[Map[RequestVal[_], Any]]))
val reduced: ScalaPathMatcher[ValMap] = matchers.map(toScala).reduce(_.~(_)(AddToMapJoin))
directive(reduced.asInstanceOf[PathMatcher1[T]]).flatMap(addExtractions)
}
def addExtraction[T](key: RequestVal[T], value: T): Directive0 = {
@tailrec def addToExtractionMap(headers: immutable.Seq[HttpHeader], prefix: Vector[HttpHeader] = Vector.empty): immutable.Seq[HttpHeader] =
def fromAlternatives(alternatives: Seq[Route]): ScalaRoute =
alternatives.map(RouteImplementation.apply).reduce(_ ~ _)
def addExtraction[T](key: RequestVal[T], value: T): Directive0 =
transformExtractionMap(_.set(key, value))
def transformExtractionMap(f: ExtractionMap ExtractionMap): Directive0 = {
@tailrec def updateExtractionMap(headers: immutable.Seq[HttpHeader], prefix: Vector[HttpHeader] = Vector.empty): immutable.Seq[HttpHeader] =
headers match {
case (m: ExtractionMap) +: rest m.set(key, value) +: (prefix ++ rest)
case other +: rest addToExtractionMap(rest, prefix :+ other)
case Nil ExtractionMap(Map(key -> value)) +: prefix
case (m: ExtractionMap) +: rest f(m) +: (prefix ++ rest)
case other +: rest updateExtractionMap(rest, prefix :+ other)
case Nil f(ExtractionMap.Empty) +: prefix
}
mapRequest(_.mapHeaders(addToExtractionMap(_)))
mapRequest(_.mapHeaders(updateExtractionMap(_)))
}
private def scalaResolver(resolver: directives.ContentTypeResolver): ContentTypeResolver =
ContentTypeResolver(f resolver.resolve(f).asScala)
def requestValToDirective[T](value: RequestVal[T]): Directive1[T] =
value match {
case s: StandaloneExtractionImpl[_] s.directive
case v: RequestVal[_] extract(ctx v.get(new RequestContextImpl(ctx)))
}
}