=doc Significantly extend HTTP documentation with new content and ports from spray docs

This commit is contained in:
Mathias 2015-05-11 23:05:18 +02:00 committed by Johannes Rudolph
parent 6149b328a0
commit 20759e1b34
238 changed files with 6541 additions and 1563 deletions

View file

@ -1,73 +0,0 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http
import akka.actor.ActorSystem
import akka.http.scaladsl.model._
import akka.stream.scaladsl._
import akka.stream.testkit.AkkaSpec
import scala.concurrent.Future
class HttpServerExampleSpec
extends AkkaSpec("akka.actor.default-mailbox.mailbox-type = akka.dispatch.UnboundedMailbox") {
def ActorSystem(): ActorSystem = system
"binding example" in {
//#bind-example
import akka.http.scaladsl.Http
import akka.stream.ActorFlowMaterializer
implicit val system = ActorSystem()
implicit val materializer = ActorFlowMaterializer()
val serverSource: Source[Http.IncomingConnection, Future[Http.ServerBinding]] =
Http(system).bind(interface = "localhost", port = 8080)
val bindingFuture: Future[Http.ServerBinding] = serverSource.to(Sink.foreach { connection =>
// foreach materializes the source
println("Accepted new connection from " + connection.remoteAddress)
// ... and then actually handle the connection
}).run()
//#bind-example
}
"full-server-example" in {
import akka.http.scaladsl.Http
import akka.stream.ActorFlowMaterializer
implicit val system = ActorSystem()
implicit val materializer = ActorFlowMaterializer()
val serverSource = Http(system).bind(interface = "localhost", port = 8080)
//#full-server-example
import akka.http.scaladsl.model.HttpMethods._
import akka.stream.scaladsl.{ Flow, Sink }
val requestHandler: HttpRequest => HttpResponse = {
case HttpRequest(GET, Uri.Path("/"), _, _, _) =>
HttpResponse(
entity = HttpEntity(MediaTypes.`text/html`,
"<html><body>Hello world!</body></html>"))
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
HttpResponse(entity = "PONG!")
case HttpRequest(GET, Uri.Path("/crash"), _, _, _) =>
sys.error("BOOM!")
case _: HttpRequest =>
HttpResponse(404, entity = "Unknown resource!")
}
val bindingFuture: Future[Http.ServerBinding] = serverSource.to(Sink.foreach { connection =>
println("Accepted new connection from " + connection.remoteAddress)
connection handleWithSyncHandler requestHandler
// this is equivalent to
// connection handleWith { Flow[HttpRequest] map requestHandler }
}).run()
//#full-server-example
}
}

View file

@ -0,0 +1,71 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl
import akka.actor.ActorSystem
import org.scalatest.{ Matchers, WordSpec }
class HttpClientExampleSpec extends WordSpec with Matchers {
"outgoing-connection-example" in {
pending // compile-time only test
//#outgoing-connection-example
import scala.concurrent.Future
import akka.stream.ActorFlowMaterializer
import akka.stream.scaladsl._
import akka.http.scaladsl.model._
import akka.http.scaladsl.Http
implicit val system = ActorSystem()
implicit val materializer = ActorFlowMaterializer()
val connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] =
Http().outgoingConnection("http://akka.io")
val responseFuture: Future[HttpResponse] =
Source.single(HttpRequest(uri = "/"))
.via(connectionFlow)
.runWith(Sink.head)
//#outgoing-connection-example
}
"host-level-example" in {
pending // compile-time only test
//#host-level-example
import scala.concurrent.Future
import scala.util.Try
import akka.stream.ActorFlowMaterializer
import akka.stream.scaladsl._
import akka.http.scaladsl.model._
import akka.http.scaladsl.Http
implicit val system = ActorSystem()
implicit val materializer = ActorFlowMaterializer()
// construct a pool client flow with context type `Int`
val poolClientFlow = Http().cachedHostConnectionPool[Int]("http://akka.io")
val responseFuture: Future[(Try[HttpResponse], Int)] =
Source.single(HttpRequest(uri = "/") -> 42)
.via(poolClientFlow)
.runWith(Sink.head)
//#host-level-example
}
"single-request-example" in {
pending // compile-time only test
//#single-request-example
import scala.concurrent.Future
import akka.stream.ActorFlowMaterializer
import akka.http.scaladsl.model._
import akka.http.scaladsl.Http
implicit val system = ActorSystem()
implicit val materializer = ActorFlowMaterializer()
val responseFuture: Future[HttpResponse] =
Http().singleRequest(HttpRequest(uri = "http://akka.io"))
//#single-request-example
}
}

View file

@ -0,0 +1,256 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl
import scala.concurrent.Future
import org.scalatest.{ WordSpec, Matchers }
import akka.actor.ActorSystem
class HttpServerExampleSpec extends WordSpec with Matchers {
"binding-example" in {
import akka.stream.ActorFlowMaterializer
import akka.stream.scaladsl._
import akka.http.scaladsl.Http
implicit val system = ActorSystem()
implicit val materializer = ActorFlowMaterializer()
val serverSource: Source[Http.IncomingConnection, Future[Http.ServerBinding]] =
Http().bind(interface = "localhost", port = 8080)
val bindingFuture: Future[Http.ServerBinding] =
serverSource.to(Sink.foreach { connection => // foreach materializes the source
println("Accepted new connection from " + connection.remoteAddress)
// ... and then actually handle the connection
}).run()
}
"full-server-example" in {
import akka.stream.ActorFlowMaterializer
import akka.stream.scaladsl.Sink
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpMethods._
import akka.http.scaladsl.model._
implicit val system = ActorSystem()
implicit val materializer = ActorFlowMaterializer()
val serverSource = Http().bind(interface = "localhost", port = 8080)
val requestHandler: HttpRequest => HttpResponse = {
case HttpRequest(GET, Uri.Path("/"), _, _, _) =>
HttpResponse(entity = HttpEntity(MediaTypes.`text/html`,
"<html><body>Hello world!</body></html>"))
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
HttpResponse(entity = "PONG!")
case HttpRequest(GET, Uri.Path("/crash"), _, _, _) =>
sys.error("BOOM!")
case _: HttpRequest =>
HttpResponse(404, entity = "Unknown resource!")
}
val bindingFuture: Future[Http.ServerBinding] =
serverSource.to(Sink.foreach { connection =>
println("Accepted new connection from " + connection.remoteAddress)
connection handleWithSyncHandler requestHandler
// this is equivalent to
// connection handleWith { Flow[HttpRequest] map requestHandler }
}).run()
}
"low-level-server-example" in {
import akka.stream.ActorFlowMaterializer
import akka.http.scaladsl.Http
import akka.http.scaladsl.model.HttpMethods._
import akka.http.scaladsl.model._
implicit val system = ActorSystem()
implicit val materializer = ActorFlowMaterializer()
val requestHandler: HttpRequest => HttpResponse = {
case HttpRequest(GET, Uri.Path("/"), _, _, _) =>
HttpResponse(entity = HttpEntity(MediaTypes.`text/html`,
"<html><body>Hello world!</body></html>"))
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
HttpResponse(entity = "PONG!")
case HttpRequest(GET, Uri.Path("/crash"), _, _, _) =>
sys.error("BOOM!")
case _: HttpRequest =>
HttpResponse(404, entity = "Unknown resource!")
}
Http().bindAndHandleSync(requestHandler, "localhost", 8080)
}
// format: OFF
"high-level-server-example" in {
import akka.stream.ActorFlowMaterializer
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._
implicit val system = ActorSystem()
implicit val materializer = ActorFlowMaterializer()
val route =
get {
pathSingleSlash {
complete {
<html>
<body>Hello world!</body>
</html>
}
} ~
path("ping") {
complete("PONG!")
} ~
path("crash") {
sys.error("BOOM!")
}
}
Http().bindAndHandle(route, "localhost", 8080)
}
"minimal-routing-example" in {
import akka.stream.ActorFlowMaterializer
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._
object Main extends App {
implicit val system = ActorSystem("my-system")
implicit val materializer = ActorFlowMaterializer()
val route =
path("hello") {
get {
complete {
<h1>Say hello to spray</h1>
}
}
}
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
Console.readLine()
import system.dispatcher // for the future transformations
bindingFuture
.flatMap(_.unbind()) // trigger unbinding from the port
.onComplete(_ system.shutdown()) // and shutdown when done
}
}
"long-routing-example" in {
import akka.actor.ActorRef
import akka.util.Timeout
import akka.pattern.ask
import akka.http.scaladsl.marshalling.ToResponseMarshaller
import akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller
import akka.http.scaladsl.model.StatusCodes.MovedPermanently
import akka.http.scaladsl.coding.Deflate
import akka.http.scaladsl.server.Directives._
// types used by the API routes
type Money = Double // only for demo purposes, don't try this at home!
type TransactionResult = String
case class User(name: String)
case class Order(email: String, amount: Money)
case class Update(order: Order)
case class OrderItem(i: Int, os: Option[String], s: String)
implicit val orderUM: FromRequestUnmarshaller[Order] = ???
implicit val orderM: ToResponseMarshaller[Seq[Order]] = ???
implicit val timeout: Timeout = ??? // for actor asks
val route = {
path("orders") {
authenticateBasic(realm = "admin area", myAuthenticator) { user =>
get {
encodeResponseWith(Deflate) {
complete {
// marshal custom object with in-scope marshaller
retrieveOrdersFromDB
}
}
} ~
post {
// decompress gzipped or deflated requests if required
decodeRequest {
// unmarshal with in-scope unmarshaller
entity(as[Order]) { order =>
complete {
// ... write order to DB
"Order received"
}
}
}
}
}
} ~
// extract URI path element as Int
pathPrefix("order" / IntNumber) { orderId =>
pathEnd {
(put | parameter('method ! "put")) {
// form extraction from multipart or www-url-encoded forms
formFields('email, 'total.as[Money]).as(Order) { order =>
complete {
// complete with serialized Future result
(myDbActor ? Update(order)).mapTo[TransactionResult]
}
}
} ~
get {
// debugging helper
logRequest("GET-ORDER") {
// use in-scope marshaller to create completer function
completeWith(instanceOf[Order]) { completer =>
// custom
processOrderRequest(orderId, completer)
}
}
}
} ~
path("items") {
get {
// parameters to case class extraction
parameters('size.as[Int], 'color ?, 'dangerous ? "no")
.as(OrderItem) { orderItem =>
// ... route using case class instance created from
// required and optional query parameters
complete("") // hide
}
}
}
} ~
pathPrefix("documentation") {
// optionally compresses the response with Gzip or Deflate
// if the client accepts compressed responses
encodeResponse {
// serve up static content from a JAR resource
getFromResourceDirectory("docs")
}
} ~
path("oldApi" / Rest) { pathRest =>
redirect("http://oldapi.example.com/" + pathRest, MovedPermanently)
}
}
// backend entry points
def myAuthenticator: Authenticator[User] = ???
def retrieveOrdersFromDB: Seq[Order] = ???
def myDbActor: ActorRef = ???
def processOrderRequest(id: Int, complete: Order => Unit): Unit = ???
}
}

View file

@ -1,4 +1,8 @@
package docs.http
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl
//#import-model
import akka.http.scaladsl.model._
@ -7,19 +11,18 @@ import akka.http.scaladsl.model._
import akka.stream.testkit.AkkaSpec
import akka.util.ByteString
import akka.http.scaladsl.model.headers.{ GenericHttpCredentials, BasicHttpCredentials }
import org.scalatest.MustMatchers
import akka.http.scaladsl.model.headers.BasicHttpCredentials
class ModelSpec extends AkkaSpec {
"construct request" in {
//#construct-request
import HttpMethods._
// construct simple GET request to `homeUri`
// construct a simple GET request to `homeUri`
val homeUri = Uri("/abc")
HttpRequest(GET, uri = homeUri)
// construct simple GET request to "/index" which is converted to Uri automatically
// construct simple GET request to "/index" (implicit string to Uri conversion)
HttpRequest(GET, uri = "/index")
// construct simple POST request containing entity
@ -70,11 +73,13 @@ class ModelSpec extends AkkaSpec {
// create an ``Authorization`` header with HTTP Basic authentication data
val auth = Authorization(BasicHttpCredentials("joe", "josepp"))
// a method that extracts basic HTTP credentials from a request
// custom type
case class User(name: String, pass: String)
// a method that extracts basic HTTP credentials from a request
def credentialsOfRequest(req: HttpRequest): Option[User] =
for {
Authorization(BasicHttpCredentials(user, pass)) <- req.header[headers.Authorization]
Authorization(BasicHttpCredentials(user, pass)) <- req.header[Authorization]
} yield User(user, pass)
//#headers

View file

@ -0,0 +1,76 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
import akka.http.scaladsl.server._
class CaseClassExtractionExamplesSpec extends RoutingSpec {
// format: OFF
"example-1" in {
case class Color(red: Int, green: Int, blue: Int)
val route =
path("color") {
parameters('red.as[Int], 'green.as[Int], 'blue.as[Int]) { (red, green, blue) =>
val color = Color(red, green, blue)
// ... route working with the `color` instance
null // hide
}
}
Get("/color?red=1&green=2&blue=3") ~> route ~> check { responseAs[String] shouldEqual "Color(1,2,3)" } // hide
}
"example-2" in {
case class Color(red: Int, green: Int, blue: Int)
val route =
path("color") {
parameters('red.as[Int], 'green.as[Int], 'blue.as[Int]).as(Color) { color =>
// ... route working with the `color` instance
null // hide
}
}
Get("/color?red=1&green=2&blue=3") ~> route ~> check { responseAs[String] shouldEqual "Color(1,2,3)" } // hide
}
"example-3" in {
case class Color(name: String, red: Int, green: Int, blue: Int)
val route =
(path("color" / Segment) & parameters('r.as[Int], 'g.as[Int], 'b.as[Int]))
.as(Color) { color =>
// ... route working with the `color` instance
null // hide
}
Get("/color/abc?r=1&g=2&b=3") ~> route ~> check { responseAs[String] shouldEqual "Color(abc,1,2,3)" } // hide
}
//# example-4
case class Color(name: String, red: Int, green: Int, blue: Int) {
require(!name.isEmpty, "color name must not be empty")
require(0 <= red && red <= 255, "red color component must be between 0 and 255")
require(0 <= green && green <= 255, "green color component must be between 0 and 255")
require(0 <= blue && blue <= 255, "blue color component must be between 0 and 255")
}
//#
"example 4 test" in {
val route =
(path("color" / Segment) &
parameters('r.as[Int], 'g.as[Int], 'b.as[Int])).as(Color) { color =>
doSomethingWith(color) // route working with the Color instance
}
Get("/color/abc?r=1&g=2&b=3") ~> route ~> check {
responseAs[String] shouldEqual "Color(abc,1,2,3)"
}
Get("/color/abc?r=1&g=2&b=345") ~> route ~> check {
rejection must beLike {
case ValidationRejection("requirement failed: blue color component must be between 0 and 255", _) => ok
}
}
}
}

View file

@ -2,7 +2,7 @@
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
package docs.http.scaladsl.server
import akka.http.scaladsl.server._
import Directives._
@ -11,7 +11,9 @@ import org.scalatest._
class DirectiveExamplesSpec extends RoutingSpec {
"example-1, example-2" in {
// format: OFF
"example-1" in {
val route: Route =
path("order" / IntNumber) { id =>
get {
@ -19,37 +21,49 @@ class DirectiveExamplesSpec extends RoutingSpec {
"Received GET request for order " + id
}
} ~
put {
complete {
"Received PUT request for order " + id
}
put {
complete {
"Received PUT request for order " + id
}
}
}
verify(route) // hide
}
"example-3" in {
"example-2" in {
def innerRoute(id: Int): Route =
get {
complete {
"Received GET request for order " + id
}
} ~
put {
complete {
"Received PUT request for order " + id
}
put {
complete {
"Received PUT request for order " + id
}
}
val route: Route = path("order" / IntNumber) { id => innerRoute(id) }
verify(route) // hide
}
"example-3" in {
val route =
path("order" / IntNumber) { id =>
(get | put) { ctx =>
ctx.complete(s"Received ${ctx.request.method.name} request for order $id")
}
}
verify(route) // hide
}
"example-4" in {
val route =
path("order" / IntNumber) { id =>
(get | put) { ctx =>
ctx.complete("Received " + ctx.request.method.name + " request for order " + id)
(get | put) {
extractMethod { m =>
complete(s"Received ${m.name} request for order $id")
}
}
}
verify(route) // hide
@ -59,8 +73,10 @@ class DirectiveExamplesSpec extends RoutingSpec {
val getOrPut = get | put
val route =
path("order" / IntNumber) { id =>
getOrPut { ctx =>
ctx.complete("Received " + ctx.request.method.name + " request for order " + id)
getOrPut {
extractMethod { m =>
complete(s"Received ${m.name} request for order $id")
}
}
}
verify(route) // hide
@ -69,51 +85,18 @@ class DirectiveExamplesSpec extends RoutingSpec {
"example-6" in {
val getOrPut = get | put
val route =
(path("order" / IntNumber) & getOrPut) { id =>
ctx =>
ctx.complete("Received " + ctx.request.method.name + " request for order " + id)
(path("order" / IntNumber) & getOrPut & extractMethod) { (id, m) =>
complete(s"Received ${m.name} request for order $id")
}
verify(route) // hide
}
"example-7" in {
val orderGetOrPut = path("order" / IntNumber) & (get | put)
val orderGetOrPutWithMethod =
path("order" / IntNumber) & (get | put) & extractMethod
val route =
orderGetOrPut { id =>
ctx =>
ctx.complete("Received " + ctx.request.method.name + " request for order " + id)
}
verify(route) // hide
}
"example-8" in {
val orderGetOrPut = path("order" / IntNumber) & (get | put)
val requestMethod = extract(_.request.method)
val route =
orderGetOrPut { id =>
requestMethod { m =>
complete("Received " + m.name + " request for order " + id)
}
}
verify(route) // hide
}
"example-9" in {
val orderGetOrPut = path("order" / IntNumber) & (get | put)
val requestMethod = extract(_.request.method)
val route =
(orderGetOrPut & requestMethod) { (id, m) =>
complete("Received " + m.name + " request for order " + id)
}
verify(route) // hide
}
"example-A" in {
val orderGetOrPutMethod =
path("order" / IntNumber) & (get | put) & extract(_.request.method)
val route =
orderGetOrPutMethod { (id, m) =>
complete("Received " + m.name + " request for order " + id)
orderGetOrPutWithMethod { (id, m) =>
complete(s"Received ${m.name} request for order $id")
}
verify(route) // hide
}

View file

@ -0,0 +1,93 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
// format: OFF
object MyExplicitExceptionHandler {
//#explicit-handler-example
import akka.actor.ActorSystem
import akka.stream.ActorFlowMaterializer
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.server._
import StatusCodes._
import Directives._
val myExceptionHandler = ExceptionHandler {
case _: ArithmeticException =>
extractUri { uri =>
println(s"Request to $uri could not be handled normally")
complete(HttpResponse(InternalServerError, entity = "Bad numbers, bad result!!!"))
}
}
object MyApp extends App {
implicit val system = ActorSystem()
implicit val materializer = ActorFlowMaterializer()
val route: Route =
handleExceptions(myExceptionHandler) {
// ... some route structure
null // hide
}
Http().bindAndHandle(route, "localhost", 8080)
}
//#
}
object MyImplicitExceptionHandler {
//#implicit-handler-example
import akka.actor.ActorSystem
import akka.stream.ActorFlowMaterializer
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.server._
import StatusCodes._
import Directives._
implicit def myExceptionHandler: ExceptionHandler =
ExceptionHandler {
case _: ArithmeticException =>
extractUri { uri =>
println(s"Request to $uri could not be handled normally")
complete(HttpResponse(InternalServerError, entity = "Bad numbers, bad result!!!"))
}
}
object MyApp extends App {
implicit val system = ActorSystem()
implicit val materializer = ActorFlowMaterializer()
val route: Route =
// ... some route structure
null // hide
Http().bindAndHandle(route, "localhost", 8080)
}
//#
}
class ExceptionHandlerExamplesSpec extends RoutingSpec {
"test explicit example" in {
Get() ~> handleExceptions(MyExplicitExceptionHandler.myExceptionHandler) {
_.complete((1 / 0).toString)
} ~> check {
responseAs[String] === "Bad numbers, bad result!!!"
}
}
"test implicit example" in {
import akka.http.scaladsl.server._
import MyImplicitExceptionHandler.myExceptionHandler
Get() ~> Route.seal(ctx => ctx.complete((1 / 0).toString)) ~> check {
responseAs[String] === "Bad numbers, bad result!!!"
}
}
}

View file

@ -0,0 +1,58 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
// format: OFF
//# source-quote
import org.scalatest.{ Matchers, WordSpec }
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.testkit.ScalatestRouteTest
import akka.http.scaladsl.server._
import Directives._
class FullTestKitExampleSpec extends WordSpec with Matchers with ScalatestRouteTest {
val smallRoute =
get {
pathSingleSlash {
complete {
"Captain on the bridge!"
}
} ~
path("ping") {
complete("PONG!")
}
}
"The service" should {
"return a greeting for GET requests to the root path" in {
Get() ~> smallRoute ~> check {
responseAs[String] should contain("Captain on the bridge")
}
}
"return a 'PONG!' response for GET requests to /ping" in {
Get("/ping") ~> smallRoute ~> check {
responseAs[String] shouldEqual "PONG!"
}
}
"leave GET requests to other paths unhandled" in {
Get("/kermit") ~> smallRoute ~> check {
handled shouldBe false
}
}
"return a MethodNotAllowed error for PUT requests to the root path" in {
Put() ~> Route.seal(smallRoute) ~> check {
status === StatusCodes.MethodNotAllowed
responseAs[String] shouldEqual "HTTP method not allowed, supported methods: GET"
}
}
}
}
//#

View file

@ -0,0 +1,73 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
// format: OFF
object MyRejectionHandler {
//#custom-handler-example
import akka.actor.ActorSystem
import akka.stream.ActorFlowMaterializer
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.server._
import StatusCodes._
import Directives._
implicit def myRejectionHandler =
RejectionHandler.newBuilder()
.handle { case MissingCookieRejection(cookieName) =>
complete(HttpResponse(BadRequest, entity = "No cookies, no service!!!"))
}
.handle { case AuthorizationFailedRejection
complete(Forbidden, "You're out of your depth!")
}
.handleAll[MethodRejection] { methodRejections
val names = methodRejections.map(_.supported.name)
complete(MethodNotAllowed, s"Can't do that! Supported: ${names mkString " or "}!")
}
.handleNotFound { complete(NotFound, "Not here!") }
.result()
object MyApp extends App {
implicit val system = ActorSystem()
implicit val materializer = ActorFlowMaterializer()
val route: Route =
// ... some route structure
null // hide
Http().bindAndHandle(route, "localhost", 8080)
}
//#
}
class RejectionHandlerExamplesSpec extends RoutingSpec {
import MyRejectionHandler._
"example-1" in {
import akka.http.scaladsl.coding.Gzip
val route =
path("order") {
get {
complete("Received GET")
} ~
post {
decodeRequestWith(Gzip) {
complete("Received compressed POST")
}
}
}
}
"test custom handler example" in {
import akka.http.scaladsl.server._
Get() ~> Route.seal(reject(MissingCookieRejection("abc"))) ~> check {
responseAs[String] === "No cookies, no service!!!"
}
}
}

View file

@ -2,7 +2,7 @@
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
package docs.http.scaladsl.server
import akka.http.scaladsl.server.Directives
import akka.http.scaladsl.testkit.ScalatestRouteTest

View file

@ -2,16 +2,14 @@
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
package docs.http.scaladsl.server
package directives
import scala.util.control.NonFatal
import akka.util.ByteString
import akka.http.scaladsl.model.headers.RawHeader
import akka.http.scaladsl.server.RouteResult.Rejected
import akka.http.scaladsl.server._
import akka.util.ByteString
import scala.util.control.NonFatal
import akka.http.scaladsl.model._
class BasicDirectivesExamplesSpec extends RoutingSpec {
@ -164,7 +162,7 @@ class BasicDirectivesExamplesSpec extends RoutingSpec {
rejections.nonEmpty shouldEqual true
}
}
"mapRouteResponsePF" in {
"mapRouteResultPF" in {
case object MyCustomRejection extends Rejection
val rejectRejections = // not particularly useful directive
mapRouteResultPF {

View file

@ -2,7 +2,7 @@
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
package docs.http.scaladsl.server
package directives
import akka.http.scaladsl.coding._

View file

@ -2,7 +2,7 @@
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
package docs.http.scaladsl.server
package directives
import akka.http.scaladsl.server._

View file

@ -2,7 +2,7 @@
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
package docs.http.scaladsl.server
package directives
import akka.http.scaladsl.model.{ HttpResponse, HttpRequest }

View file

@ -2,7 +2,7 @@
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
package docs.http.scaladsl.server
package directives
import akka.http.scaladsl.model.StatusCodes

View file

@ -0,0 +1,29 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
package directives
import akka.http.scaladsl.server.Route
import akka.http.scaladsl.model._
class FormFieldDirectivesExamplesSpec extends RoutingSpec {
"formFields" in {
val route =
formFields('color, 'age.as[Int]) { (color, age) =>
complete(s"The color is '$color' and the age ten years ago was ${age - 10}")
}
Post("/", FormData(Seq("color" -> "blue", "age" -> "68"))) ~> route ~> check {
responseAs[String] shouldEqual "The color is 'blue' and the age ten years ago was 58"
}
Get("/") ~> Route.seal(route) ~> check {
status shouldEqual StatusCodes.BadRequest
responseAs[String] shouldEqual "Request is missing required form field 'color'"
}
}
}

View file

@ -0,0 +1,102 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
package directives
import java.util.concurrent.TimeUnit
import scala.concurrent.Future
import scala.util.{ Success, Failure }
import akka.http.scaladsl.server.ExceptionHandler
import akka.actor.{ Actor, Props }
import akka.util.Timeout
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Route
import StatusCodes._
// format: OFF
class FutureDirectivesExamplesSpec extends RoutingSpec {
object TestException extends Throwable
implicit val myExceptionHandler =
ExceptionHandler {
case TestException => ctx =>
ctx.complete(InternalServerError, "Unsuccessful future!")
}
val resourceActor = system.actorOf(Props(new Actor {
def receive = { case _ => sender ! "resource" }
}))
implicit val responseTimeout = Timeout(2, TimeUnit.SECONDS)
"example-1" in {
def divide(a: Int, b: Int): Future[Int] = Future {
a / b
}
val route =
path("divide" / IntNumber / IntNumber) { (a, b) =>
onComplete(divide(a, b)) {
case Success(value) => complete(s"The result was $value")
case Failure(ex) => complete(InternalServerError, s"An error occurred: ${ex.getMessage}")
}
}
Get("/divide/10/2") ~> route ~> check {
responseAs[String] shouldEqual "The result was 5"
}
Get("/divide/10/0") ~> Route.seal(route) ~> check {
status shouldEqual InternalServerError
responseAs[String] shouldEqual "An error occurred: / by zero"
}
}
"example-2" in {
val route =
path("success") {
onSuccess(Future { "Ok" }) { extraction =>
complete(extraction)
}
} ~
path("failure") {
onSuccess(Future.failed[String](TestException)) { extraction =>
complete(extraction)
}
}
Get("/success") ~> route ~> check {
responseAs[String] shouldEqual "Ok"
}
Get("/failure") ~> Route.seal(route) ~> check {
status shouldEqual InternalServerError
responseAs[String] shouldEqual "Unsuccessful future!"
}
}
"example-3" in {
val route =
path("success") {
onFailure(Future { "Ok" }) { extraction =>
failWith(extraction) // not executed.
}
} ~
path("failure") {
onFailure(Future.failed[String](TestException)) { extraction =>
failWith(extraction)
}
}
Get("/success") ~> route ~> check {
responseAs[String] shouldEqual "Ok"
}
Get("/failure") ~> Route.seal(route) ~> check {
status shouldEqual InternalServerError
responseAs[String] shouldEqual "Unsuccessful future!"
}
}
}

View file

@ -0,0 +1,103 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
package directives
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.MissingHeaderRejection
import akka.http.scaladsl.server.Route
import headers._
import StatusCodes._
class HeaderDirectivesExamplesSpec extends RoutingSpec {
"headerValueByName-0" in {
val route =
headerValueByName("X-User-Id") { userId =>
complete(s"The user is $userId")
}
Get("/") ~> RawHeader("X-User-Id", "Joe42") ~> route ~> check {
responseAs[String] shouldEqual "The user is Joe42"
}
Get("/") ~> Route.seal(route) ~> check {
status shouldEqual BadRequest
responseAs[String] shouldEqual "Request is missing required HTTP header 'X-User-Id'"
}
}
"headerValue-0" in {
def extractHostPort: HttpHeader => Option[Int] = {
case h: `Host` => Some(h.port)
case x => None
}
val route =
headerValue(extractHostPort) { port =>
complete(s"The port was $port")
}
Get("/") ~> Host("example.com", 5043) ~> route ~> check {
responseAs[String] shouldEqual "The port was 5043"
}
Get("/") ~> Route.seal(route) ~> check {
status shouldEqual NotFound
responseAs[String] shouldEqual "The requested resource could not be found."
}
}
"headerValuePF-0" in {
def extractHostPort: PartialFunction[HttpHeader, Int] = {
case h: `Host` => h.port
}
val route =
headerValuePF(extractHostPort) { port =>
complete(s"The port was $port")
}
Get("/") ~> Host("example.com", 5043) ~> route ~> check {
responseAs[String] shouldEqual "The port was 5043"
}
Get("/") ~> Route.seal(route) ~> check {
status shouldEqual NotFound
responseAs[String] shouldEqual "The requested resource could not be found."
}
}
"headerValueByType-0" in {
val route =
headerValueByType[Origin]() { origin
complete(s"The first origin was ${origin.originList.head}")
}
val originHeader = Origin(Seq(HttpOrigin("http://localhost:8080")))
// extract a header if the type is matching
Get("abc") ~> originHeader ~> route ~> check {
responseAs[String] shouldEqual "The first origin was http://localhost:8080"
}
// reject a request if no header of the given type is present
Get("abc") ~> route ~> check {
rejection must beLike { case MissingHeaderRejection("Origin") ok }
}
}
"optionalHeaderValueByType-0" in {
val route =
optionalHeaderValueByType[Origin]() {
case Some(origin) complete(s"The first origin was ${origin.originList.head}")
case None complete("No Origin header found.")
}
val originHeader = Origin(Seq(HttpOrigin("http://localhost:8080")))
// extract Some(header) if the type is matching
Get("abc") ~> originHeader ~> route ~> check {
responseAs[String] shouldEqual "The first origin was http://localhost:8080"
}
// extract None if no header of the given type is present
Get("abc") ~> route ~> check {
responseAs[String] shouldEqual "No Origin header found."
}
}
}

View file

@ -0,0 +1,88 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
package directives
import akka.http.scaladsl.model._
import headers._
import StatusCodes._
class HostDirectivesExamplesSpec extends RoutingSpec {
"extract-hostname" in {
val route =
hostName { hn =>
complete(s"Hostname: $hn")
}
Get() ~> Host("company.com", 9090) ~> route ~> check {
status shouldEqual OK
responseAs[String] shouldEqual "Hostname: company.com"
}
}
"list-of-hosts" in {
val route =
host("api.company.com", "rest.company.com") {
complete("Ok")
}
Get() ~> Host("rest.company.com") ~> route ~> check {
status shouldEqual OK
responseAs[String] shouldEqual "Ok"
}
Get() ~> Host("notallowed.company.com") ~> route ~> check {
handled shouldBe false
}
}
"predicate" in {
val shortOnly: String => Boolean = (hostname) => hostname.length < 10
val route =
host(shortOnly) {
complete("Ok")
}
Get() ~> Host("short.com") ~> route ~> check {
status shouldEqual OK
responseAs[String] shouldEqual "Ok"
}
Get() ~> Host("verylonghostname.com") ~> route ~> check {
handled shouldBe false
}
}
"using-regex" in {
val route =
host("api|rest".r) { prefix =>
complete(s"Extracted prefix: $prefix")
} ~
host("public.(my|your)company.com".r) { captured =>
complete(s"You came through $captured company")
}
Get() ~> Host("api.company.com") ~> route ~> check {
status shouldEqual OK
responseAs[String] shouldEqual "Extracted prefix: api"
}
Get() ~> Host("public.mycompany.com") ~> route ~> check {
status shouldEqual OK
responseAs[String] shouldEqual "You came through my company"
}
}
"failing-regex" in {
{
host("server-([0-9]).company.(com|net|org)".r) { target =>
complete("Will never complete :'(")
}
} must throwAn[IllegalArgumentException]
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
package directives
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
import akka.http.scaladsl.model._
import spray.json.DefaultJsonProtocol
import headers._
import StatusCodes._
//# person-case-class
case class Person(name: String, favoriteNumber: Int)
//# person-json-support
object PersonJsonSupport extends DefaultJsonProtocol with SprayJsonSupport {
implicit val PortofolioFormats = jsonFormat2(Person)
}
//#
class MarshallingDirectivesExamplesSpec extends RoutingSpec {
"example-entity-with-json" in {
import PersonJsonSupport._
val route = post {
entity(as[Person]) { person =>
complete(s"Person: ${person.name} - favorite number: ${person.favoriteNumber}")
}
}
Post("/", HttpEntity(`application/json`, """{ "name": "Jane", "favoriteNumber" : 42 }""")) ~>
route ~> check {
responseAs[String] shouldEqual "Person: Jane - favorite number: 42"
}
}
"example-produce-with-json" in {
import PersonJsonSupport._
val findPerson = (f: Person => Unit) => {
//... some processing logic...
//complete the request
f(Person("Jane", 42))
}
val route = get {
produce(instanceOf[Person]) { completionFunction => ctx => findPerson(completionFunction) }
}
Get("/") ~> route ~> check {
mediaType shouldEqual `application/json`
responseAs[String] must contain(""""name": "Jane"""")
responseAs[String] must contain(""""favoriteNumber": 42""")
}
}
"example-handleWith-with-json" in {
import PersonJsonSupport._
val updatePerson = (person: Person) => {
//... some processing logic...
//return the person
person
}
val route = post {
handleWith(updatePerson)
}
Post("/", HttpEntity(`application/json`, """{ "name": "Jane", "favoriteNumber" : 42 }""")) ~>
route ~> check {
mediaType shouldEqual `application/json`
responseAs[String] must contain(""""name": "Jane"""")
responseAs[String] must contain(""""favoriteNumber": 42""")
}
}
}

View file

@ -0,0 +1,80 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
package directives
import akka.http.scaladsl.model._
class MethodDirectivesExamplesSpec extends RoutingSpec {
"delete-method" in {
val route = delete { complete("This is a DELETE request.") }
Delete("/") ~> route ~> check {
responseAs[String] shouldEqual "This is a DELETE request."
}
}
"get-method" in {
val route = get { complete("This is a GET request.") }
Get("/") ~> route ~> check {
responseAs[String] shouldEqual "This is a GET request."
}
}
"head-method" in {
val route = head { complete("This is a HEAD request.") }
Head("/") ~> route ~> check {
responseAs[String] shouldEqual "This is a HEAD request."
}
}
"options-method" in {
val route = options { complete("This is an OPTIONS request.") }
Options("/") ~> route ~> check {
responseAs[String] shouldEqual "This is an OPTIONS request."
}
}
"patch-method" in {
val route = patch { complete("This is a PATCH request.") }
Patch("/", "patch content") ~> route ~> check {
responseAs[String] shouldEqual "This is a PATCH request."
}
}
"post-method" in {
val route = post { complete("This is a POST request.") }
Post("/", "post content") ~> route ~> check {
responseAs[String] shouldEqual "This is a POST request."
}
}
"put-method" in {
val route = put { complete("This is a PUT request.") }
Put("/", "put content") ~> route ~> check {
responseAs[String] shouldEqual "This is a PUT request."
}
}
"method-example" in {
val route = method(HttpMethods.PUT) { complete("This is a PUT request.") }
Put("/", "put content") ~> route ~> check {
responseAs[String] shouldEqual "This is a PUT request."
}
Get("/") ~> Route.seal(route) ~> check {
status shouldEqual StatusCodes.MethodNotAllowed
responseAs[String] shouldEqual "HTTP method not allowed, supported methods: PUT"
}
}
}

View file

@ -0,0 +1,199 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
package directives
import akka.http.scaladsl.model._
import akka.http.scaladsl.server._
import headers._
class MiscDirectivesExamplesSpec extends RoutingSpec {
"cancelAllRejections-example" in {
def isMethodRejection: Rejection => Boolean = {
case MethodRejection(_) => true
case _ => false
}
val route =
cancelAllRejections(isMethodRejection) {
post {
complete("Result")
}
}
Get("/") ~> route ~> check {
rejections shouldEqual Nil
handled shouldEqual false
}
}
"cancelRejection-example" in {
val route =
cancelRejection(MethodRejection(HttpMethods.POST)) {
post {
complete("Result")
}
}
Get("/") ~> route ~> check {
rejections shouldEqual Nil
handled shouldEqual false
}
}
"clientIP-example" in {
val route = clientIP { ip =>
complete("Client's ip is " + ip.toOption.map(_.getHostAddress).getOrElse("unknown"))
}
Get("/").withHeaders(`Remote-Address`("192.168.3.12")) ~> route ~> check {
responseAs[String] shouldEqual "Client's ip is 192.168.3.12"
}
}
"jsonpWithParameter-example" in {
case class Test(abc: Int)
object TestProtocol {
import spray.json.DefaultJsonProtocol._
implicit val testFormat = jsonFormat(Test, "abc")
}
val route =
jsonpWithParameter("jsonp") {
import TestProtocol._
import spray.httpx.SprayJsonSupport._
complete(Test(456))
}
Get("/?jsonp=result") ~> route ~> check {
responseAs[String] shouldEqual
"""result({
| "abc": 456
|})""".stripMarginWithNewline("\n")
contentType shouldEqual MediaTypes.`application/javascript`.withCharset(HttpCharsets.`UTF-8`)
}
Get("/") ~> route ~> check {
responseAs[String] shouldEqual
"""{
| "abc": 456
|}""".stripMarginWithNewline("\n")
contentType shouldEqual ContentTypes.`application/json`
}
}
"rejectEmptyResponse-example" in {
val route = rejectEmptyResponse {
path("even" / IntNumber) { i =>
complete {
// returns Some(evenNumberDescription) or None
Option(i).filter(_ % 2 == 0).map { num =>
s"Number $num is even."
}
}
}
}
Get("/even/23") ~> Route.seal(route) ~> check {
status shouldEqual StatusCodes.NotFound
}
Get("/even/28") ~> route ~> check {
responseAs[String] shouldEqual "Number 28 is even."
}
}
"requestEntityEmptyPresent-example" in {
val route =
requestEntityEmpty {
complete("request entity empty")
} ~
requestEntityPresent {
complete("request entity present")
}
Post("/", "text") ~> Route.seal(route) ~> check {
responseAs[String] shouldEqual "request entity present"
}
Post("/") ~> route ~> check {
responseAs[String] shouldEqual "request entity empty"
}
}
"requestInstance-example" in {
val route =
requestInstance { request =>
complete(s"Request method is ${request.method} and length is ${request.entity.data.length}")
}
Post("/", "text") ~> route ~> check {
responseAs[String] shouldEqual "Request method is POST and length is 4"
}
Get("/") ~> route ~> check {
responseAs[String] shouldEqual "Request method is GET and length is 0"
}
}
"requestUri-example" in {
val route =
requestUri { uri =>
complete(s"Full URI: $uri")
}
Get("/") ~> route ~> check {
// tests are executed with the host assumed to be "example.com"
responseAs[String] shouldEqual "Full URI: http://example.com/"
}
Get("/test") ~> route ~> check {
responseAs[String] shouldEqual "Full URI: http://example.com/test"
}
}
"rewriteUnmatchedPath-example" in {
def ignore456(path: Uri.Path) = path match {
case s @ Uri.Path.Segment(head, tail) if head.startsWith("456") =>
val newHead = head.drop(3)
if (newHead.isEmpty) tail
else s.copy(head = head.drop(3))
case _ => path
}
val ignoring456 = rewriteUnmatchedPath(ignore456)
val route =
pathPrefix("123") {
ignoring456 {
path("abc") {
complete(s"Content")
}
}
}
Get("/123/abc") ~> route ~> check {
responseAs[String] shouldEqual "Content"
}
Get("/123456/abc") ~> route ~> check {
responseAs[String] shouldEqual "Content"
}
}
"unmatchedPath-example" in {
val route =
pathPrefix("abc") {
unmatchedPath { remaining =>
complete(s"Unmatched: '$remaining'")
}
}
Get("/abc") ~> route ~> check {
responseAs[String] shouldEqual "Unmatched: ''"
}
Get("/abc/456") ~> route ~> check {
responseAs[String] shouldEqual "Unmatched: '/456'"
}
}
"validate-example" in {
val route =
requestUri { uri =>
validate(uri.path.toString.size < 5, s"Path too long: '${uri.path.toString}'") {
complete(s"Full URI: $uri")
}
}
Get("/234") ~> route ~> check {
responseAs[String] shouldEqual "Full URI: http://example.com/234"
}
Get("/abcdefghijkl") ~> route ~> check {
rejection shouldEqual ValidationRejection("Path too long: '/abcdefghijkl'", None)
}
}
}

View file

@ -0,0 +1,138 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
package directives
import spray.http.StatusCodes
class ParameterDirectivesExamplesSpec extends RoutingSpec {
"example-1" in {
val route =
parameter('color) { color =>
complete(s"The color is '$color'")
}
Get("/?color=blue") ~> route ~> check {
responseAs[String] shouldEqual "The color is 'blue'"
}
Get("/") ~> Route.seal(route) ~> check {
status shouldEqual StatusCodes.NotFound
responseAs[String] shouldEqual "Request is missing required query parameter 'color'"
}
}
"required-1" in {
val route =
parameters('color, 'backgroundColor) { (color, backgroundColor) =>
complete(s"The color is '$color' and the background is '$backgroundColor'")
}
Get("/?color=blue&backgroundColor=red") ~> route ~> check {
responseAs[String] shouldEqual "The color is 'blue' and the background is 'red'"
}
Get("/?color=blue") ~> Route.seal(route) ~> check {
status shouldEqual StatusCodes.NotFound
responseAs[String] shouldEqual "Request is missing required query parameter 'backgroundColor'"
}
}
"optional" in {
val route =
parameters('color, 'backgroundColor.?) { (color, backgroundColor) =>
val backgroundStr = backgroundColor.getOrElse("<undefined>")
complete(s"The color is '$color' and the background is '$backgroundStr'")
}
Get("/?color=blue&backgroundColor=red") ~> route ~> check {
responseAs[String] shouldEqual "The color is 'blue' and the background is 'red'"
}
Get("/?color=blue") ~> route ~> check {
responseAs[String] shouldEqual "The color is 'blue' and the background is '<undefined>'"
}
}
"optional-with-default" in {
val route =
parameters('color, 'backgroundColor ? "white") { (color, backgroundColor) =>
complete(s"The color is '$color' and the background is '$backgroundColor'")
}
Get("/?color=blue&backgroundColor=red") ~> route ~> check {
responseAs[String] shouldEqual "The color is 'blue' and the background is 'red'"
}
Get("/?color=blue") ~> route ~> check {
responseAs[String] shouldEqual "The color is 'blue' and the background is 'white'"
}
}
"required-value" in {
val route =
parameters('color, 'action ! "true") { (color) =>
complete(s"The color is '$color'.")
}
Get("/?color=blue&action=true") ~> route ~> check {
responseAs[String] shouldEqual "The color is 'blue'."
}
Get("/?color=blue&action=false") ~> Route.seal(route) ~> check {
status shouldEqual StatusCodes.NotFound
responseAs[String] shouldEqual "The requested resource could not be found."
}
}
"mapped-value" in {
val route =
parameters('color, 'count.as[Int]) { (color, count) =>
complete(s"The color is '$color' and you have $count of it.")
}
Get("/?color=blue&count=42") ~> route ~> check {
responseAs[String] shouldEqual "The color is 'blue' and you have 42 of it."
}
Get("/?color=blue&count=blub") ~> Route.seal(route) ~> check {
status shouldEqual StatusCodes.BadRequest
responseAs[String] shouldEqual "The query parameter 'count' was malformed:\n'blub' is not a valid 32-bit integer value"
}
}
"parameterMap" in {
val route =
parameterMap { params =>
def paramString(param: (String, String)): String = s"""${param._1} = '${param._2}'"""
complete(s"The parameters are ${params.map(paramString).mkString(", ")}")
}
Get("/?color=blue&count=42") ~> route ~> check {
responseAs[String] shouldEqual "The parameters are color = 'blue', count = '42'"
}
Get("/?x=1&x=2") ~> route ~> check {
responseAs[String] shouldEqual "The parameters are x = '2'"
}
}
"parameterMultiMap" in {
val route =
parameterMultiMap { params =>
complete(s"There are parameters ${params.map(x => x._1 + " -> " + x._2.size).mkString(", ")}")
}
Get("/?color=blue&count=42") ~> route ~> check {
responseAs[String] shouldEqual "There are parameters color -> 1, count -> 1"
}
Get("/?x=23&x=42") ~> route ~> check {
responseAs[String] shouldEqual "There are parameters x -> 2"
}
}
"parameterSeq" in {
val route =
parameterSeq { params =>
def paramString(param: (String, String)): String = s"""${param._1} = '${param._2}'"""
complete(s"The parameters are ${params.map(paramString).mkString(", ")}")
}
Get("/?color=blue&count=42") ~> route ~> check {
responseAs[String] shouldEqual "The parameters are color = 'blue', count = '42'"
}
Get("/?x=1&x=2") ~> route ~> check {
responseAs[String] shouldEqual "The parameters are x = '1', x = '2'"
}
}
}

View file

@ -2,7 +2,7 @@
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
package docs.http.scaladsl.server
package directives
import akka.http.scaladsl.server._

View file

@ -0,0 +1,36 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
package directives
import akka.http.scaladsl.model._
import headers._
class RangeDirectivesExamplesSpec extends RoutingSpec {
"withRangeSupport" in {
val route =
withRangeSupport(4, 2L) {
complete("ABCDEFGH")
}
Get() ~> addHeader(Range(ByteRange(3, 4))) ~> route ~> check {
headers must contain(`Content-Range`(ContentRange(3, 4, 8)))
status shouldEqual StatusCodes.PartialContent
responseAs[String] shouldEqual "DE"
}
Get() ~> addHeader(Range(ByteRange(0, 1), ByteRange(1, 2), ByteRange(6, 7))) ~> route ~> check {
headers must not(contain(like[HttpHeader] { case `Content-Range`(_, _) ok }))
responseAs[MultipartByteRanges] must beLike {
case MultipartByteRanges(
BodyPart(entity1, `Content-Range`(RangeUnit.Bytes, range1) +: _) +:
BodyPart(entity2, `Content-Range`(RangeUnit.Bytes, range2) +: _) +: Seq()
) entity1.asString shouldEqual "ABC" and range1 shouldEqual ContentRange(0, 2, 8) and
entity2.asString shouldEqual "GH" and range2 shouldEqual ContentRange(6, 7, 8)
}
}
}
}

View file

@ -0,0 +1,105 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
package directives
import akka.http.scaladsl.server.UnacceptedResponseContentTypeRejection
import akka.http.scaladsl.model._
import headers._
class RespondWithDirectivesExamplesSpec extends RoutingSpec {
"respondWithHeader-examples" in {
val route =
path("foo") {
respondWithHeader(RawHeader("Funky-Muppet", "gonzo")) {
complete("beep")
}
}
Get("/foo") ~> route ~> check {
header("Funky-Muppet") shouldEqual Some(RawHeader("Funky-Muppet", "gonzo"))
responseAs[String] shouldEqual "beep"
}
}
"respondWithHeaders-examples" in {
val route =
path("foo") {
respondWithHeaders(RawHeader("Funky-Muppet", "gonzo"), Origin(Seq(HttpOrigin("http://spray.io")))) {
complete("beep")
}
}
Get("/foo") ~> route ~> check {
header("Funky-Muppet") shouldEqual Some(RawHeader("Funky-Muppet", "gonzo"))
header[Origin] shouldEqual Some(Origin(Seq(HttpOrigin("http://spray.io"))))
responseAs[String] shouldEqual "beep"
}
}
"respondWithMediaType-examples" in {
import MediaTypes._
val route =
path("foo") {
respondWithMediaType(`application/json`) {
complete("[]") // marshalled to `text/plain` here
}
}
Get("/foo") ~> route ~> check {
mediaType shouldEqual `application/json`
responseAs[String] shouldEqual "[]"
}
Get("/foo") ~> Accept(MediaRanges.`text/*`) ~> route ~> check {
rejection shouldEqual UnacceptedResponseContentTypeRejection(ContentType(`application/json`) :: Nil)
}
}
"respondWithSingletonHeader-examples" in {
val respondWithMuppetHeader =
respondWithSingletonHeader(RawHeader("Funky-Muppet", "gonzo"))
val route =
path("foo") {
respondWithMuppetHeader {
complete("beep")
}
} ~
path("bar") {
respondWithMuppetHeader {
respondWithHeader(RawHeader("Funky-Muppet", "kermit")) {
complete("beep")
}
}
}
Get("/foo") ~> route ~> check {
headers.filter(_.is("funky-muppet")) shouldEqual List(RawHeader("Funky-Muppet", "gonzo"))
responseAs[String] shouldEqual "beep"
}
Get("/bar") ~> route ~> check {
headers.filter(_.is("funky-muppet")) shouldEqual List(RawHeader("Funky-Muppet", "kermit"))
responseAs[String] shouldEqual "beep"
}
}
"respondWithStatus-examples" in {
val route =
path("foo") {
respondWithStatus(201) {
complete("beep")
}
}
Get("/foo") ~> route ~> check {
status shouldEqual StatusCodes.Created
responseAs[String] shouldEqual "beep"
}
}
}

View file

@ -0,0 +1,95 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
package directives
import spray.http.{ RequestProcessingException, HttpResponse, StatusCodes }
import spray.routing.ValidationRejection
class RouteDirectivesExamplesSpec extends RoutingSpec {
"complete-examples" in {
val route =
path("a") {
complete(HttpResponse(entity = "foo"))
} ~
path("b") {
complete(StatusCodes.Created, "bar")
} ~
(path("c") & complete("baz")) // `&` also works with `complete` as the 2nd argument
Get("/a") ~> route ~> check {
status shouldEqual StatusCodes.OK
responseAs[String] shouldEqual "foo"
}
Get("/b") ~> route ~> check {
status shouldEqual StatusCodes.Created
responseAs[String] shouldEqual "bar"
}
Get("/c") ~> route ~> check {
status shouldEqual StatusCodes.OK
responseAs[String] shouldEqual "baz"
}
}
"reject-examples" in {
val route =
path("a") {
reject // don't handle here, continue on
} ~
path("a") {
complete("foo")
} ~
path("b") {
// trigger a ValidationRejection explicitly
// rather than through the `validate` directive
reject(ValidationRejection("Restricted!"))
}
Get("/a") ~> route ~> check {
responseAs[String] shouldEqual "foo"
}
Get("/b") ~> route ~> check {
rejection shouldEqual ValidationRejection("Restricted!")
}
}
"redirect-examples" in {
val route =
pathPrefix("foo") {
pathSingleSlash {
complete("yes")
} ~
pathEnd {
redirect("/foo/", StatusCodes.PermanentRedirect)
}
}
Get("/foo/") ~> route ~> check {
responseAs[String] shouldEqual "yes"
}
Get("/foo") ~> route ~> check {
status shouldEqual StatusCodes.PermanentRedirect
responseAs[String] shouldEqual """The request, and all future requests should be repeated using <a href="/foo/">this URI</a>."""
}
}
"failwith-examples" in {
val route =
path("foo") {
failWith(new RequestProcessingException(StatusCodes.BandwidthLimitExceeded))
}
Get("/foo") ~> Route.seal(route) ~> check {
status shouldEqual StatusCodes.BandwidthLimitExceeded
responseAs[String] shouldEqual "Bandwidth limit has been exceeded."
}
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
package directives
import akka.http.scaladsl.model._
class SchemeDirectivesExamplesSpec extends RoutingSpec {
"example-1" in {
val route =
schemeName { scheme =>
complete(s"The scheme is '${scheme}'")
}
Get("https://www.example.com/") ~> route ~> check {
responseAs[String] shouldEqual "The scheme is 'https'"
}
}
"example-2" in {
val route =
scheme("http") {
extract(_.request.uri) { uri
redirect(uri.copy(scheme = "https"), MovedPermanently)
}
} ~
scheme("https") {
complete(s"Safe and secure!")
}
Get("http://www.example.com/hello") ~> route ~> check {
status shouldEqual MovedPermanently
header[headers.Location] shouldEqual Some(headers.Location(Uri("https://www.example.com/hello")))
}
Get("https://www.example.com/hello") ~> route ~> check {
responseAs[String] shouldEqual "Safe and secure!"
}
}
}

View file

@ -0,0 +1,120 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.scaladsl.server
package directives
import com.typesafe.config.ConfigFactory
import scala.concurrent.Future
import akka.http.scaladsl.model._
import headers._
class SecurityDirectivesExamplesSpec extends RoutingSpec {
"authenticate-custom-user-pass-authenticator" in {
def myUserPassAuthenticator(userPass: Option[UserPass]): Future[Option[String]] =
Future {
if (userPass.exists(up => up.user == "John" && up.pass == "p4ssw0rd")) Some("John")
else None
}
val route =
Route.seal {
path("secured") {
authenticateBasic(BasicAuth(myUserPassAuthenticator _, realm = "secure site")) { userName =>
complete(s"The user is '$userName'")
}
}
}
Get("/secured") ~> route ~> check {
status shouldEqual StatusCodes.Unauthorized
responseAs[String] shouldEqual "The resource requires authentication, which was not supplied with the request"
header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", "secure site")
}
val validCredentials = BasicHttpCredentials("John", "p4ssw0rd")
Get("/secured") ~>
addCredentials(validCredentials) ~> // adds Authorization header
route ~> check {
responseAs[String] shouldEqual "The user is 'John'"
}
val invalidCredentials = BasicHttpCredentials("Peter", "pan")
Get("/secured") ~>
addCredentials(invalidCredentials) ~> // adds Authorization header
route ~> check {
status shouldEqual StatusCodes.Unauthorized
responseAs[String] shouldEqual "The supplied authentication is invalid"
header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", "secure site")
}
}
"authenticate-from-config" in {
def extractUser(userPass: UserPass): String = userPass.user
val config = ConfigFactory.parseString("John = p4ssw0rd")
val route =
Route.seal {
path("secured") {
authenticateBasic(BasicAuth(realm = "secure site", config = config, createUser = extractUser _)) { userName =>
complete(s"The user is '$userName'")
}
}
}
Get("/secured") ~> route ~> check {
status shouldEqual StatusCodes.Unauthorized
responseAs[String] shouldEqual "The resource requires authentication, which was not supplied with the request"
header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", "secure site")
}
val validCredentials = BasicHttpCredentials("John", "p4ssw0rd")
Get("/secured") ~>
addCredentials(validCredentials) ~> // adds Authorization header
route ~> check {
responseAs[String] shouldEqual "The user is 'John'"
}
val invalidCredentials = BasicHttpCredentials("Peter", "pan")
Get("/secured") ~>
addCredentials(invalidCredentials) ~> // adds Authorization header
route ~> check {
status shouldEqual StatusCodes.Unauthorized
responseAs[String] shouldEqual "The supplied authentication is invalid"
header[`WWW-Authenticate`].get.challenges.head shouldEqual HttpChallenge("Basic", "secure site")
}
}
"authorize-1" in {
def extractUser(userPass: UserPass): String = userPass.user
val config = ConfigFactory.parseString("John = p4ssw0rd\nPeter = pan")
def hasPermissionToPetersLair(userName: String) = userName == "Peter"
val route =
Route.seal {
authenticateBasic(BasicAuth(realm = "secure site", config = config, createUser = extractUser _)) { userName =>
path("peters-lair") {
authorize(hasPermissionToPetersLair(userName)) {
complete(s"'$userName' visited Peter's lair")
}
}
}
}
val johnsCred = BasicHttpCredentials("John", "p4ssw0rd")
Get("/peters-lair") ~>
addCredentials(johnsCred) ~> // adds Authorization header
route ~> check {
status shouldEqual StatusCodes.Forbidden
responseAs[String] shouldEqual "The supplied authentication is not authorized to access this resource"
}
val petersCred = BasicHttpCredentials("Peter", "pan")
Get("/peters-lair") ~>
addCredentials(petersCred) ~> // adds Authorization header
route ~> check {
responseAs[String] shouldEqual "'Peter' visited Peter's lair"
}
}
}

View file

@ -1,48 +0,0 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
import akka.actor.ActorSystem
import akka.http.scaladsl.server.Route
import akka.stream.ActorFlowMaterializer
object MyHandler {
//# example-1
import akka.http.scaladsl.model.HttpResponse
import akka.http.scaladsl.model.StatusCodes._
import akka.http.scaladsl.server._
import Directives._
implicit def myExceptionHandler =
ExceptionHandler {
case e: ArithmeticException =>
extractUri { uri =>
logWarning(s"Request to $uri could not be handled normally")
complete(HttpResponse(InternalServerError, entity = "Bad numbers, bad result!!!"))
}
}
object MyApp {
implicit val system = ActorSystem()
import system.dispatcher
implicit val materializer = ActorFlowMaterializer()
def handler = Route.handlerFlow(`<my-route-definition>`)
}
//#
def `<my-route-definition>`: Route = null
def logWarning(str: String): Unit = {}
}
class ExceptionHandlerExamplesSpec extends RoutingSpec {
import MyHandler._
"example" in {
Get() ~> Route.seal(ctx => ctx.complete((1 / 0).toString)) ~> check {
responseAs[String] === "Bad numbers, bad result!!!"
}
}
}

View file

@ -1,64 +0,0 @@
/*
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.http.server
import akka.actor.ActorSystem
import akka.stream.ActorFlowMaterializer
import akka.http.scaladsl.server.{ Route, MissingCookieRejection }
import scala.concurrent.ExecutionContext
object MyRejectionHandler {
//# example-1
import akka.http.scaladsl.model._
import akka.http.scaladsl.server._
import StatusCodes._
import Directives._
implicit val myRejectionHandler = RejectionHandler.newBuilder()
.handle {
case MissingCookieRejection(cookieName) =>
complete(HttpResponse(BadRequest, entity = "No cookies, no service!!!"))
}
.result()
object MyApp {
implicit val system = ActorSystem()
import system.dispatcher
implicit val materializer = ActorFlowMaterializer()
def handler = Route.handlerFlow(`<my-route-definition>`)
}
//#
def `<my-route-definition>`: Route = null
}
class RejectionHandlerExamplesSpec extends RoutingSpec {
import MyRejectionHandler._
"example" in {
Get() ~> Route.seal(reject(MissingCookieRejection("abc"))) ~> check {
responseAs[String] === "No cookies, no service!!!"
}
}
"example-2" in {
import akka.http.scaladsl.coding.Gzip
val route =
path("order") {
get {
complete("Received GET")
} ~
post {
decodeRequestWith(Gzip) {
complete("Received POST")
}
}
}
}
}

View file

@ -0,0 +1,66 @@
.. _ConnectionLevelApi:
Connection-Level Client-Side API
================================
The connection-level API is the lowest-level client-side API Akka HTTP provides. It gives you full control over when
HTTP connections are opened and closed and how requests are to be send across which connection. As such it offers the
highest flexibility at the cost of providing the least convenience.
Opening HTTP Connections
------------------------
With the connection-level API you open a new HTTP connection by materializing a ``Flow`` returned by the
``Http.outgoingConnection(...)`` method. The target endpoint to which a connection is to be opened needs to be
specified as an argument to ``Http.outgoingConnection(...)``. Here is an example:
.. includecode:: ../../code/docs/http/scaladsl/HttpClientExampleSpec.scala
:include: outgoing-connection-example
Apart from the host name and port the ``Http.outgoingConnection(...)`` method also allows you to specify socket options
and a number of configuration settings for the connection.
Note that no connection is attempted until the returned flow is actually materialized! If the flow is materialized
several times then several independent connections will be opened (one per materialization).
If the connection attempt fails, for whatever reason, the materialized flow will be immediately terminated with a
respective exception.
Request-Response Cycle
----------------------
Once the connection flow has been materialized it is ready to consume ``HttpRequest`` instances from the source it is
attached to. Each request is sent across the connection and incoming responses dispatched to the downstream pipeline.
Of course and as always, back-pressure is adequately maintained across all parts of the
connection. This means that, if the downstream pipeline consuming the HTTP responses is slow, the request source will
eventually be slowed down in sending requests.
Any errors occurring on the underlying connection are surfaced as exceptions terminating the response stream (and
canceling the request source).
Note that, if the source produces subsequent requests before the prior responses have arrived, these requests will be
pipelined__ across the connection, which is something that is not supported by all HTTP servers.
Also, if the server closes the connection before responses to all requests have been received this will result in the
response stream being terminated with a truncation error.
__ http://en.wikipedia.org/wiki/HTTP_pipelining
Closing Connections
-------------------
Akka HTTP actively closes an established connection upon reception of a response containing ``Connection: close`` header.
Of course the connection can also be closed by the server.
An application can actively trigger the closing of the connection by completing the request stream. In this case the
underlying TCP connection will be closed when the last pending response has been received.
Timeouts
--------
Currently Akka HTTP doesn't implement client-side request timeout checking itself as this functionality can be regarded
as a more general purpose streaming infrastructure feature.
However, akka-stream should soon provide such a feature.

View file

@ -0,0 +1,147 @@
.. _HostLevelApi:
Host-Level Client-Side API
==========================
As opposed to the :ref:`ConnectionLevelApi` the host-level API relieves you from manually managing individual HTTP
connections. It autonomously manages a configurable pool of connections to *one particular target endpoint* (i.e.
host/port combination).
Requesting a Host Connection Pool
---------------------------------
The best way to get a hold of a connection pool to a given target endpoint is the ``Http.cachedHostConnectionPool(...)``
method, which returns a ``Flow`` that can be "baked" into an application-level stream setup. This flow is also called
a "pool client flow".
The connection pool underlying a pool client flow is cached. For every ``ActorSystem``, target endpoint and pool
configuration there will never be more than a single pool live at any time.
Also, the HTTP layer transparently manages idle shutdown and restarting of connection pools as configured.
The client flow instances therefore remain valid throughout the lifetime of the application, i.e. they can be
materialized as often as required and the time between individual materialization is of no importance.
When you request a pool client flow with ``Http.cachedHostConnectionPool(...)`` Akka HTTP will immediately start
the pool, even before the first client flow materialization. However, this running pool will not actually open the
first connection to the target endpoint until the first request has arrived.
Configuring a Host Connection Pool
----------------------------------
Apart from the connection-level config settings and socket options there are a number of settings that allow you to
influence the behavior of the connection pool logic itself.
Check out the ``akka.http.client.host-connection-pool`` section of the Akka HTTP :ref:`akka-http-configuration` for
more information about which settings are available and what they mean.
Note that, if you request pools with different configurations for the same target host you will get *independent* pools.
This means that, in total, your application might open more concurrent HTTP connections to the target endpoint than any
of the individual pool's ``max-connections`` settings allow!
There is one setting that likely deserves a bit deeper explanation: ``max-open-requests``.
This setting limits the maximum number of requests that can be open at any time for a single connection pool.
If an application calls ``Http.cachedHostConnectionPool(...)`` 3 times (with the same endpoint and settings) it will get
back 3 different client flow instances for the same pool. If each of these client flows is then materialized 4 times
(concurrently) the application will have 12 concurrently running client flow materializations.
All of these share the resources of the single pool.
This means that, if the pool's ``pipelining-limit`` is left at ``1``, no more than 12 requests can be open at any time.
With a ``pipelining-limit`` of ``8`` and 12 concurrent client flow materializations the theoretical open requests
maximum is 96.
The ``max-open-requests`` config setting allows for applying a hard limit which serves mainly as a protection against
erroneous connection pool use, e.g. because the application is materializing too many client flows that all compete for
the same pooled connections.
.. _using-a-host-connection-pool:
Using a Host Connection Pool
----------------------------
The "pool client flow" returned by ``Http.cachedHostConnectionPool(...)`` has the following type::
Flow[(HttpRequest, T), (Try[HttpResponse], T), HostConnectionPool]
This means it consumes tuples of type ``(HttpRequest, T)`` and produces tuples of type ``(Try[HttpResponse], T)``
which might appear more complicated than necessary on first sight.
The reason why the pool API includes objects of custom type ``T`` on both ends lies in the fact that the underlying
transport usually comprises more than a single connection and as such the pool client flow often generates responses in
an order that doesn't directly match the consumed requests.
We could have built the pool logic in a way that reorders responses according to their requests before dispatching them
to the application, but this would have meant that a single slow response could block the delivery of potentially many
responses that would otherwise be ready for consumption by the application.
In order to prevent unnecessary head-of-line blocking the pool client-flow is allowed to dispatch responses as soon as
they arrive, independently of the request order. Of course this means that there needs to be another way to associate a
response with its respective request. The way that this is done is by allowing the application to pass along a custom
"context" object with the request, which is then passed back to the application with the respective response.
This context object of type ``T`` is completely opaque to Akka HTTP, i.e. you can pick whatever works best for your
particular application scenario.
Connection Allocation Logic
---------------------------
This is how Akka HTTP allocates incoming requests to the available connection "slots":
1. If there is a connection alive and currently idle then schedule the request across this connection.
2. If no connection is idle and there is still an unconnected slot then establish a new connection.
3. If all connections are already established and "loaded" with other requests then pick the connection with the least
open requests (< the configured ``pipelining-limit``) that only has requests with idempotent methods scheduled to it,
if there is one.
4. Otherwise apply back-pressure to the request source, i.e. stop accepting new requests.
For more information about scheduling more than one request at a time across a single connection see
`this wikipedia entry on HTTP pipelining`__.
__ http://en.wikipedia.org/wiki/HTTP_pipelining
Retrying a Request
------------------
If the ``max-retries`` pool config setting is greater than zero the pool retries idempotent requests for which
a response could not be successfully retrieved. Idempotent requests are those whose HTTP method is defined to be
idempotent by the HTTP spec, which are all the ones currently modelled by Akka HTTP except for the ``POST``, ``PATCH``
and ``CONNECT`` methods.
When a response could not be received for a certain request there are essentially three possible error scenarios:
1. The request got lost on the way to the server.
2. The server experiences a problem while processing the request.
3. The response from the server got lost on the way back.
Since the host connector cannot know which one of these possible reasons caused the problem and therefore ``PATCH`` and
``POST`` requests could have already triggered a non-idempotent action on the server these requests cannot be retried.
In these cases, as well as when all retries have not yielded a proper response, the pool produces a failed ``Try``
(i.e. a ``scala.util.Failure``) together with the custom request context.
Pool Shutdown
-------------
Completing a pool client flow will simply detach the flow from the pool. The connection pool itself will continue to run
as it may be serving other client flows concurrently or in the future. Only after the configured ``idle-timeout`` for
the pool has expired will Akka HTTP automatically terminate the pool and free all its resources.
If a new client flow is requested with ``Http.cachedHostConnectionPool(...)`` or if an already existing client flow is
re-materialized the respective pool is automatically and transparently restarted.
In addition to the automatic shutdown via the configured idle timeouts it's also possible to trigger the immediate
shutdown of specific pool by calling ``shutdown()`` on the ``Http.HostConnectionPool`` instance that a pool client
flow materializes into. This ``shutdown()`` call produces a ``Future[Unit]`` which is fulfilled when the pool
termination has been completed.
It's also possible to trigger the immediate termination of *all* connection pools in the ``ActorSystem`` at the same
time by calling ``Http.shutdownAllConnectionPools()``. This call too produces a ``Future[Unit]`` which is fulfilled when
all pools have terminated.
Example
-------
.. includecode:: ../../code/docs/http/scaladsl/HttpClientExampleSpec.scala
:include: host-level-example

View file

@ -0,0 +1,4 @@
Client-Side HTTPS Support
=========================
TODO

View file

@ -0,0 +1,31 @@
.. _http-client-side:
Consuming HTTP-based Services (Client-Side)
===========================================
All client-side functionality of Akka HTTP, for consuming HTTP-based services offered by other systems, is currently
provided by the ``akka-http-core`` module.
Depending your application's specific needs you can choose from three different API levels:
:ref:`ConnectionLevelApi`
for full-control over when HTTP connections are opened/closed and how requests are scheduled across them
:ref:`HostLevelApi`
for letting Akka HTTP manage a connection-pool to *one specific* host/port endpoint
:ref:`RequestLevelApi`
for letting Akka HTTP perform all connection management
You can interact with different API levels at the same time and, independently of which API level you choose,
Akka HTTP will happily handle many thousand concurrent connections to a single or many different hosts.
.. toctree::
:maxdepth: 2
connection-level
host-level
request-level
https-support
websocket-support

View file

@ -0,0 +1,48 @@
.. _RequestLevelApi:
Request-Level Client-Side API
=============================
The request-level API is the most convenient way of using Akka HTTP's client-side. It internally builds upon the
:ref:`HostLevelApi` to provide you with a simple and easy-to-use way of retrieving HTTP responses from remote servers.
Depending on your preference you can pick the flow-based or the future-based variant.
Flow-Based Variant
------------------
The flow-based variant of the request-level client-side API is presented by the ``Http.superPool(...)`` method.
It creates a new "super connection pool flow", which routes incoming requests to a (cached) host connection pool
depending on their respective effective URI.
The ``Flow`` returned by ``Http.superPool(...)`` is very similar to the one from the :ref:`HostLevelApi`, so the
:ref:`using-a-host-connection-pool` section also applies here.
However, there is one notable difference between a "host connection pool client flow" for the host-level API and a
"super-pool flow":
Since in the former case the flow has an implicit target host context the requests it takes don't need to have absolute
URIs or a valid ``Host`` header. The host connection pool will automatically add a ``Host`` header if required.
For a super-pool flow this is not the case. All requests to a super-pool must either have an absolute URI or a valid
``Host`` header, because otherwise it'd be impossible to find out which target endpoint to direct the request to.
Future-Based Variant
--------------------
Sometimes your HTTP client needs are very basic. You simply need the HTTP response for a certain request and don't
want to bother with setting up a full-blown streaming infrastructure.
For these cases Akka HTTP offers the ``Http.singleRequest(...)`` method, which simply turns an ``HttpRequest`` instance
into ``Future[HttpResponse]``. Internally the request is dispatched across the (cached) host connection pool for the
request's effective URI.
Just like in the case of the super-pool flow described above the request must have either an absolute URI or a valid
``Host`` header, otherwise the returned future will be completed with an error.
Example
-------
.. includecode:: ../../code/docs/http/scaladsl/HttpClientExampleSpec.scala
:include: single-request-example

View file

@ -0,0 +1,4 @@
Client-Side WebSocket Support
=============================
TODO

View file

@ -1,4 +0,0 @@
Client API
==========
(todo)

View file

@ -0,0 +1,4 @@
Encoding / Decoding
===================
...

View file

@ -0,0 +1,230 @@
.. _http-model-scala:
HTTP Model
==========
Akka HTTP model contains a deeply structured, fully immutable, case-class based model of all the major HTTP data
structures, like HTTP requests, responses and common headers.
It lives in the *akka-http-core* module and forms the basis for most of Akka HTTP's APIs.
Overview
--------
Since akka-http-core provides the central HTTP data structures you will find the following import in quite a
few places around the code base (and probably your own code as well):
.. includecode:: ../../code/docs/http/scaladsl/ModelSpec.scala
:include: import-model
This brings all of the most relevant types in scope, mainly:
- ``HttpRequest`` and ``HttpResponse``, the central message model
- ``headers``, the package containing all the predefined HTTP header models and supporting types
- Supporting types like ``Uri``, ``HttpMethods``, ``MediaTypes``, ``StatusCodes``, etc.
A common pattern is that the model of a certain entity is represented by an immutable type (class or trait),
while the actual instances of the entity defined by the HTTP spec live in an accompanying object carrying the name of
the type plus a trailing plural 's'.
For example:
- Defined ``HttpMethod`` instances live in the ``HttpMethods`` object.
- Defined ``HttpCharset`` instances live in the ``HttpCharsets`` object.
- Defined ``HttpEncoding`` instances live in the ``HttpEncodings`` object.
- Defined ``HttpProtocol`` instances live in the ``HttpProtocols`` object.
- Defined ``MediaType`` instances live in the ``MediaTypes`` object.
- Defined ``StatusCode`` instances live in the ``StatusCodes`` object.
HttpRequest
-----------
``HttpRequest`` and ``HttpResponse`` are the basic case classes representing HTTP messages.
An ``HttpRequest`` consists of
- a method (GET, POST, etc.)
- a URI
- a seq of headers
- an entity (body data)
- a protocol
Here are some examples how to construct an ``HttpRequest``:
.. includecode:: ../../code/docs/http/scaladsl/ModelSpec.scala
:include: construct-request
All parameters of ``HttpRequest.apply`` have default values set, so ``headers`` for example don't need to be specified
if there are none. Many of the parameters types (like ``HttpEntity`` and ``Uri``) define implicit conversions
for common use cases to simplify the creation of request and response instances.
HttpResponse
------------
An ``HttpResponse`` consists of
- a status code
- a seq of headers
- an entity (body data)
- a protocol
Here are some examples how to construct an ``HttpResponse``:
.. includecode:: ../../code/docs/http/scaladsl/ModelSpec.scala
:include: construct-response
In addition to the simple ``HttpEntity`` constructors create an entity from a fixed ``String`` or ``ByteString`` shown
as here the Akka HTTP model defines a number of subclasses of ``HttpEntity`` which allow body data to be specified as a
stream of bytes.
.. _HttpEntity:
HttpEntity
----------
An ``HttpEntity`` carries the data bytes of a message together with its Content-Type and, if known, its Content-Length.
In Akka HTTP there are five different kinds of entities which model the various ways that message content can be
received or sent:
HttpEntity.Strict
The simplest entity, which is used when all the entity are already available in memory.
It wraps a plain ``ByteString`` and represents a standard, unchunked entity with a known ``Content-Length``.
HttpEntity.Default
The general, unchunked HTTP/1.1 message entity.
It has a known length and presents its data as a ``Source[ByteString]`` which can only materialized once.
It is an error if the provided source doesn't produce exactly as many bytes as specified.
On the wire, a ``Strict`` entity and a ``Default`` entity cannot be distinguished.
HttpEntity.Chunked
The model for HTTP/1.1 `chunked content`__ (i.e. sent with ``Transfer-Encoding: chunked``).
The content length is unknown and the individual chunks are presented as a ``Source[HttpEntity.ChunkStreamPart]``.
A ``ChunkStreamPart`` is either a non-empty ``Chunk`` or a ``LastChunk`` containing optional trailer headers.
The stream consists of zero or more ``Chunked`` parts and can be terminated by an optional ``LastChunk`` part.
HttpEntity.CloseDelimited
An unchunked entity of unknown length that is implicitly delimited by closing the connection (``Connection: close``).
The content data are presented as a ``Source[ByteString]``.
Since the connection must be closed after sending an entity of this type it can only be used on the server-side for
sending a response.
Also, the main purpose of ``CloseDelimited`` entities is compatibility with HTTP/1.0 peers, which do not support
chunked transfer encoding. If you are building a new application and are not constrained by legacy requirements you
shouldn't rely on ``CloseDelimited`` entities, since implicit terminate-by-connection-close is not a robust way of
signaling response end, especially in the presence of proxies. Additionally this type of entity prevents connection
reuse which can seriously degrade performance. Use ``HttpEntity.Chunked`` instead!
HttpEntity.IndefiniteLength
A streaming entity of unspecified length for use in a ``Multipart.BodyPart``.
__ http://tools.ietf.org/html/rfc7230#section-4.1
Entity types ``Strict``, ``Default``, and ``Chunked`` are a subtype of ``HttpEntity.Regular`` which allows to use them
for requests and responses. In contrast, ``HttpEntity.CloseDelimited`` can only be used for responses.
Streaming entity types (i.e. all but ``Strict``) cannot be shared or serialized. To create a strict, sharable copy of an
entity or message use ``HttpEntity.toStrict`` or ``HttpMessage.toStrict`` which returns a ``Future`` of the object with
the body data collected into a ``ByteString``.
The ``HttpEntity`` companion object contains several helper constructors to create entities from common types easily.
You can pattern match over the subtypes of ``HttpEntity`` if you want to provide special handling for each of the
subtypes. However, in many cases a recipient of an ``HttpEntity`` doesn't care about of which subtype an entity is
(and how data is transported exactly on the HTTP layer). Therefore, a general
``HttpEntity::dataBytes: Source[ByteString, Any]`` is provided which allows access to the data of an entity regardless
of its concrete subtype.
.. note::
When to use which subtype?
- Use ``Strict`` if the amount of data is "small" and already available in memory (e.g. as a ``String`` or ``ByteString``)
- Use ``Default`` if the data is generated by a streaming data source and the size of the data is known
- Use ``Chunked`` for an entity of unknown length
- Use ``CloseDelimited`` for a response as a legacy alternative to ``Chunked`` if the client doesn't support
chunked transfer encoding. Otherwise use ``Chunked``!
- In a ``Multipart.Bodypart`` use ``IndefiniteLength`` for content of unknown length.
.. caution::
When you receive a non-strict message from a connection then additional data are only read from the network when you
request them by consuming the entity data stream. This means that, if you *don't* consume the entity stream then the
connection will effectively be stalled. In particular no subsequent message (request or response) will be read from
the connection as the entity of the current message "blocks" the stream.
Therefore you must make sure that you always consume the entity data, even in the case that you are not actually
interested in it!
Header Model
------------
Akka HTTP contains a rich model of the most common HTTP headers. Parsing and rendering is done automatically so that
applications don't need to care for the actual syntax of headers. Headers not modelled explicitly are represented
as a ``RawHeader`` (which is essentially a String/String name/value pair).
See these examples of how to deal with headers:
.. includecode:: ../../code/docs/http/scaladsl/ModelSpec.scala
:include: headers
HTTP Headers
------------
When the Akka HTTP server receives an HTTP request it tries to parse all its headers into their respective
model classes. Independently of whether this succeeds or not, the HTTP layer will
always pass on all received headers to the application. Unknown headers as well as ones with invalid syntax (according
to the header parser) will be made available as ``RawHeader`` instances. For the ones exhibiting parsing errors a
warning message is logged depending on the value of the ``illegal-header-warnings`` config setting.
Some headers have special status in HTTP and are therefore treated differently from "regular" headers:
Content-Type
The Content-Type of an HTTP message is modeled as the ``contentType`` field of the ``HttpEntity``.
The ``Content-Type`` header therefore doesn't appear in the ``headers`` sequence of a message.
Also, a ``Content-Type`` header instance that is explicitly added to the ``headers`` of a request or response will
not be rendered onto the wire and trigger a warning being logged instead!
Transfer-Encoding
Messages with ``Transfer-Encoding: chunked`` are represented via the ``HttpEntity.Chunked`` entity.
As such chunked messages that do not have another deeper nested transfer encoding will not have a ``Transfer-Encoding``
header in their ``headers`` sequence.
Similarly, a ``Transfer-Encoding`` header instance that is explicitly added to the ``headers`` of a request or
response will not be rendered onto the wire and trigger a warning being logged instead!
Content-Length
The content length of a message is modelled via its :ref:`HttpEntity`. As such no ``Content-Length`` header will ever
be part of a message's ``header`` sequence.
Similarly, a ``Content-Length`` header instance that is explicitly added to the ``headers`` of a request or
response will not be rendered onto the wire and trigger a warning being logged instead!
Server
A ``Server`` header is usually added automatically to any response and its value can be configured via the
``akka.http.server.server-header`` setting. Additionally an application can override the configured header with a
custom one by adding it to the response's ``header`` sequence.
User-Agent
A ``User-Agent`` header is usually added automatically to any request and its value can be configured via the
``akka.http.client.user-agent-header`` setting. Additionally an application can override the configured header with a
custom one by adding it to the request's ``header`` sequence.
Date
The ``Date`` response header is added automatically but can be overridden by supplying it manually.
Connection
On the server-side Akka HTTP watches for explicitly added ``Connection: close`` response headers and as such honors
the potential wish of the application to close the connection after the respective response has been sent out.
The actual logic for determining whether to close the connection is quite involved. It takes into account the
request's method, protocol and potential ``Connection`` header as well as the response's protocol, entity and
potential ``Connection`` header. See `this test`__ for a full table of what happens when.
__ @github@/akka-http-core/src/test/scala/akka/http/impl/engine/rendering/ResponseRendererSpec.scala#L422
Parsing / Rendering
-------------------
Parsing and rendering of HTTP data structures is heavily optimized and for most types there's currently no public API
provided to parse (or render to) Strings or byte arrays.

View file

@ -0,0 +1,21 @@
.. _http-scala-common:
Common Abstractions (Client- and Server-Side)
=============================================
HTTP and related specifications define a great number of concepts and functionality that is not specific to either
HTTP's client- or server-side since they are meaningful on both end of an HTTP connection.
The documentation for their counterparts in Akka HTTP lives in this section rather than in the ones for the
:ref:`Client-Side API <http-client-side>`, :ref:`http-low-level-server-side-api` or :ref:`http-high-level-server-side-api`,
which are specific to one side only.
.. toctree::
:maxdepth: 2
http-model
marshalling
unmarshalling
de-coding
json-support
xml-support

View file

@ -0,0 +1,4 @@
JSON Support
============
...

View file

@ -0,0 +1,6 @@
.. _http-marshalling-scala:
Marshalling
===========
...

View file

@ -0,0 +1,6 @@
.. _http-unmarshalling-scala:
Unmarshalling
=============
...

View file

@ -0,0 +1,4 @@
XML Support
===========
...

View file

@ -0,0 +1,28 @@
.. _akka-http-configuration:
Configuration
=============
Just like any other Akka module Akka HTTP is configured via `Typesafe Config`_.
Usually this means that you provide an ``application.conf`` which contains all the application-specific settings that
differ from the default ones provided by the reference configuration files from the individual Akka modules.
These are the relevant default configuration values for the Akka HTTP modules.
akka-http-core
~~~~~~~~~~~~~~
.. literalinclude:: ../../../../akka-http-core/src/main/resources/reference.conf
:language: none
akka-http-core-scala
~~~~~~~~~~~~~~~~~~~~
.. literalinclude:: ../../../../akka-http-scala/src/main/resources/reference.conf
:language: none
The other Akka HTTP modules do not offer any configuration via `Typesafe Config`_.
.. _Typesafe Config: https://github.com/typesafehub/config

View file

@ -1,138 +0,0 @@
.. _Directives:
Directives
==========
A "Directive" is a small building block to construct arbitrarily complex route structures.
Here is a simple example of a route built from directives:
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
:snippet: example-1
The general anatomy of a directive is as follows::
name(arguments) { extractions =>
... // inner Route
}
It has a name, zero or more arguments and optionally an inner Route. Additionally directives can "extract" a number of
values and make them available to their inner routes as function arguments. When seen "from the outside" a directive
with its inner Route form an expression of type ``Route``.
What Directives do
------------------
A directive can do one or more of the following:
.. rst-class:: wide
* Transform the incoming ``RequestContext`` before passing it on to its inner Route
* Filter the ``RequestContext`` according to some logic, i.e. only pass on certain requests and reject all others
* Extract values from the ``RequestContext`` and make them available to its inner Route as "extractions"
* Complete the request
The first point deserves some more discussion. The ``RequestContext`` is the central object that is passed on through a
route structure (also see :ref:`RequestContext`). When a directive (or better the Route it built) receives a ``RequestContext``
it can decide to pass this instance on unchanged to its inner Route or it can create a copy of the ``RequestContext`` instance,
with one or more changes, and pass on this copy to its inner Route. Typically this is good for two things:
* Transforming the ``HttpRequest`` instance
* "Hooking in" a response transformation function that changes the ``RouteResponse`` (and therefore the response).
This means a ``Directive`` completely wraps the functionality of its inner routes and can apply arbitrarily complex
transformations, both (or either) on the request and on the response side.
Composing Directives
--------------------
As you have seen from the examples presented so far the "normal" way of composing directives is nesting. Let's take
another look at the example from above:
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
:snippet: example-2
Here the ``get`` and ``put`` directives are chained together with the ``~`` operator to form a higher-level route that
serves as the inner Route of the ``path`` directive. To make this structure more explicit you could also write the whole
thing like this:
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
:snippet: example-3
What you can't see from this snippet is that directives are not implemented as simple methods but rather as stand-alone
objects of type ``Directive``. This gives you more flexibility when composing directives. For example you can
also use the ``|`` operator on directives. Here is yet another way to write the example:
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
:snippet: example-4
If you have a larger route structure where the ``(get | put)`` snippet appears several times you could also factor it
out like this:
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
:snippet: example-5
As an alternative to nesting you can also use the `&` operator:
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
:snippet: example-6
And once again, you can factor things out if you want:
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
:snippet: example-7
This type of combining directives with the ``|`` and ``&`` operators as well as "saving" more complex directive
configurations as a ``val`` works across the board, with all directives taking inner routes.
There is one more "ugly" thing remaining in our snippet: we have to fall back to the lowest-level route definition,
directly manipulating the ``RequestContext``, in order to get to the request method. It'd be nicer if we could somehow
"extract" the method name in a special directive, so that we can express our inner-most route with a simple
``complete``. As it turns out this is easy with the ``extract`` directive:
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
:snippet: example-8
Or differently:
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
:snippet: example-9
Now, pushing the "factoring out" of directive configurations to its extreme, we end up with this:
.. includecode2:: ../code/docs/http/server/DirectiveExamplesSpec.scala
:snippet: example-A
Note that going this far with "compressing" several directives into a single one probably doesn't result in the most
readable and therefore maintainable routing code. It might even be that the very first of this series of examples
is in fact the most readable one.
Still, the purpose of the exercise presented here is to show you how flexible directives can be and how you can
use their power to define your web service behavior at the level of abstraction that is right for **your** application.
Type Safety of Directives
-------------------------
When you combine directives with the ``|`` and ``&`` operators the routing DSL makes sure that all extractions work as
expected and logical constraints are enforced at compile-time.
For example you cannot ``|`` a directive producing an extraction with one that doesn't::
val route = path("order" / IntNumber) | get // doesn't compile
Also the number of extractions and their types have to match up::
val route = path("order" / IntNumber) | path("order" / DoubleNumber) // doesn't compile
val route = path("order" / IntNumber) | parameter('order.as[Int]) // ok
When you combine directives producing extractions with the ``&`` operator all extractions will be properly gathered up::
val order = path("order" / IntNumber) & parameters('oem, 'expired ?)
val route =
order { (orderId, oem, expired) =>
...
}
Directives offer a great way of constructing your web service logic from small building blocks in a plug and play
fashion while maintaining DRYness and full type-safety. If the large range of :ref:`Predefined Directives` does not
fully satisfy your needs you can also very easily create :ref:`Custom Directives`.

View file

@ -1,161 +0,0 @@
.. _Predefined Directives:
Predefined Directives (alphabetically)
======================================
.. rst-class:: table table-striped
====================================== =================================================================================
Directive Description
====================================== =================================================================================
:ref:`-cancelAllRejections-` Adds a ``TransformationRejection`` to rejections from its inner Route, which
cancels other rejections according to a predicate function
:ref:`-cancelRejection-` Adds a ``TransformationRejection`` cancelling all rejections equal to a given one
:ref:`-clientIP-` Extracts the IP address of the client from either the ``X-Forwarded-For``,
``Remote-Address`` or ``X-Real-IP`` request header
:ref:`-complete-` Completes the request with a given response, several overloads
:ref:`-compressResponse-` Compresses responses coming back from its inner Route using either ``Gzip`` or
``Deflate`` unless the request explicitly sets ``Accept-Encoding`` to ``identity``.
:ref:`-compressResponseIfRequested-` Compresses responses coming back from its inner Route using either ``Gzip`` or
``Deflate``, but only when the request explicitly accepts one of them.
:ref:`-conditional-` Depending on the given ETag and Last-Modified values responds with
``304 Not Modified`` if the request comes with the respective conditional headers.
:ref:`-cookie-` Extracts an ``HttpCookie`` with a given name or rejects if no such cookie is
present in the request
:ref:`-decodeRequest-` Decompresses incoming requests using a given Decoder
:ref:`-decompressRequest-` Decompresses incoming requests using either ``Gzip``, ``Deflate``, or ``NoEncoding``
:ref:`-delete-` Rejects all non-DELETE requests
:ref:`-deleteCookie-` Adds a ``Set-Cookie`` header expiring the given cookie to all ``HttpResponse``
replies of its inner Route
:ref:`-encodeResponse-` Compresses responses coming back from its inner Route using a given Encoder
:ref:`-entity-` Unmarshalls the requests entity according to a given definition, rejects in
case of problems
:ref:`-extract-` Extracts a single value from the ``RequestContext`` using a function
``RequestContext => T``
:ref:`-extractRequest-` Extracts the complete request
:ref:`-failWith-` Bubbles the given error up the response chain, where it is dealt with by the
closest :ref:`-handleExceptions-` directive and its ExceptionHandler
:ref:`-formField-` Extracts the value of an HTTP form field, rejects if the request doesn't come
with a field matching the definition
:ref:`-formFields-` Same as :ref:`-formField-`, except for several fields at once
:ref:`-get-` Rejects all non-GET requests
:ref:`-getFromBrowseableDirectories-` Same as :ref:`-getFromBrowseableDirectory-`, but allows for serving the "union"
of several directories as one single "virtual" one
:ref:`-getFromBrowseableDirectory-` Completes GET requests with the content of a file underneath a given directory,
renders directory contents as browsable listings
:ref:`-getFromDirectory-` Completes GET requests with the content of a file underneath a given directory
:ref:`-getFromFile-` Completes GET requests with the content of a given file
:ref:`-getFromResource-` Completes GET requests with the content of a given resource
:ref:`-getFromResourceDirectory-` Same as :ref:`-getFromDirectory-` except that the file is not fetched from the
file system but rather from a "resource directory"
:ref:`-handleExceptions-` Converts exceptions thrown during evaluation of its inner Route into
``HttpResponse`` replies using a given ExceptionHandler
:ref:`-handleRejections-` Converts rejections produced by its inner Route into ``HttpResponse`` replies
using a given RejectionHandler
:ref:`-handleWith-` Completes the request using a given function. Uses the in-scope ``Unmarshaller``
and ``Marshaller`` for converting to and from the function
:ref:`-head-` Rejects all non-HEAD requests
:ref:`-headerValue-` Extracts an HTTP header value using a given function, rejects if no value can
be extracted
:ref:`-headerValueByName-` Extracts an HTTP header value by selecting a header by name
:ref:`-headerValueByType-` Extracts an HTTP header value by selecting a header by type
:ref:`-headerValuePF-` Same as :ref:`-headerValue-`, but with a ``PartialFunction``
:ref:`-host-` Rejects all requests with a hostname different from a given definition,
can extract the hostname using a regex pattern
:ref:`-hostName-` Extracts the hostname part of the requests ``Host`` header value
:ref:`-listDirectoryContents-` Completes GET requests with a unified listing of the contents of one or more
given directories
:ref:`-logRequest-` Produces a log entry for every incoming request
:ref:`-logRequestResult-` Produces a log entry for every response or rejection coming back from its inner
route, allowing for coalescing with the corresponding request
:ref:`-logResult-` Produces a log entry for every response or rejection coming back from its inner
route
:ref:`-mapResponse-` Transforms the ``HttpResponse`` coming back from its inner Route
:ref:`-mapResponseEntity-` Transforms the entity of the ``HttpResponse`` coming back from its inner Route
:ref:`-mapResponseHeaders-` Transforms the headers of the ``HttpResponse`` coming back from its inner Route
:ref:`-mapInnerRoute-` Transforms its inner Route with a ``Route => Route`` function
:ref:`-mapRejections-` Transforms all rejections coming back from its inner Route
:ref:`-mapRequest-` Transforms the incoming ``HttpRequest``
:ref:`-mapRequestContext-` Transforms the ``RequestContext``
:ref:`-mapRouteResult-` Transforms all responses coming back from its inner Route with a ``Any => Any``
function
:ref:`-mapRouteResultPF-` Same as :ref:`-mapRouteResult-`, but with a ``PartialFunction``
:ref:`-method-` Rejects if the request method does not match a given one
:ref:`-overrideMethodWithParameter-` Changes the HTTP method of the request to the value of the specified query string
parameter
:ref:`-onComplete-` "Unwraps" a ``Future[T]`` and runs its inner route after future completion with
the future's value as an extraction of type ``Try[T]``
:ref:`-onFailure-` "Unwraps" a ``Future[T]`` and runs its inner route when the future has failed
with the future's failure exception as an extraction of type ``Throwable``
:ref:`-onSuccess-` "Unwraps" a ``Future[T]`` and runs its inner route after future completion with
the future's value as an extraction of type ``T``
:ref:`-optionalCookie-` Extracts an ``HttpCookie`` with a given name, if the cookie is not present in the
request extracts ``None``
:ref:`-optionalHeaderValue-` Extracts an optional HTTP header value using a given function
:ref:`-optionalHeaderValueByName-` Extracts an optional HTTP header value by selecting a header by name
:ref:`-optionalHeaderValueByType-` Extracts an optional HTTP header value by selecting a header by type
:ref:`-optionalHeaderValuePF-` Extracts an optional HTTP header value using a given partial function
:ref:`-options-` Rejects all non-OPTIONS requests
:ref:`-parameter-` Extracts the value of a request query parameter, rejects if the request doesn't
come with a parameter matching the definition
:ref:`-parameterMap-` Extracts the requests query parameters as a ``Map[String, String]``
:ref:`-parameterMultiMap-` Extracts the requests query parameters as a ``Map[String, List[String]]``
:ref:`-parameters-` Same as :ref:`-parameter-`, except for several parameters at once
:ref:`-parameterSeq-` Extracts the requests query parameters as a ``Seq[(String, String)]``
:ref:`-pass-` Does nothing, i.e. passes the ``RequestContext`` unchanged to its inner Route
:ref:`-patch-` Rejects all non-PATCH requests
:ref:`-path-` Extracts zero+ values from the ``unmatchedPath`` of the ``RequestContext``
according to a given ``PathMatcher``, rejects if no match
:ref:`-pathEnd-` Only passes on the request to its inner route if the request path has been
matched completely, rejects otherwise
:ref:`-pathEndOrSingleSlash-` Only passes on the request to its inner route if the request path has been matched
completely or only consists of exactly one remaining slash, rejects otherwise
:ref:`-pathPrefix-` Same as :ref:`-path-`, but also matches (and consumes) prefixes of the unmatched
path (rather than only the complete unmatched path at once)
:ref:`-pathPrefixTest-` Like :ref:`-pathPrefix-` but without "consumption" of the matched path (prefix).
:ref:`-pathSingleSlash-` Only passes on the request to its inner route if the request path consists of
exactly one remaining slash
:ref:`-pathSuffix-` Like as :ref:`-pathPrefix-`, but for suffixes rather than prefixed of the
unmatched path
:ref:`-pathSuffixTest-` Like :ref:`-pathSuffix-` but without "consumption" of the matched path (suffix).
:ref:`-post-` Rejects all non-POST requests
:ref:`-produce-` Uses the in-scope marshaller to extract a function that can be used for
completing the request with an instance of a custom type
:ref:`-provide-` Injects a single value into a directive, which provides it as an extraction
:ref:`-put-` Rejects all non-PUT requests
:ref:`-rawPathPrefix-` Applies a given ``PathMatcher`` directly to the unmatched path of the
``RequestContext``, i.e. without implicitly consuming a leading slash
:ref:`-rawPathPrefixTest-` Checks whether the unmatchedPath of the ``RequestContext`` has a prefix matched
by a ``PathMatcher``
:ref:`-redirect-` Completes the request with redirection response of the given type to a given URI
:ref:`-reject-` Rejects the request with a given set of rejections
:ref:`-rejectEmptyResponse-` Converts responses with an empty entity into a rejection
:ref:`-requestEncodedWith-` Rejects the request if its encoding doesn't match a given one
:ref:`-requestEntityEmpty-` Rejects the request if its entity is not empty
:ref:`-requestEntityPresent-` Rejects the request if its entity is empty
:ref:`-requestUri-` Extracts the complete request URI
:ref:`-respondWithHeader-` Adds a given response header to all ``HttpResponse`` replies from its inner
Route
:ref:`-respondWithHeaders-` Same as :ref:`-respondWithHeader-`, but for several headers at once
:ref:`-respondWithMediaType-` Overrides the media-type of all ``HttpResponse`` replies from its inner Route,
rejects if the media-type is not accepted by the client
:ref:`-respondWithSingletonHeader-` Adds a given response header to all ``HttpResponse`` replies from its inner
Route, if a header with the same name is not yet present
:ref:`-respondWithSingletonHeaders-` Same as :ref:`-respondWithSingletonHeader-`, but for several headers at once
:ref:`-respondWithStatus-` Overrides the response status of all ``HttpResponse`` replies coming back from
its inner Route
:ref:`-responseEncodingAccepted-` Rejects the request if the client doesn't accept a given encoding for the
response
:ref:`-mapUnmatchedPath-` Transforms the ``unmatchedPath`` of the ``RequestContext`` using a given function
:ref:`-scheme-` Rejects a request if its Uri scheme does not match a given one
:ref:`-schemeName-` Extracts the request Uri scheme
:ref:`-setCookie-` Adds a ``Set-Cookie`` header to all ``HttpResponse`` replies of its inner Route
:ref:`-textract-` Extracts a ``TupleN`` of values from the ``RequestContext`` using a function
:ref:`-hprovide-` Injects a ``TupleN`` of values into a directive, which provides them as
extractions
:ref:`-unmatchedPath-` Extracts the unmatched path from the RequestContext
:ref:`-validate-` Passes or rejects the request depending on evaluation of a given conditional
expression
:ref:`-withRangeSupport-` Transforms the response from its inner route into a ``206 Partial Content``
response if the client requested only part of the resource with a ``Range`` header.
====================================== =================================================================================

View file

@ -1,80 +0,0 @@
.. _BasicDirectives:
BasicDirectives
===============
Basic directives are building blocks for building :ref:`Custom Directives`. As such they
usually aren't used in a route directly but rather in the definition of new directives.
.. _ProvideDirectives:
Directives to provide values to inner routes
--------------------------------------------
These directives allow to provide the inner routes with extractions. They can be distinguished
on two axes: a) provide a constant value or extract a value from the ``RequestContext`` b) provide
a single value or an HList of values.
* :ref:`-extract-`
* :ref:`-textract-`
* :ref:`-provide-`
* :ref:`-tprovide-`
.. _Request Transforming Directives:
Directives transforming the request
-----------------------------------
* :ref:`-mapRequestContext-`
* :ref:`-mapRequest-`
.. _Response Transforming Directives:
Directives transforming the response
------------------------------------
These directives allow to hook into the response path and transform the complete response or
the parts of a response or the list of rejections:
* :ref:`-mapResponse-`
* :ref:`-mapResponseEntity-`
* :ref:`-mapResponseHeaders-`
* :ref:`-mapRejections-`
.. _Result Transformation Directives:
Directives transforming the RouteResult
---------------------------------------
These directives allow to transform the RouteResult of the inner route.
* :ref:`-mapRouteResult-`
* :ref:`-mapRouteResponsePF-`
Directives changing the execution of the inner route
----------------------------------------------------
* :ref:`-mapInnerRoute-`
Directives alphabetically
-------------------------
.. toctree::
:maxdepth: 1
extract
mapInnerRoute
mapRejections
mapRequest
mapRequestContext
mapResponse
mapResponseEntity
mapResponseHeaders
mapRouteResult
mapRouteResultPF
noop
pass
provide
textract
tprovide

View file

@ -1,27 +0,0 @@
.. _-mapResponse-:
mapResponse
===============
Changes the response that was generated by the inner route.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
:snippet: mapResponse
Description
-----------
The ``mapResponse`` directive is used as a building block for :ref:`Custom Directives` to transform a response that
was generated by the inner route. This directive transforms only complete responses. Use :ref:`-mapHttpResponsePart-`,
instead, to transform parts of chunked responses as well.
See :ref:`Response Transforming Directives` for similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0mapResponse

View file

@ -1,27 +0,0 @@
.. _-mapRouteResponsePF-:
mapRouteResponsePF
==================
Changes the message the inner route sends to the responder.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
:snippet: mapRouteResponsePF
Description
-----------
The ``mapRouteResponsePF`` directive is used as a building block for :ref:`Custom Directives` to transform what
the inner route sends to the responder (see :ref:`The Responder Chain`). It's similar to the :ref:`-mapRouteResult-`
directive but allows to specify a partial function that doesn't have to handle all the incoming response messages.
See :ref:`Result Transformation Directives` for similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: mapRouteResultPF

View file

@ -1,46 +0,0 @@
.. _-compressResponse-:
compressResponse
================
Uses the first of a given number of encodings that the client accepts. If none are accepted the request
is rejected with an ``UnacceptedResponseEncodingRejection``.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala
:snippet: compressResponse
Description
-----------
The ``compressResponse`` directive allows to specify zero to three encoders to try in the specified order.
If none are specified the tried list is ``Gzip``, ``Deflate``, and then ``NoEncoding``.
The ``compressResponse()`` directive (without an explicit list of encoders given) will therefore behave as follows:
========================================= ===============================
``Accept-Encoding`` header resulting response
========================================= ===============================
``Accept-Encoding: gzip`` compressed with ``Gzip``
``Accept-Encoding: deflate`` compressed with ``Deflate``
``Accept-Encoding: deflate, gzip`` compressed with ``Gzip``
``Accept-Encoding: identity`` uncompressed
no ``Accept-Encoding`` header present compressed with ``Gzip``
========================================= ===============================
For an overview of the different ``compressResponse`` directives see :ref:`WhenToUseWhichCompressResponseDirective`.
Example
-------
This example shows the behavior of ``compressResponse`` without any encoders specified:
.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala
:snippet: compressResponse-0
This example shows the behaviour of ``compressResponse(Gzip)``:
.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala
:snippet: compressResponse-1

View file

@ -1,37 +0,0 @@
.. _-compressResponseIfRequested-:
compressResponseIfRequested
===========================
Only compresses the response when specifically requested by the ``Accept-Encoding`` request header
(i.e. the default is "no compression").
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala
:snippet: compressResponseIfRequested
Description
-----------
The ``compressResponseIfRequested`` directive is an alias for ``compressResponse(NoEncoding, Gzip, Deflate)`` and will
behave as follows:
========================================= ===============================
``Accept-Encoding`` header resulting response
========================================= ===============================
``Accept-Encoding: gzip`` compressed with ``Gzip``
``Accept-Encoding: deflate`` compressed with ``Deflate``
``Accept-Encoding: deflate, gzip`` compressed with ``Gzip``
``Accept-Encoding: identity`` uncompressed
no ``Accept-Encoding`` header present uncompressed
========================================= ===============================
For an overview of the different ``compressResponse`` directives see :ref:`WhenToUseWhichCompressResponseDirective`.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala
:snippet: compressResponseIfRequested

View file

@ -1,46 +0,0 @@
.. _-decompressRequest-:
decompressRequest
=================
Decompresses the request if it is can be decoded with one of the given decoders. Otherwise,
the request is rejected with an ``UnsupportedRequestEncodingRejection(supportedEncoding)``.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/CodingDirectives.scala
:snippet: decompressRequest
Description
-----------
The ``decompressRequest`` directive allows either to specify a list of decoders or none at all. If
no ``Decoder`` is specified ``Gzip``, ``Deflate``, or ``NoEncoding`` will be tried.
The ``decompressRequest`` directive will behave as follows:
========================================= ===============================
``Content-Encoding`` header resulting request
========================================= ===============================
``Content-Encoding: gzip`` decompressed
``Content-Encoding: deflate`` decompressed
``Content-Encoding: identity`` unchanged
no ``Content-Encoding`` header present unchanged
========================================= ===============================
For an overview of the different ``decompressRequest`` directives and which one to use when,
see :ref:`WhenToUseWhichDecompressRequestDirective`.
Example
-------
This example shows the behavior of ``decompressRequest()`` without any decoders specified:
.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala
:snippet: decompressRequest-0
This example shows the behaviour of ``decompressRequest(Gzip, NoEncoding)``:
.. includecode2:: ../../../code/docs/http/server/directives/CodingDirectivesExamplesSpec.scala
:snippet: decompressRequest-1

View file

@ -1,64 +0,0 @@
.. _CodingDirectives:
CodingDirectives
================
.. toctree::
:maxdepth: 1
compressResponse
compressResponseIfRequested
decodeRequest
decompressRequest
encodeResponse
requestEncodedWith
responseEncodingAccepted
.. _WhenToUseWhichCompressResponseDirective:
When to use which compression directive?
----------------------------------------
There are three different directives for performing response compressing with slightly different behavior:
:ref:`-encodeResponse-`
Always compresses the response with the one given encoding, rejects the request with an
``UnacceptedResponseEncodingRejection`` if the client doesn't accept the given encoding. The other
compression directives are built upon this one. See its description for an overview how they
relate exactly.
:ref:`-compressResponse-`
Uses the first of a given number of encodings that the client accepts. If none are accepted the request
is rejected.
:ref:`-compressResponseIfRequested-`
Only compresses the response when specifically requested by the
``Accept-Encoding`` request header (i.e. the default is "no compression").
See the individual directives for more detailed usage examples.
.. _WhenToUseWhichDecompressRequestDirective:
When to use which decompression directive?
------------------------------------------
There are two different directives for performing request decompressing with slightly different behavior:
:ref:`-decodeRequest-`
Attempts to decompress the request using **the one given decoder**, rejects the request with an
``UnsupportedRequestEncodingRejection`` if the request is not encoded with the given encoder.
:ref:`-decompressRequest-`
Decompresses the request if it is encoded with **one of the given encoders**.
If the request's encoding doesn't match one of the given encoders it is rejected.
Combining compression and decompression
---------------------------------------
As with all Spray directives, the above single directives can be combined
using ``&`` to produce compound directives that will decompress requests and
compress responses in whatever combination required. Some examples:
.. includecode2:: /../spray-routing-tests/src/test/scala/spray/routing/EncodingDirectivesSpec.scala
:snippet: decompress-compress-combination-example

View file

@ -1,31 +0,0 @@
.. _Exception Handling:
Exception Handling
==================
Exceptions thrown during route execution bubble up through the route structure to the next enclosing
:ref:`-handleExceptions-` directive, ``Route.seal`` or the ``onFailure`` callback of a
future created by ``detach``.
Similarly to the way that :ref:`Rejections` are handled the :ref:`-handleExceptions-` directive delegates the actual job of
converting a list of rejections to its argument, an ExceptionHandler__, which is defined like this::
trait ExceptionHandler extends PartialFunction[Throwable, Route]
__ @github@/akka-http/src/main/scala/akka/http/server/ExceptionHandler.scala
:ref:`runRoute` defined in :ref:`HttpService` does the same but gets its ``ExceptionHandler`` instance
implicitly.
Since an ``ExceptionHandler`` is a partial function it can choose, which exceptions it would like to handle and
which not. Unhandled exceptions will simply continue to bubble up in the route structure. The top-most
``ExceptionHandler`` applied by :ref:`runRoute` will handle *all* exceptions that reach it.
So, if you'd like to customize the way certain exceptions are handled simply bring a custom ``ExceptionHandler`` into
implicit scope of :ref:`runRoute` or pass it to an explicit :ref:`-handleExceptions-` directive that you
have put somewhere into your route structure.
Here is an example:
.. includecode2:: ../code/docs/http/server/ExceptionHandlerExamplesSpec.scala
:snippet: example-1

View file

@ -1,6 +0,0 @@
HTTPS
=====
Is not yet supported.
(todo)

View file

@ -1,12 +0,0 @@
.. _http-client-server-scala:
HTTP Client & Server
====================
.. toctree::
:maxdepth: 2
model
client
server
https

View file

@ -1,19 +0,0 @@
.. _http-routing-index-scala:
HTTP Routing
============
.. toctree::
:maxdepth: 2
quick-start
routing
directives
rejections
exception-handling
path-matchers
marshalling
custom-directives
directives/alphabetically
directives/by-trait

View file

@ -1,8 +0,0 @@
.. _http-testkit-scala:
HTTP TestKit
============
.. note::
This part of the documentation has not yet been written, please stay tuned for updates.

View file

@ -3,26 +3,13 @@
Akka HTTP
=========
The purpose of the Akka HTTP layer is to expose Actors to the web via HTTP and
to enable them to consume HTTP services as a client. It is not an HTTP
framework, it is an Actor-based toolkit for interacting with web services and
clients. This toolkit is structured into several layers:
* ``akka-http-core``: A complete implementation of the HTTP standard, both as
client and server.
* ``akka-http``: A convenient and powerful routing DSL for expressing web
services.
* ``akka-http-testkit``: A test harness and set of utilities for verifying
your web service implementations.
Additionally there are several integration modules with bindings for different
serialization and deserialization schemes for HTTP entities.
.. toctree::
:maxdepth: 2
index-client-server
index-routing
index-testkit
introduction
configuration
client-side/index
low-level-server-side-api
routing-dsl/index
common/index
migration-from-spray

View file

@ -0,0 +1,32 @@
Introduction
============
The Akka HTTP modules implement a full server- and client-side HTTP stack on top of *akka-actor* and *akka-stream*. It's
not a web-framework but rather a more general toolkit for providing and consuming HTTP-based services. While interaction
with a browser is of course also in scope it is not the primary focus of Akka HTTP.
Akka HTTP follows a rather open design and many times offers several different API levels for "doing the same thing".
You get to pick the API level of abstraction that is most suitable for your application.
This means that, if you have trouble achieving something using a high-level API, there's a good chance that you can get
it done with a low-level API, which offers more flexibility but might require you do write more application code.
Akka HTTP is structured into several modules:
akka-http-core
A complete, mostly low-level, server- and client-side implementation of HTTP (incl. WebSockets)
akka-http
Higher-level functionality, like (un)marshalling, (de)compression as well as a powerful DSL
for defining HTTP-based APIs on the server-side
akka-http-testkit
A test harness and set of utilities for verifying server-side service implementations
akka-http-spray-json
Predefined glue-code for (de)serializing custom types from/to JSON with spray-json_
akka-http-xml
Predefined glue-code for (de)serializing custom types from/to XML with scala-xml_
.. _spray-json: https://github.com/spray/spray-json
.. _scala-xml: https://github.com/scala/scala-xml

View file

@ -0,0 +1,135 @@
.. _http-low-level-server-side-api:
Low-Level Server-Side API
=========================
Apart from the :ref:`HTTP Client <http-client-side>` Akka HTTP also provides an embedded,
`Reactive-Streams`_-based, fully asynchronous HTTP/1.1 server implemented on top of :ref:`Akka Stream <streams-scala>`.
It sports the following features:
- Full support for `HTTP persistent connections`_
- Full support for `HTTP pipelining`_
- Full support for asynchronous HTTP streaming including "chunked" transfer encoding accessible through an idiomatic API
- Optional SSL/TLS encryption
.. _HTTP persistent connections: http://en.wikipedia.org/wiki/HTTP_persistent_connection
.. _HTTP pipelining: http://en.wikipedia.org/wiki/HTTP_pipelining
.. _Reactive-Streams: http://www.reactive-streams.org/
The server-side components of Akka HTTP are split into two layers:
1. The basic low-level server implementation in the ``akka-http-core`` module
2. Higher-level functionality in the ``akka-http`` module
The low-level server (1) is scoped with a clear focus on the essential functionality of an HTTP/1.1 server:
- Connection management
- Parsing messages and headers
- Timeout management (for requests and connections)
- Response ordering (for transparent pipelining support)
All non-core features of typical HTTP servers (like request routing, file serving, compression, etc.) are left to
the higher layers, they are not implemented by the ``akka-http-core``-level server itself.
Apart from general focus this design keeps the server core small and light-weight as well as easy to understand and
maintain.
Depending on your needs you can either use the low-level API directly or rely on the high-level
:ref:`Routing DSL <http-high-level-server-side-api>` which can make the definition of more complex service logic much
easier.
Streams and HTTP
----------------
The Akka HTTP server is implemented on top of :ref:`Akka Stream <streams-scala>` and makes heavy use of it - in its
implementation as well as on all levels of its API.
On the connection level Akka HTTP offers basically the same kind of interface as :ref:`Akka Stream IO <stream-io-scala>`:
A socket binding is represented as a stream of incoming connections. The application pulls connections from this stream
source and, for each of them, provides a ``Flow[HttpRequest, HttpResponse, _]`` to "translate" requests into responses.
Apart from regarding a socket bound on the server-side as a ``Source[IncomingConnection]`` and each connection as a
``Source[HttpRequest]`` with a ``Sink[HttpResponse]`` the stream abstraction is also present inside a single HTTP
message: The entities of HTTP requests and responses are generally modeled as a ``Source[ByteString]``. See also
the :ref:`http-model-scala` for more information on how HTTP messages are represented in Akka HTTP.
Starting and Stopping
---------------------
On the most basic level an Akka HTTP server is bound by invoking the ``bind`` method of the `akka.http.scaladsl.Http`_
extension:
.. includecode2:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala
:snippet: binding-example
Arguments to the ``Http.bind`` method specify the interface and port to bind to and register interest in handling
incoming HTTP connections. Additionally, the method also allows for the definition of socket options as well as a larger
number of settings for configuring the server according to your needs.
The result of the ``bind`` method is a ``Source[Http.IncomingConnection]`` which must be drained by the application in
order to accept incoming connections.
The actual binding is not performed before this source is materialized as part of a processing pipeline. In
case the bind fails (e.g. because the port is already busy) the materialized stream will immediately be terminated with
a respective exception.
The binding is released (i.e. the underlying socket unbound) when the subscriber of the incoming
connection source has cancelled its subscription. Alternatively one can use the ``unbind()`` method of the
``Http.ServerBinding`` instance that is created as part of the connection source's materialization process.
The ``Http.ServerBinding`` also provides a way to get a hold of the actual local address of the bound socket, which is
useful for example when binding to port zero (and thus letting the OS pick an available port).
.. _akka.http.scaladsl.Http: @github@/akka-http-core/src/main/scala/akka/http/scaladsl/Http.scala
Request-Response Cycle
----------------------
When a new connection has been accepted it will be published as an ``Http.IncomingConnection`` which consists
of the remote address and methods to provide a ``Flow[HttpRequest, HttpResponse, _]`` to handle requests coming in over
this connection.
Requests are handled by calling one of the ``handleWithXXX`` methods with a handler, which can either be
- a ``Flow[HttpRequest, HttpResponse, _]`` for ``handleWith``,
- a function ``HttpRequest => HttpResponse`` for ``handleWithSyncHandler``,
- a function ``HttpRequest => Future[HttpResponse]`` for ``handleWithAsyncHandler``.
Here is a complete example:
.. includecode2:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala
:snippet: full-server-example
In this example, a request is handled by transforming the request stream with a function ``HttpRequest => HttpResponse``
using ``handleWithSyncHandler`` (or equivalently, Akka Stream's ``map`` operator). Depending on the use case many
other ways of providing a request handler are conceivable using Akka Stream's combinators.
If the application provides a ``Flow`` it is also the responsibility of the application to generate exactly one response
for every request and that the ordering of responses matches the ordering of the associated requests (which is relevant
if HTTP pipelining is enabled where processing of multiple incoming requests may overlap). When relying on
``handleWithSyncHandler`` or ``handleWithAsyncHandler``, or the ``map`` or ``mapAsync`` stream operators, this
requirement will be automatically fulfilled.
Streaming Request/Response Entities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Streaming of HTTP message entities is supported through subclasses of ``HttpEntity``. The application needs to be able
to deal with streamed entities when receiving a request as well as, in many cases, when constructing responses.
See :ref:`HttpEntity` for a description of the alternatives.
If you rely on the :ref:`http-marshalling-scala` and/or :ref:`http-unmarshalling-scala` facilities provided by
Akka HTTP then the conversion of custom types to and from streamed entities can be quite convenient.
Closing a connection
~~~~~~~~~~~~~~~~~~~~
The HTTP connection will be closed when the handling ``Flow`` cancels its upstream subscription or the peer closes the
connection. An often times more convenient alternative is to explicitly add a ``Connection: close`` header to an
``HttpResponse``. This response will then be the last one on the connection and the server will actively close the
connection when it has been sent out.
// TODO: show an example of using the HTTP layer independently with a BidFlow join

View file

@ -1,4 +0,0 @@
Marshalling
===========
(todo)

View file

@ -0,0 +1,4 @@
Migration Guide from spray
==========================
TODO

View file

@ -1,156 +0,0 @@
.. _http-model-scala:
Model
=====
The akka HTTP model contains a mostly immutable, case-class based model of the major HTTP data structures,
like HTTP requests, responses and common headers. It also includes a parser for the latter, which is able to construct
the more structured header models from raw unstructured header name/value pairs.
Overview
--------
Since akka-http-core provides the central HTTP data structures you will find the following import in quite a
few places around the code base (and probably your own code as well):
.. includecode:: ../code/docs/http/ModelSpec.scala
:include: import-model
This brings in scope all of the relevant things that are defined here and that youll want to work with, mainly:
- ``HttpRequest`` and ``HttpResponse``, the central message model
- ``headers``, the package containing all the predefined HTTP header models and supporting types
- Supporting types like ``Uri``, ``HttpMethods``, ``MediaTypes``, ``StatusCodes``, etc.
A common pattern is that the model of a certain entity is represented by an immutable type (class or trait),
while the actual instances of the entity defined by the HTTP spec live in an accompanying object carrying the name of
the type plus a trailing plural 's'.
For example:
- Defined HttpMethod instances live in the HttpMethods object.
- Defined HttpCharset instances live in the HttpCharsets object.
- Defined HttpEncoding instances live in the HttpEncodings object.
- Defined HttpProtocol instances live in the HttpProtocols object.
- Defined MediaType instances live in the MediaTypes object.
- Defined StatusCode instances live in the StatusCodes object.
HttpRequest / HttpResponse
--------------------------
``HttpRequest`` and ``HttpResponse`` are the basic case classes representing HTTP messages.
An ``HttpRequest`` consists of
- method (GET, POST, etc.)
- URI
- protocol
- headers
- entity (body data)
Here are some examples how to construct an ``HttpRequest``:
.. includecode:: ../code/docs/http/ModelSpec.scala
:include: construct-request
All parameters of ``HttpRequest`` have default values set, so e.g. ``headers`` don't need to be specified
if there are none. Many of the parameters types (like ``HttpEntity`` and ``Uri``) define implicit conversions
for common use cases to simplify the creation of request and response instances.
An ``HttpResponse`` consists of
- status code
- protocol
- headers
- entity
Here are some examples how to construct an ``HttpResponse``:
.. includecode:: ../code/docs/http/ModelSpec.scala
:include: construct-response
Aside from the simple ``HttpEntity`` constructors to create an entity from a fixed ``ByteString`` shown here,
subclasses of ``HttpEntity`` allow data to be specified as a stream of data which is explained in the next section.
.. _HttpEntity:
HttpEntity
----------
An ``HttpEntity`` carries the content of a request together with its content-type which is needed to interpret the raw
byte data.
Akka HTTP provides several kinds of entities to support static and streaming data for the different kinds of ways
to transport streaming data with HTTP. There are four subtypes of HttpEntity:
HttpEntity.Strict
An entity which wraps a static ``ByteString``. It represents a standard, non-chunked HTTP message with ``Content-Length``
set.
HttpEntity.Default
A streaming entity which needs a predefined length and a ``Producer[ByteString]`` to produce the body data of
the message. It represents a standard, non-chunked HTTP message with ``Content-Length`` set. It is an error if the
provided ``Producer[ByteString]`` doesn't produce exactly as many bytes as specified. On the wire, a Strict entity
and a Default entity cannot be distinguished. However, they offer a valuable alternative in the API to distinguish
between strict and streamed data.
HttpEntity.Chunked
A streaming entity of unspecified length that uses `Chunked Transfer Coding`_ for transmitting data. Data is
represented by a ``Producer[ChunkStreamPart]``. A ``ChunkStreamPart`` is either a non-empty ``Chunk`` or a ``LastChunk``
containing optional trailer headers. The stream must consist of 0..n ``Chunked`` parts and can be terminated by an
optional ``LastChunk`` part (which carries optional trailer headers).
HttpEntity.CloseDelimited
A streaming entity of unspecified length that is delimited by closing the connection ("Connection: close"). Note,
that this entity type can only be used in an ``HttpResponse``.
HttpEntity.IndefiniteLength
A streaming entity of unspecified length that can be used as a ``BodyPart`` entity.
Entity types ``Strict``, ``Default``, and ``Chunked`` are a subtype of ``HttpEntity.Regular`` which allows to use them for
requests and responses. In contrast, ``HttpEntity.CloseDelimited`` can only be used for responses.
Streaming entity types (i.e. all but ``Strict``) cannot be shared or serialized. To create a strict, sharable copy of an
entity or message use ``HttpEntity.toStrict`` or ``HttpMessage.toStrict`` which returns a Future of the object with the
body data collected into a ``ByteString``.
.. _Chunked Transfer Coding: http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-26#section-4.1
The ``HttpEntity`` companion object contains several helper constructors to create entities from common types easily.
You can pattern match over the subtypes of ``HttpEntity`` if you want to provide special handling for each of the
subtypes. However, in many cases a recipient of an `HttpEntity` doesn't care about of which subtype an entity is
(and how data is transported exactly on the HTTP layer). Therefore, a general ``HttpEntity.dataBytes`` is provided
which allows access to the data of an entity regardless of its concrete subtype.
.. note::
When to use which subtype?
- Use Strict if the amount of data is small and it is already in the heap (or even available as a ``ByteString``)
- Use Default if the data is generated by a streaming data source and the size of the data is fixed
- Use Chunked to support a data stream of unknown length
- Use CloseDelimited for a response as an alternative to Chunked e.g. if chunked transfer encoding isn't supported
by a client.
- Use IndefiniteLength instead of CloseDelimited in a BodyPart.
Header model
------------
Akka HTTP contains a rich model of the common HTTP headers. Parsing and rendering is done automatically so that
applications don't need to care for the actual syntax of headers. Headers not modelled explicitly are represented
as a ``RawHeader``.
See these examples of how to deal with headers:
.. includecode:: ../code/docs/http/ModelSpec.scala
:include: headers
Parsing / Rendering
-------------------
Parsing and rendering of HTTP data structures is heavily optimized and for most types there's currently no public API
provided to parse (or render to) Strings or byte arrays.

View file

@ -1,4 +0,0 @@
Quick-Start
===========
(todo)

View file

@ -1,100 +0,0 @@
.. _Rejections:
Rejections
==========
In the chapter about constructing :ref:`Routes` the ``~`` operator was introduced, which connects two routes in a way
that allows a second route to get a go at a request if the first route "rejected" it. The concept of "rejections" is
used by Akka HTTP for maintaining a more functional overall architecture and in order to be able to properly
handle all kinds of error scenarios.
When a filtering directive, like the :ref:`-get-` directive, cannot let the request pass through to its inner Route because
the filter condition is not satisfied (e.g. because the incoming request is not a GET request) the directive doesn't
immediately complete the request with an error response. Doing so would make it impossible for other routes chained in
after the failing filter to get a chance to handle the request.
Rather, failing filters "reject" the request in the same way as by explicitly calling ``requestContext.reject(...)``.
After having been rejected by a route the request will continue to flow through the routing structure and possibly find
another route that can complete it. If there are more rejections all of them will be picked up and collected.
If the request cannot be completed by (a branch of) the route structure an enclosing :ref:`-handleRejections-` directive
can be used to convert a set of rejections into an ``HttpResponse`` (which, in most cases, will be an error response).
``Route.seal`` internally wraps its argument route with the :ref:`-handleRejections-` directive in order to "catch"
and handle any rejection.
Predefined Rejections
---------------------
A rejection encapsulates a specific reason why a Route was not able to handle a request. It is modeled as an object of
type ``Rejection``. Akka HTTP comes with a set of `predefined rejections`__, which are used by various
:ref:`predefined directives <Predefined Directives>`.
Rejections are gathered up over the course of a Route evaluation and finally converted to ``HttpResponse`` replies by
the :ref:`-handleRejections-` directive if there was no way for the request to be completed.
__ @github@/akka-http/src/main/scala/akka/http/server/Rejection.scala
.. _RejectionHandler:
RejectionHandler
----------------
The :ref:`-handleRejections-` directive delegates the actual job of converting a list of rejections to its argument, a
RejectionHandler__, which is defined like this::
trait RejectionHandler extends PartialFunction[List[Rejection], Route]
__ @github@/akka-http/src/main/scala/akka/http/server/RejectionHandler.scala
Since a ``RejectionHandler`` is a partial function it can choose, which rejections it would like to handle and
which not. Unhandled rejections will simply continue to flow through the route structure. The top-most
``RejectionHandler`` applied by :ref:`runRoute` will handle *all* rejections that reach it.
So, if you'd like to customize the way certain rejections are handled simply bring a custom ``RejectionHandler`` into
implicit scope of :ref:`runRoute` or pass it to an explicit :ref:`-handleRejections-` directive that you
have put somewhere into your route structure.
Here is an example:
.. includecode2:: ../code/docs/http/server/RejectionHandlerExamplesSpec.scala
:snippet: example-1
Rejection Cancellation
----------------------
As you can see from its definition above the ``RejectionHandler`` handles not single rejections but a whole list of
them. This is because some route structure produce several "reasons" why a request could not be handled.
Take this route structure for example:
.. includecode2:: ../code/docs/http/server/RejectionHandlerExamplesSpec.scala
:snippet: example-2
For uncompressed POST requests this route structure could yield two rejections:
- a ``MethodRejection`` produced by the :ref:`-get-` directive (which rejected because the request is not a GET request)
- an ``UnsupportedRequestEncodingRejection`` produced by the :ref:`-decodeRequest-` directive (which only accepts
gzip-compressed requests)
In reality the route even generates one more rejection, a ``TransformationRejection`` produced by the :ref:`-post-`
directive. It "cancels" all other potentially existing *MethodRejections*, since they are invalid after the
:ref:`-post-` directive allowed the request to pass (after all, the route structure *can* deal with POST requests).
These types of rejection cancellations are resolved *before* a ``RejectionHandler`` sees the rejection list.
So, for the example above the ``RejectionHandler`` will be presented with only a single-element rejection list,
containing nothing but the ``UnsupportedRequestEncodingRejection``.
.. _empty rejections:
Empty Rejections
----------------
Since rejections are passed around in lists you might ask yourself what the semantics of an empty rejection list are.
In fact, empty rejection lists have well defined semantics. They signal that a request was not handled because the
respective resource could not be found. Akka HTTP reserves the special status of "empty rejection" to this most
common failure a service is likely to produce.
So, for example, if the :ref:`-path-` directive rejects a request, it does so with an empty rejection list. The
:ref:`-host-` directive behaves in the same way.

View file

@ -0,0 +1,68 @@
.. _Case Class Extraction:
Case Class Extraction
=====================
The value extraction performed by :ref:`Directives` is a nice way of providing your route logic with interesting request
properties, all with proper type-safety and error handling. However, in some case you might want even more.
Consider this example:
.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala
:snippet: example-1
Here the :ref:`-parameters-` directives is employed to extract three ``Int`` values, which are then used to construct an
instance of the ``Color`` case class. So far so good. However, if the model classes we'd like to work with have more
than just a few parameters the overhead introduced by capturing the arguments as extractions only to feed them into the
model class constructor directly afterwards can somewhat clutter up your route definitions.
If your model classes are case classes, as in our example, Akka HTTP supports an even shorter and more concise
syntax. You can also write the example above like this:
.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala
:snippet: example-2
You can postfix any directive with extractions with an ``as(...)`` call. By simply passing the companion object of your
model case class to the ``as`` modifier method the underlying directive is transformed into an equivalent one, which
extracts only one value of the type of your model class. Note that there is no reflection involved and your case class
does not have to implement any special interfaces. The only requirement is that the directive you attach the ``as``
call to produces the right number of extractions, with the right types and in the right order.
If you'd like to construct a case class instance from extractions produced by *several* directives you can first join
the directives with the ``&`` operator before using the ``as`` call:
.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala
:snippet: example-3
Here the ``Color`` class has gotten another member, ``name``, which is supplied not as a parameter but as a path
element. By joining the ``path`` and ``parameters`` directives with ``&`` you create a directive extracting 4 values,
which directly fit the member list of the ``Color`` case class. Therefore you can use the ``as`` modifier to convert
the directive into one extracting only a single ``Color`` instance.
Generally, when you have routes that work with, say, more than 3 extractions it's a good idea to introduce a case class
for these and resort to case class extraction. Especially since it supports another nice feature: validation.
.. caution:: There is one quirk to look out for when using case class extraction: If you create an explicit companion
object for your case class, no matter whether you actually add any members to it or not, the syntax presented above
will not (quite) work anymore. Instead of ``as(Color)`` you will then have to say ``as(Color.apply)``. This behavior
appears as if it's not really intended, so this might be improved in future Scala versions.
Case Class Validation
---------------------
In many cases your web service needs to verify input parameters according to some logic before actually working with
them. E.g. in the example above the restriction might be that all color component values must be between 0 and 255.
You could get this done with a few :ref:`-validate-` directives but this would quickly become cumbersome and hard to
read.
If you use case class extraction you can put the verification logic into the constructor of your case class, where it
should be:
.. includecode2:: ../../code/docs/http/scaladsl/server/CaseClassExtractionExamplesSpec.scala
:snippet: example-4
If you write your validations like this Akka HTTP's case class extraction logic will properly pick up all error
messages and generate a ``ValidationRejection`` if something goes wrong. By default, ``ValidationRejections`` are
converted into ``400 Bad Request`` error response by the default :ref:`RejectionHandler <The RejectionHandler>`, if no
subsequent route successfully handles the request.

View file

@ -0,0 +1,138 @@
.. _Predefined Directives:
Predefined Directives (alphabetically)
======================================
=========================================== ============================================================================
Directive Description
=========================================== ============================================================================
:ref:`-authenticateBasic-` ...
:ref:`-authenticateBasicAsync-` ...
:ref:`-authenticateBasicPF-` ...
:ref:`-authenticateBasicPFAsync-` ...
:ref:`-authenticateOrRejectWithChallenge-` ...
:ref:`-authorize-` ...
:ref:`-cancelRejection-` ...
:ref:`-cancelRejections-` ...
:ref:`-complete-` ...
:ref:`-completeOrRecoverWith-` ...
:ref:`-completeWith-` ...
:ref:`-conditional-` ...
:ref:`-cookie-` ...
:ref:`-decodeRequest-` ...
:ref:`-decodeRequestWith-` ...
:ref:`-delete-` ...
:ref:`-deleteCookie-` ...
:ref:`-encodeResponse-` ...
:ref:`-encodeResponseWith-` ...
:ref:`-entity-` ...
:ref:`-extract-` ...
:ref:`-extractClientIP-` ...
:ref:`-extractCredentials-` ...
:ref:`-extractExecutionContext-` ...
:ref:`-extractFlowMaterializer-` ...
:ref:`-extractHost-` ...
:ref:`-extractLog-` ...
:ref:`-extractMethod-` ...
:ref:`-extractRequest-` ...
:ref:`-extractRequestContext-` ...
:ref:`-extractScheme-` ...
:ref:`-extractSettings-` ...
:ref:`-extractUnmatchedPath-` ...
:ref:`-extractUri-` ...
:ref:`-failWith-` ...
:ref:`-formField-` ...
:ref:`-formFields-` ...
:ref:`-get-` ...
:ref:`-getFromBrowseableDirectories-` ...
:ref:`-getFromBrowseableDirectory-` ...
:ref:`-getFromDirectory-` ...
:ref:`-getFromFile-` ...
:ref:`-getFromResource-` ...
:ref:`-getFromResourceDirectory-` ...
:ref:`-handleExceptions-` ...
:ref:`-handleRejections-` ...
:ref:`-handleWebsocketMessages-` ...
:ref:`-handleWith-` ...
:ref:`-head-` ...
:ref:`-headerValue-` ...
:ref:`-headerValueByName-` ...
:ref:`-headerValueByType-` ...
:ref:`-headerValuePF-` ...
:ref:`-host-` ...
:ref:`-listDirectoryContents-` ...
:ref:`-logRequest-` ...
:ref:`-logRequestResult-` ...
:ref:`-logResult-` ...
:ref:`-mapInnerRoute-` ...
:ref:`-mapRejections-` ...
:ref:`-mapRequest-` ...
:ref:`-mapRequestContext-` ...
:ref:`-mapResponse-` ...
:ref:`-mapResponseEntity-` ...
:ref:`-mapResponseHeaders-` ...
:ref:`-mapRouteResult-` ...
:ref:`-mapRouteResultFuture-` ...
:ref:`-mapRouteResultPF-` ...
:ref:`-mapRouteResultWith-` ...
:ref:`-mapRouteResultWithPF-` ...
:ref:`-mapSettings-` ...
:ref:`-mapUnmatchedPath-` ...
:ref:`-method-` ...
:ref:`-onComplete-` ...
:ref:`-onSuccess-` ...
:ref:`-optionalCookie-` ...
:ref:`-optionalHeaderValue-` ...
:ref:`-optionalHeaderValueByName-` ...
:ref:`-optionalHeaderValueByType-` ...
:ref:`-optionalHeaderValuePF-` ...
:ref:`-options-` ...
:ref:`-overrideMethodWithParameter-` ...
:ref:`-overrideStatusCode-` ...
:ref:`-parameter-` ...
:ref:`-parameterMap-` ...
:ref:`-parameterMultiMap-` ...
:ref:`-parameters-` ...
:ref:`-parameterSeq-` ...
:ref:`-pass-` ...
:ref:`-patch-` ...
:ref:`-path-` ...
:ref:`-pathEnd-` ...
:ref:`-pathEndOrSingleSlash-` ...
:ref:`-pathPrefix-` ...
:ref:`-pathPrefixTest-` ...
:ref:`-pathSingleSlash-` ...
:ref:`-pathSuffix-` ...
:ref:`-pathSuffixTest-` ...
:ref:`-post-` ...
:ref:`-provide-` ...
:ref:`-put-` ...
:ref:`-rawPathPrefix-` ...
:ref:`-rawPathPrefixTest-` ...
:ref:`-recoverRejections-` ...
:ref:`-recoverRejectionsWith-` ...
:ref:`-redirect-` ...
:ref:`-redirectToNoTrailingSlashIfPresent-` ...
:ref:`-redirectToTrailingSlashIfMissing-` ...
:ref:`-reject-` ...
:ref:`-rejectEmptyResponse-` ...
:ref:`-requestEncodedWith-` ...
:ref:`-requestEntityEmpty-` ...
:ref:`-requestEntityPresent-` ...
:ref:`-respondWithDefaultHeader-` ...
:ref:`-respondWithDefaultHeaders-` ...
:ref:`-respondWithHeader-` ...
:ref:`-respondWithHeaders-` ...
:ref:`-respondWithHeaders-` ...
:ref:`-responseEncodingAccepted-` ...
:ref:`-scheme-` ...
:ref:`-setCookie-` ...
:ref:`-textract-` ...
:ref:`-tprovide-` ...
:ref:`-validate-` ...
:ref:`-withExecutionContext-` ...
:ref:`-withFlowMaterializer-` ...
:ref:`-withLog-` ...
:ref:`-withRangeSupport-` ...
:ref:`-withSettings-` ...
=========================================== ============================================================================

View file

@ -0,0 +1,23 @@
.. _-cancelRejection-:
cancelRejection
===============
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: cancelRejection
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0cancelRejection

View file

@ -0,0 +1,23 @@
.. _-cancelRejections-:
cancelRejections
================
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: cancelRejections
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0cancelRejections

View file

@ -8,7 +8,7 @@ Calculates a value from the request context and provides the value to the inner
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: extract
Description
@ -23,5 +23,5 @@ See :ref:`ProvideDirectives` for an overview of similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0extract

View file

@ -0,0 +1,23 @@
.. _-extractExecutionContext-:
extractExecutionContext
=======================
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: extractExecutionContext
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0extractExecutionContext

View file

@ -0,0 +1,23 @@
.. _-extractFlowMaterializer-:
extractFlowMaterializer
=======================
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: extractFlowMaterializer
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0extractFlowMaterializer

View file

@ -0,0 +1,23 @@
.. _-extractLog-:
extractLog
==========
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: extractLog
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0extractLog

View file

@ -0,0 +1,23 @@
.. _-extractRequest-:
extractRequest
==============
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: extractRequest
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0extractRequest

View file

@ -0,0 +1,23 @@
.. _-extractRequestContext-:
extractRequestContext
=====================
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: extractRequestContext
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0extractRequestContext

View file

@ -0,0 +1,23 @@
.. _-extractSettings-:
extractSettings
===============
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: extractSettings
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0extractSettings

View file

@ -0,0 +1,23 @@
.. _-extractUnmatchedPath-:
extractUnmatchedPath
====================
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: extractUnmatchedPath
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0extractUnmatchedPath

View file

@ -0,0 +1,23 @@
.. _-extractUri-:
extractUri
==========
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: extractUri
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0extractUri

View file

@ -0,0 +1,127 @@
.. _BasicDirectives:
BasicDirectives
===============
Basic directives are building blocks for building :ref:`Custom Directives`. As such they
usually aren't used in a route directly but rather in the definition of new directives.
.. _ProvideDirectives:
Providing Values to Inner Routes
--------------------------------
These directives provide values to the inner routes with extractions. They can be distinguished
on two axes: a) provide a constant value or extract a value from the ``RequestContext`` b) provide
a single value or a tuple of values.
* :ref:`-extract-`
* :ref:`-extractExecutionContext-`
* :ref:`-extractFlowMaterializer-`
* :ref:`-extractLog-`
* :ref:`-extractRequest-`
* :ref:`-extractRequestContext-`
* :ref:`-extractSettings-`
* :ref:`-extractUnmatchedPath-`
* :ref:`-extractUri-`
* :ref:`-textract-`
* :ref:`-provide-`
* :ref:`-tprovide-`
.. _Request Transforming Directives:
Transforming the Request(Context)
---------------------------------
* :ref:`-mapRequest-`
* :ref:`-mapRequestContext-`
* :ref:`-mapSettings-`
* :ref:`-mapUnmatchedPath-`
* :ref:`-withExecutionContext-`
* :ref:`-withFlowMaterializer-`
* :ref:`-withLog-`
* :ref:`-withSettings-`
.. _Response Transforming Directives:
Transforming the Response
-------------------------
These directives allow to hook into the response path and transform the complete response or
the parts of a response or the list of rejections:
* :ref:`-mapResponse-`
* :ref:`-mapResponseEntity-`
* :ref:`-mapResponseHeaders-`
.. _Result Transformation Directives:
Transforming the RouteResult
----------------------------
These directives allow to transform the RouteResult of the inner route.
* :ref:`-cancelRejection-`
* :ref:`-cancelRejections-`
* :ref:`-mapRejections-`
* :ref:`-mapRouteResult-`
* :ref:`-mapRouteResultFuture-`
* :ref:`-mapRouteResultPF-`
* :ref:`-mapRouteResultWith-`
* :ref:`-mapRouteResultWithPF-`
* :ref:`-recoverRejections-`
* :ref:`-recoverRejectionsWith-`
Other
-----
* :ref:`-mapInnerRoute-`
* :ref:`-pass-`
Alphabetically
--------------
.. toctree::
:maxdepth: 1
cancelRejection
cancelRejections
extract
extractExecutionContext
extractFlowMaterializer
extractLog
extractRequest
extractRequestContext
extractSettings
extractUnmatchedPath
extractUri
mapInnerRoute
mapRejections
mapRequest
mapRequestContext
mapResponse
mapResponseEntity
mapResponseHeaders
mapRouteResult
mapRouteResultFuture
mapRouteResultPF
mapRouteResultWith
mapRouteResultWithPF
mapSettings
mapUnmatchedPath
pass
provide
recoverRejections
recoverRejectionsWith
textract
tprovide
withExecutionContext
withFlowMaterializer
withLog
withSettings

View file

@ -8,7 +8,7 @@ Changes the execution model of the inner route by wrapping it with arbitrary log
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: mapInnerRoute
Description
@ -20,5 +20,5 @@ with any other route. Usually, the returned route wraps the original one with cu
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: mapInnerRoute

View file

@ -8,7 +8,7 @@ Transforms the list of rejections the inner route produced.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: mapRejections
Description
@ -22,5 +22,5 @@ See :ref:`Response Transforming Directives` for similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: mapRejections

View file

@ -8,7 +8,7 @@ Transforms the request before it is handled by the inner route.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: mapRequest
Description
@ -24,5 +24,5 @@ See :ref:`Request Transforming Directives` for an overview of similar directives
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0mapRequest

View file

@ -8,7 +8,7 @@ Transforms the ``RequestContext`` before it is passed to the inner route.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: mapRequestContext
Description
@ -23,5 +23,5 @@ See :ref:`Request Transforming Directives` for an overview of similar directives
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: mapRequestContext

View file

@ -0,0 +1,27 @@
.. _-mapResponse-:
mapResponse
===========
Changes the response that was generated by the inner route.
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: mapResponse
Description
-----------
The ``mapResponse`` directive is used as a building block for :ref:`Custom Directives` to transform a response that
was generated by the inner route. This directive transforms complete responses.
See also :ref:`-mapResponseHeaders-` or :ref:`-mapResponseEntity-` for more specialized variants and
:ref:`Response Transforming Directives` for similar directives.
Example
-------
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0mapResponse

View file

@ -1,14 +1,14 @@
.. _-mapResponseEntity-:
mapResponseEntity
=====================
=================
Changes the response entity that was generated by the inner route.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: mapResponseEntity
Description
@ -22,5 +22,5 @@ See :ref:`Response Transforming Directives` for similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: mapResponseEntity

View file

@ -1,14 +1,14 @@
.. _-mapResponseHeaders-:
mapResponseHeaders
======================
==================
Changes the list of response headers that was generated by the inner route.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: mapResponseHeaders
Description
@ -22,5 +22,5 @@ See :ref:`Response Transforming Directives` for similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: mapResponseHeaders

View file

@ -1,26 +1,26 @@
.. _-mapRouteResult-:
mapRouteResult
================
==============
Changes the message the inner route sends to the responder.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: mapRouteResult
Description
-----------
The ``mapRouteResult`` directive is used as a building block for :ref:`Custom Directives` to transform what
the inner route sends to the responder (see :ref:`The Responder Chain`).
The ``mapRouteResult`` directive is used as a building block for :ref:`Custom Directives` to transform the
:ref:`RouteResult` coming back from the inner route.
See :ref:`Result Transformation Directives` for similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0mapRouteResult

View file

@ -0,0 +1,23 @@
.. _-mapRouteResultFuture-:
mapRouteResultFuture
====================
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: mapRouteResultFuture
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0mapRouteResultFuture

View file

@ -0,0 +1,27 @@
.. _-mapRouteResultPF-:
mapRouteResultPF
================
Changes the message the inner route sends to the responder.
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: mapRouteResultPF
Description
-----------
The ``mapRouteResult`` directive is used as a building block for :ref:`Custom Directives` to transform the
:ref:`RouteResult` coming back from the inner route. It's similar to the :ref:`-mapRouteResult-` directive but allows to
specify a partial function that doesn't have to handle all potential ``RouteResult`` instances.
See :ref:`Result Transformation Directives` for similar directives.
Example
-------
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: mapRouteResultPF

View file

@ -0,0 +1,23 @@
.. _-mapRouteResultWith-:
mapRouteResultWith
==================
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: mapRouteResultWith
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0mapRouteResultWith

View file

@ -0,0 +1,23 @@
.. _-mapRouteResultWithPF-:
mapRouteResultWithPF
====================
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: mapRouteResultWithPF
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0mapRouteResultWithPF

View file

@ -0,0 +1,23 @@
.. _-mapSettings-:
mapSettings
===========
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: mapSettings
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0mapSettings

View file

@ -0,0 +1,23 @@
.. _-mapUnmatchedPath-:
mapUnmatchedPath
================
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: mapUnmatchedPath
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0mapUnmatchedPath

View file

@ -8,7 +8,7 @@ A directive that passes the request unchanged to its inner route.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: pass
Description
@ -20,6 +20,6 @@ The directive is usually used as a "neutral element" when combining directives g
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: pass

View file

@ -8,7 +8,7 @@ Provides a constant value to the inner route.
Signature
---------
.. includecode2:: /../../akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: provide
Description
@ -22,5 +22,5 @@ See :ref:`ProvideDirectives` for an overview of similar directives.
Example
-------
.. includecode2:: ../../../code/docs/http/server/directives/BasicDirectivesExamplesSpec.scala
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0provide

View file

@ -0,0 +1,23 @@
.. _-recoverRejections-:
recoverRejections
=================
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: recoverRejections
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0recoverRejections

View file

@ -0,0 +1,23 @@
.. _-recoverRejectionsWith-:
recoverRejectionsWith
=====================
...
Signature
---------
.. includecode2:: /../../akka-http-scala/src/main/scala/akka/http/scaladsl/server/directives/BasicDirectives.scala
:snippet: recoverRejectionsWith
Description
-----------
...
Example
-------
... includecode2:: ../../../../code/docs/http/scaladsl/server/directives/BasicDirectivesExamplesSpec.scala
:snippet: 0recoverRejectionsWith

Some files were not shown because too many files have changed in this diff Show more