=htj #16436 add missing Java directives
This commit is contained in:
parent
10ea40b2f8
commit
83833ae4f8
59 changed files with 2595 additions and 236 deletions
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue