pekko/akka-http/src/main/scala/akka/http/impl/server/RouteImplementation.scala

149 lines
6 KiB
Scala
Raw Normal View History

/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package akka.http.impl.server
import akka.http.javadsl.server.values.{ PathMatcher, BasicUserCredentials }
import scala.language.implicitConversions
import scala.annotation.tailrec
import scala.collection.immutable
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.impl.util.JavaMapping.Implicits._
import akka.http.scaladsl.server
import akka.http.javadsl.server._
import RouteStructure._
/**
* INTERNAL API
*/
private[http] trait ExtractionMap extends CustomHeader {
def get[T](key: RequestVal[T]): Option[T]
def set[T](key: RequestVal[T], value: T): ExtractionMap
}
/**
* INTERNAL API
*/
private[http] object ExtractionMap {
implicit def apply(map: Map[RequestVal[_], Any]): ExtractionMap =
new ExtractionMap {
def get[T](key: RequestVal[T]): Option[T] =
map.get(key).asInstanceOf[Option[T]]
def set[T](key: RequestVal[T], value: T): ExtractionMap =
ExtractionMap(map.updated(key, value))
// CustomHeader methods
override def suppressRendering: Boolean = true
def name(): String = "ExtractedValues"
def value(): String = "<empty>"
}
}
/**
* 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 one[T](matcher: PathMatcher[T]): Directive0 =
rawPathPrefix(matcher.asInstanceOf[PathMatcherImpl[T]].matcher) flatMap { value
addExtraction(matcher, value)
}
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 MethodFilter(m, children)
val inner = apply(RouteAlternatives(children))
method(m.asScala).apply(inner)
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 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)
}
}
authenticator.authenticate(javaCreds)
}).flatMap { user
addExtraction(authenticator.asInstanceOf[RequestVal[Any]], user)
}.apply(inner)
case EncodeResponse(coders, children)
val scalaCoders = coders.map(_._underlyingScalaCoder())
encodeResponseWith(scalaCoders.head, scalaCoders.tail: _*).apply(apply(RouteAlternatives(children)))
case Conditional(eTag, lastModified, children)
conditional(eTag.asScala, lastModified.asScala).apply(apply(RouteAlternatives(children)))
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 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 addExtraction[T](key: RequestVal[T], value: T): Directive0 = {
@tailrec def addToExtractionMap(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
}
mapRequest(_.mapHeaders(addToExtractionMap(_)))
}
private def scalaResolver(resolver: directives.ContentTypeResolver): ContentTypeResolver =
ContentTypeResolver(f resolver.resolve(f).asScala)
}