2015-05-11 23:05:18 +02:00
|
|
|
/*
|
2016-02-23 12:58:39 +01:00
|
|
|
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
|
2015-05-11 23:05:18 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
package docs.http.scaladsl
|
2015-09-29 23:08:11 +02:00
|
|
|
|
|
|
|
|
import akka.event.LoggingAdapter
|
|
|
|
|
import akka.http.scaladsl.Http.ServerBinding
|
|
|
|
|
import akka.http.scaladsl.model._
|
|
|
|
|
import akka.stream.ActorMaterializer
|
|
|
|
|
import akka.stream.scaladsl.{ Flow, Sink }
|
2015-09-30 14:06:23 +02:00
|
|
|
import akka.testkit.TestActors
|
2016-02-26 18:38:24 +01:00
|
|
|
import docs.CompileOnlySpec
|
2015-09-29 23:08:11 +02:00
|
|
|
import org.scalatest.{ Matchers, WordSpec }
|
2015-09-30 14:06:23 +02:00
|
|
|
import scala.language.postfixOps
|
2015-09-29 23:08:11 +02:00
|
|
|
|
|
|
|
|
import scala.concurrent.{ ExecutionContext, Future }
|
2015-05-11 23:05:18 +02:00
|
|
|
|
2016-02-26 18:38:24 +01:00
|
|
|
class HttpServerExampleSpec extends WordSpec with Matchers
|
|
|
|
|
with CompileOnlySpec {
|
2015-05-11 23:05:18 +02:00
|
|
|
|
2015-09-29 23:08:11 +02:00
|
|
|
// never actually called
|
|
|
|
|
val log: LoggingAdapter = null
|
|
|
|
|
|
2015-09-30 14:06:23 +02:00
|
|
|
"binding-example" in compileOnlySpec {
|
2016-04-12 11:14:48 +02:00
|
|
|
import akka.actor.ActorSystem
|
2015-09-29 23:08:11 +02:00
|
|
|
import akka.http.scaladsl.Http
|
2015-06-23 18:28:53 +02:00
|
|
|
import akka.stream.ActorMaterializer
|
2015-05-11 23:05:18 +02:00
|
|
|
import akka.stream.scaladsl._
|
|
|
|
|
|
|
|
|
|
implicit val system = ActorSystem()
|
2015-06-23 18:28:53 +02:00
|
|
|
implicit val materializer = ActorMaterializer()
|
2016-02-22 11:45:48 +01:00
|
|
|
implicit val executionContext = system.dispatcher
|
2015-05-11 23:05:18 +02:00
|
|
|
|
|
|
|
|
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()
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-30 14:06:23 +02:00
|
|
|
"binding-failure-high-level-example" in compileOnlySpec {
|
2016-04-12 11:14:48 +02:00
|
|
|
import akka.actor.ActorSystem
|
2015-09-29 23:08:11 +02:00
|
|
|
import akka.http.scaladsl.Http
|
|
|
|
|
import akka.http.scaladsl.server.Directives._
|
2015-06-23 18:28:53 +02:00
|
|
|
import akka.stream.ActorMaterializer
|
2015-09-29 23:08:11 +02:00
|
|
|
|
|
|
|
|
implicit val system = ActorSystem()
|
|
|
|
|
implicit val materializer = ActorMaterializer()
|
2016-02-22 11:45:48 +01:00
|
|
|
// needed for the future onFailure in the end
|
|
|
|
|
implicit val executionContext = system.dispatcher
|
2015-09-29 23:08:11 +02:00
|
|
|
|
|
|
|
|
val handler = get {
|
|
|
|
|
complete("Hello world!")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// let's say the OS won't allow us to bind to 80.
|
|
|
|
|
val (host, port) = ("localhost", 80)
|
|
|
|
|
val bindingFuture: Future[ServerBinding] =
|
|
|
|
|
Http().bindAndHandle(handler, host, port)
|
|
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
bindingFuture.onFailure {
|
2015-09-29 23:08:11 +02:00
|
|
|
case ex: Exception =>
|
|
|
|
|
log.error(ex, "Failed to bind to {}:{}!", host, port)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// mock values:
|
2016-04-12 11:14:48 +02:00
|
|
|
import akka.http.scaladsl.Http
|
|
|
|
|
import akka.actor.ActorSystem
|
2015-09-29 23:08:11 +02:00
|
|
|
val handleConnections: Sink[Http.IncomingConnection, Future[Http.ServerBinding]] =
|
|
|
|
|
Sink.ignore.mapMaterializedValue(_ => Future.failed(new Exception("")))
|
|
|
|
|
|
2015-09-30 14:06:23 +02:00
|
|
|
"binding-failure-handling" in compileOnlySpec {
|
2015-09-29 23:08:11 +02:00
|
|
|
implicit val system = ActorSystem()
|
|
|
|
|
implicit val materializer = ActorMaterializer()
|
2016-02-22 11:45:48 +01:00
|
|
|
// needed for the future onFailure in the end
|
|
|
|
|
implicit val executionContext = system.dispatcher
|
2015-09-29 23:08:11 +02:00
|
|
|
|
|
|
|
|
// let's say the OS won't allow us to bind to 80.
|
|
|
|
|
val (host, port) = ("localhost", 80)
|
|
|
|
|
val serverSource = Http().bind(host, port)
|
|
|
|
|
|
|
|
|
|
val bindingFuture: Future[ServerBinding] = serverSource
|
|
|
|
|
.to(handleConnections) // Sink[Http.IncomingConnection, _]
|
|
|
|
|
.run()
|
|
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
bindingFuture.onFailure {
|
2015-09-29 23:08:11 +02:00
|
|
|
case ex: Exception =>
|
|
|
|
|
log.error(ex, "Failed to bind to {}:{}!", host, port)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-30 14:06:23 +02:00
|
|
|
object MyExampleMonitoringActor {
|
|
|
|
|
def props = TestActors.echoActorProps
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"incoming-connections-source-failure-handling" in compileOnlySpec {
|
2016-04-12 11:14:48 +02:00
|
|
|
import akka.actor.ActorSystem
|
|
|
|
|
import akka.actor.ActorRef
|
|
|
|
|
import akka.http.scaladsl.Http
|
|
|
|
|
import akka.http.scaladsl.model.HttpEntity
|
|
|
|
|
import akka.http.scaladsl.model.ContentTypes
|
|
|
|
|
import akka.http.scaladsl.server.Directives._
|
|
|
|
|
import akka.stream.ActorMaterializer
|
|
|
|
|
import scala.io.StdIn
|
|
|
|
|
|
2015-09-29 23:08:11 +02:00
|
|
|
implicit val system = ActorSystem()
|
|
|
|
|
implicit val materializer = ActorMaterializer()
|
2016-02-22 11:45:48 +01:00
|
|
|
implicit val executionContext = system.dispatcher
|
2015-09-29 23:08:11 +02:00
|
|
|
|
|
|
|
|
import Http._
|
|
|
|
|
val (host, port) = ("localhost", 8080)
|
|
|
|
|
val serverSource = Http().bind(host, port)
|
|
|
|
|
|
2015-09-30 14:06:23 +02:00
|
|
|
val failureMonitor: ActorRef = system.actorOf(MyExampleMonitoringActor.props)
|
2015-09-29 23:08:11 +02:00
|
|
|
|
|
|
|
|
val reactToTopLevelFailures = Flow[IncomingConnection]
|
2016-02-11 16:39:25 +01:00
|
|
|
.watchTermination()((_, termination) => termination.onFailure {
|
|
|
|
|
case cause => failureMonitor ! cause
|
|
|
|
|
})
|
2015-09-29 23:08:11 +02:00
|
|
|
|
|
|
|
|
serverSource
|
|
|
|
|
.via(reactToTopLevelFailures)
|
|
|
|
|
.to(handleConnections) // Sink[Http.IncomingConnection, _]
|
|
|
|
|
.run()
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-30 14:06:23 +02:00
|
|
|
"connection-stream-failure-handling" in compileOnlySpec {
|
2016-04-12 11:14:48 +02:00
|
|
|
import akka.actor.ActorSystem
|
|
|
|
|
import akka.http.scaladsl.Http
|
2016-04-12 20:24:08 +08:00
|
|
|
import akka.http.scaladsl.model.{ ContentTypes, HttpEntity }
|
2016-04-12 11:14:48 +02:00
|
|
|
import akka.stream.ActorMaterializer
|
|
|
|
|
|
2015-09-29 23:08:11 +02:00
|
|
|
implicit val system = ActorSystem()
|
|
|
|
|
implicit val materializer = ActorMaterializer()
|
2016-02-22 11:45:48 +01:00
|
|
|
implicit val executionContext = system.dispatcher
|
2015-09-29 23:08:11 +02:00
|
|
|
|
|
|
|
|
val (host, port) = ("localhost", 8080)
|
|
|
|
|
val serverSource = Http().bind(host, port)
|
|
|
|
|
|
|
|
|
|
val reactToConnectionFailure = Flow[HttpRequest]
|
2016-02-11 16:39:25 +01:00
|
|
|
.recover[HttpRequest] {
|
|
|
|
|
case ex =>
|
|
|
|
|
// handle the failure somehow
|
|
|
|
|
throw ex
|
2015-09-29 23:08:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val httpEcho = Flow[HttpRequest]
|
|
|
|
|
.via(reactToConnectionFailure)
|
|
|
|
|
.map { request =>
|
|
|
|
|
// simple text "echo" response:
|
2015-12-01 23:07:56 +01:00
|
|
|
HttpResponse(entity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, request.entity.dataBytes))
|
2015-09-29 23:08:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
serverSource
|
|
|
|
|
.runForeach { con =>
|
|
|
|
|
con.handleWith(httpEcho)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-30 14:06:23 +02:00
|
|
|
"full-server-example" in compileOnlySpec {
|
2016-04-12 11:14:48 +02:00
|
|
|
import akka.actor.ActorSystem
|
2015-05-11 23:05:18 +02:00
|
|
|
import akka.http.scaladsl.Http
|
|
|
|
|
import akka.http.scaladsl.model.HttpMethods._
|
|
|
|
|
import akka.http.scaladsl.model._
|
2015-09-29 23:08:11 +02:00
|
|
|
import akka.stream.ActorMaterializer
|
|
|
|
|
import akka.stream.scaladsl.Sink
|
2015-05-11 23:05:18 +02:00
|
|
|
|
|
|
|
|
implicit val system = ActorSystem()
|
2015-06-23 18:28:53 +02:00
|
|
|
implicit val materializer = ActorMaterializer()
|
2016-02-22 11:45:48 +01:00
|
|
|
implicit val executionContext = system.dispatcher
|
2015-05-11 23:05:18 +02:00
|
|
|
|
|
|
|
|
val serverSource = Http().bind(interface = "localhost", port = 8080)
|
|
|
|
|
|
|
|
|
|
val requestHandler: HttpRequest => HttpResponse = {
|
|
|
|
|
case HttpRequest(GET, Uri.Path("/"), _, _, _) =>
|
2016-06-02 14:06:57 +02:00
|
|
|
HttpResponse(entity = HttpEntity(
|
|
|
|
|
ContentTypes.`text/html(UTF-8)`,
|
2015-05-11 23:05:18 +02:00
|
|
|
"<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()
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-30 14:06:23 +02:00
|
|
|
"low-level-server-example" in compileOnlySpec {
|
2015-05-11 23:05:18 +02:00
|
|
|
import akka.http.scaladsl.Http
|
|
|
|
|
import akka.http.scaladsl.model.HttpMethods._
|
|
|
|
|
import akka.http.scaladsl.model._
|
2015-09-29 23:08:11 +02:00
|
|
|
import akka.stream.ActorMaterializer
|
2016-02-22 11:45:48 +01:00
|
|
|
import scala.io.StdIn
|
2015-05-11 23:05:18 +02:00
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
object WebServer {
|
2015-05-11 23:05:18 +02:00
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
def main(args: Array[String]) {
|
|
|
|
|
implicit val system = ActorSystem()
|
|
|
|
|
implicit val materializer = ActorMaterializer()
|
|
|
|
|
// needed for the future map/flatmap in the end
|
|
|
|
|
implicit val executionContext = system.dispatcher
|
2015-05-11 23:05:18 +02:00
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
val requestHandler: HttpRequest => HttpResponse = {
|
|
|
|
|
case HttpRequest(GET, Uri.Path("/"), _, _, _) =>
|
2016-06-02 14:06:57 +02:00
|
|
|
HttpResponse(entity = HttpEntity(
|
|
|
|
|
ContentTypes.`text/html(UTF-8)`,
|
2016-02-22 11:45:48 +01:00
|
|
|
"<html><body>Hello world!</body></html>"))
|
2015-05-11 23:05:18 +02:00
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
case HttpRequest(GET, Uri.Path("/ping"), _, _, _) =>
|
|
|
|
|
HttpResponse(entity = "PONG!")
|
2015-05-11 23:05:18 +02:00
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
case HttpRequest(GET, Uri.Path("/crash"), _, _, _) =>
|
|
|
|
|
sys.error("BOOM!")
|
|
|
|
|
|
|
|
|
|
case _: HttpRequest =>
|
|
|
|
|
HttpResponse(404, entity = "Unknown resource!")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val bindingFuture = Http().bindAndHandleSync(requestHandler, "localhost", 8080)
|
|
|
|
|
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
|
|
|
|
|
StdIn.readLine() // let it run until user presses return
|
|
|
|
|
bindingFuture
|
|
|
|
|
.flatMap(_.unbind()) // trigger unbinding from the port
|
2016-06-02 14:06:57 +02:00
|
|
|
.onComplete(_ => system.terminate()) // and shutdown when done
|
2015-05-11 23:05:18 +02:00
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
2015-05-11 23:05:18 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// format: OFF
|
|
|
|
|
|
2015-09-30 14:06:23 +02:00
|
|
|
"high-level-server-example" in compileOnlySpec {
|
2016-04-12 11:14:48 +02:00
|
|
|
import akka.actor.ActorSystem
|
2015-05-11 23:05:18 +02:00
|
|
|
import akka.http.scaladsl.Http
|
2016-04-12 11:14:48 +02:00
|
|
|
import akka.http.scaladsl.model.{ContentTypes, HttpEntity}
|
2015-09-29 23:08:11 +02:00
|
|
|
import akka.http.scaladsl.server.Directives._
|
|
|
|
|
import akka.stream.ActorMaterializer
|
2016-02-22 11:45:48 +01:00
|
|
|
import scala.io.StdIn
|
2015-05-11 23:05:18 +02:00
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
object WebServer {
|
|
|
|
|
def main(args: Array[String]) {
|
|
|
|
|
implicit val system = ActorSystem()
|
|
|
|
|
implicit val materializer = ActorMaterializer()
|
|
|
|
|
// needed for the future flatMap/onComplete in the end
|
|
|
|
|
implicit val executionContext = system.dispatcher
|
2015-05-11 23:05:18 +02:00
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
val route =
|
|
|
|
|
get {
|
|
|
|
|
pathSingleSlash {
|
|
|
|
|
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`,"<html><body>Hello world!</body></html>"))
|
|
|
|
|
} ~
|
|
|
|
|
path("ping") {
|
|
|
|
|
complete("PONG!")
|
|
|
|
|
} ~
|
|
|
|
|
path("crash") {
|
|
|
|
|
sys.error("BOOM!")
|
|
|
|
|
}
|
2015-05-11 23:05:18 +02:00
|
|
|
}
|
|
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
// `route` will be implicitly converted to `Flow` using `RouteResult.route2HandlerFlow`
|
|
|
|
|
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
|
|
|
|
|
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
|
|
|
|
|
StdIn.readLine() // let it run until user presses return
|
|
|
|
|
bindingFuture
|
|
|
|
|
.flatMap(_.unbind()) // trigger unbinding from the port
|
2016-06-02 14:06:57 +02:00
|
|
|
.onComplete(_ => system.terminate()) // and shutdown when done
|
2016-02-22 11:45:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
2015-05-11 23:05:18 +02:00
|
|
|
}
|
|
|
|
|
|
2015-09-30 14:06:23 +02:00
|
|
|
"minimal-routing-example" in compileOnlySpec {
|
2015-05-11 23:05:18 +02:00
|
|
|
import akka.http.scaladsl.Http
|
2015-09-29 23:08:11 +02:00
|
|
|
import akka.http.scaladsl.server.Directives._
|
|
|
|
|
import akka.stream.ActorMaterializer
|
2016-02-22 11:45:48 +01:00
|
|
|
import scala.io.StdIn
|
2015-05-11 23:05:18 +02:00
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
object WebServer {
|
|
|
|
|
def main(args: Array[String]) {
|
2015-05-11 23:05:18 +02:00
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
implicit val system = ActorSystem("my-system")
|
|
|
|
|
implicit val materializer = ActorMaterializer()
|
|
|
|
|
// needed for the future flatMap/onComplete in the end
|
|
|
|
|
implicit val executionContext = system.dispatcher
|
|
|
|
|
|
|
|
|
|
val route =
|
|
|
|
|
path("hello") {
|
|
|
|
|
get {
|
|
|
|
|
complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, "<h1>Say hello to akka-http</h1>"))
|
2015-05-11 23:05:18 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
|
2015-05-11 23:05:18 +02:00
|
|
|
|
2016-02-22 11:45:48 +01:00
|
|
|
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
|
|
|
|
|
StdIn.readLine() // let it run until user presses return
|
|
|
|
|
bindingFuture
|
|
|
|
|
.flatMap(_.unbind()) // trigger unbinding from the port
|
2016-06-02 14:06:57 +02:00
|
|
|
.onComplete(_ => system.terminate()) // and shutdown when done
|
2016-02-22 11:45:48 +01:00
|
|
|
}
|
2015-05-11 23:05:18 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-09-30 14:06:23 +02:00
|
|
|
"long-routing-example" in compileOnlySpec {
|
|
|
|
|
//#long-routing-example
|
2015-05-11 23:05:18 +02:00
|
|
|
import akka.actor.ActorRef
|
2015-09-29 23:08:11 +02:00
|
|
|
import akka.http.scaladsl.coding.Deflate
|
2015-05-11 23:05:18 +02:00
|
|
|
import akka.http.scaladsl.marshalling.ToResponseMarshaller
|
|
|
|
|
import akka.http.scaladsl.model.StatusCodes.MovedPermanently
|
|
|
|
|
import akka.http.scaladsl.server.Directives._
|
2015-09-29 23:08:11 +02:00
|
|
|
import akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller
|
|
|
|
|
import akka.pattern.ask
|
|
|
|
|
import akka.util.Timeout
|
2015-05-11 23:05:18 +02:00
|
|
|
|
|
|
|
|
// 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)
|
2015-09-30 14:06:23 +02:00
|
|
|
|
|
|
|
|
// marshalling would usually be derived automatically using libraries
|
2015-05-11 23:05:18 +02:00
|
|
|
implicit val orderUM: FromRequestUnmarshaller[Order] = ???
|
2015-09-30 14:06:23 +02:00
|
|
|
implicit val orderM: ToResponseMarshaller[Order] = ???
|
|
|
|
|
implicit val orderSeqM: ToResponseMarshaller[Seq[Order]] = ???
|
2015-05-11 23:05:18 +02:00
|
|
|
implicit val timeout: Timeout = ??? // for actor asks
|
2015-09-29 23:08:11 +02:00
|
|
|
implicit val ec: ExecutionContext = ???
|
|
|
|
|
implicit val mat: ActorMaterializer = ???
|
|
|
|
|
implicit val sys: ActorSystem = ???
|
2015-05-11 23:05:18 +02:00
|
|
|
|
2015-09-30 14:06:23 +02:00
|
|
|
// backend entry points
|
|
|
|
|
def myAuthenticator: Authenticator[User] = ???
|
|
|
|
|
def retrieveOrdersFromDB: Seq[Order] = ???
|
|
|
|
|
def myDbActor: ActorRef = ???
|
|
|
|
|
def processOrderRequest(id: Int, complete: Order => Unit): Unit = ???
|
|
|
|
|
|
2015-05-11 23:05:18 +02:00
|
|
|
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
|
2015-10-31 14:46:10 +01:00
|
|
|
formFields(('email, 'total.as[Money])).as(Order) { order =>
|
2015-05-11 23:05:18 +02:00
|
|
|
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
|
2015-10-31 14:46:10 +01:00
|
|
|
parameters(('size.as[Int], 'color ?, 'dangerous ? "no"))
|
2015-05-11 23:05:18 +02:00
|
|
|
.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")
|
|
|
|
|
}
|
|
|
|
|
} ~
|
!htp #18919 #19519 New JavaDSL for Akka HTTP (#20518)
* !htt #18919 #19519 Align Java HTTP server DSL with Scala
This commits replaces the Java HTTP server DSL with a Java-8 centric one
which exposes all scala DSL concepts to be usable from Java, including
custom directives, (un)marshallers, rejections, headers, and type safety
for path and query parameters.
* Add RequestContext and RouteResult to Java DSL
fix websockets
WIP bring java docs up to date.
This applies some updates to the root-level documentation
* [htp] Fix java documentation to correctly mention timeouts
Timeouts are configured the same in Java and Scala. Hence, linking to the
scala docs for timeouts from Java.
* =htc fix optionalHeaderValueByType in Java
* =htt #20200 fix java testkit always using NoLogging instead logger
* +htt actually run new javadsl tests, allow overriding config
* =htt improve javadsl test infra with more details when fails
* =htt fix bug in wrong path matcher exposed
* +htp add missing remaining path matcher
* =htp Java DSL cookie tests fixed
* =htt Java DSL ParameterDirectivesTest fixed
Protect the tweets from scalariform
Incorrect response expectations in cache condition directives spec fixed
* =htt Path directives for Java DSL
* +!htt PathMatchers rewritten, made uniform and tests passing
* Bugfix in java reject and a little test-boyscouting
* Revert "Incorrect response expectations in cache condition directives spec fixed"
This reverts commit cd50e89d45db010309f8249b090ea654ebb11c7a.
* +htc HttpAPIsTest is compile time only, not for running
Also, moved from the client package since not strictly a client test.
SecurityDirectives passing
Two faulty tests and two actual bugs.
Fix for cache condition spec not working
* Not sending in Unit instad of the implicit magnet in the test
* HeaderMagnet now works as expected
* Java API added for - and + on DateTime
PetStore example and test fixed
* Annotations to make marshalling work without default constructor
* Made model class immutable
Incorrect tests fixed
Some scaladoc boyscouting as bonus
* =htt RequestValTest sprinkled out across multiple directive tests
Client ip extraction test with incorrect header name fixed.
* =htt Incorrect CodingDirectivesTest fixed.
* =htt Bugfix for Java Unmarshaller.firstOf and fixes to JavaRouteTest
* =htt MarshallerTest fixed
* Missing seal signature added to JavaDSL
* More consistent (with Scala) test kit setup for Java
* missing Javadocs added
* Thread.sleep in default exception handler removed
* =htt copy directive docs, prepare for finishing it up
* +htt SecurityDirectives.authorize variants and test coverage added
* +htt Custom headers in Java DSL
* =htt WIP on java docs
* +htp add missing parameterOrDefault directive
Fixed a lot of doc warnings
* =htc intense progress on javadsl docs
* =htc #20470 Link to issue about docs and fix compile error
compile, migration guide
don't mima check http-experimental
* =htt Java DSL doc warnings fixed.
Only `Could not lex literal_block` ones left now
* =htc fix mima settings
* =doc fix MethodDirectives doc test with custom method
* =htc fix coding directives spec after bad merge
* =htc fix concat being corresponding to route() in javadsl
* =htt Disable consistency check for route/concat as it fails only on ci server
* !htt Minor fixes to PathMatchers
2016-05-16 10:38:40 +02:00
|
|
|
path("oldApi" / Remaining) { pathRest =>
|
2015-05-11 23:05:18 +02:00
|
|
|
redirect("http://oldapi.example.com/" + pathRest, MovedPermanently)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-02-22 11:45:48 +01:00
|
|
|
|
|
|
|
|
"stream random numbers" in compileOnlySpec {
|
|
|
|
|
//#stream-random-numbers
|
|
|
|
|
import akka.stream.scaladsl._
|
|
|
|
|
import akka.util.ByteString
|
|
|
|
|
import akka.http.scaladsl.Http
|
|
|
|
|
import akka.http.scaladsl.model.{HttpEntity, ContentTypes}
|
|
|
|
|
import akka.http.scaladsl.server.Directives._
|
|
|
|
|
import akka.stream.ActorMaterializer
|
|
|
|
|
import scala.util.Random
|
|
|
|
|
import scala.io.StdIn
|
|
|
|
|
|
|
|
|
|
object WebServer {
|
|
|
|
|
|
|
|
|
|
def main(args: Array[String]) {
|
|
|
|
|
|
|
|
|
|
implicit val system = ActorSystem()
|
|
|
|
|
implicit val materializer = ActorMaterializer()
|
|
|
|
|
// needed for the future flatMap/onComplete in the end
|
|
|
|
|
implicit val executionContext = system.dispatcher
|
|
|
|
|
|
|
|
|
|
// streams are re-usable so we can define it here
|
|
|
|
|
// and use it for every request
|
|
|
|
|
val numbers = Source.fromIterator(() =>
|
|
|
|
|
Iterator.continually(Random.nextInt()))
|
|
|
|
|
|
|
|
|
|
val route =
|
|
|
|
|
path("random") {
|
|
|
|
|
get {
|
|
|
|
|
complete(
|
|
|
|
|
HttpEntity(
|
|
|
|
|
ContentTypes.`text/plain(UTF-8)`,
|
|
|
|
|
// transform each number to a chunk of bytes
|
|
|
|
|
numbers.map(n => ByteString(s"$n\n"))
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
|
|
|
|
|
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
|
|
|
|
|
StdIn.readLine() // let it run until user presses return
|
|
|
|
|
bindingFuture
|
|
|
|
|
.flatMap(_.unbind()) // trigger unbinding from the port
|
2016-06-02 14:06:57 +02:00
|
|
|
.onComplete(_ => system.terminate()) // and shutdown when done
|
2016-02-22 11:45:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//#stream-random-numbers
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
object Auction {
|
2016-04-12 11:14:48 +02:00
|
|
|
import akka.actor.Props
|
2016-02-22 11:45:48 +01:00
|
|
|
def props: Props = ???
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"interact with an actor" in compileOnlySpec {
|
|
|
|
|
//#actor-interaction
|
2016-04-12 11:14:48 +02:00
|
|
|
import akka.actor.ActorSystem
|
|
|
|
|
import akka.actor.Props
|
2016-02-22 11:45:48 +01:00
|
|
|
import scala.concurrent.duration._
|
|
|
|
|
import akka.util.Timeout
|
|
|
|
|
import akka.pattern.ask
|
|
|
|
|
import akka.stream.ActorMaterializer
|
|
|
|
|
import akka.http.scaladsl.Http
|
|
|
|
|
import akka.http.scaladsl.server.Directives._
|
|
|
|
|
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
|
|
|
|
|
import spray.json.DefaultJsonProtocol._
|
|
|
|
|
import scala.io.StdIn
|
|
|
|
|
|
|
|
|
|
object WebServer {
|
|
|
|
|
|
|
|
|
|
case class Bid(userId: String, bid: Int)
|
|
|
|
|
case object GetBids
|
|
|
|
|
case class Bids(bids: List[Bid])
|
|
|
|
|
|
|
|
|
|
// these are from spray-json
|
|
|
|
|
implicit val bidFormat = jsonFormat2(Bid)
|
|
|
|
|
implicit val bidsFormat = jsonFormat1(Bids)
|
|
|
|
|
|
|
|
|
|
def main(args: Array[String]) {
|
|
|
|
|
implicit val system = ActorSystem()
|
|
|
|
|
implicit val materializer = ActorMaterializer()
|
|
|
|
|
// needed for the future flatMap/onComplete in the end
|
|
|
|
|
implicit val executionContext = system.dispatcher
|
|
|
|
|
|
|
|
|
|
val auction = system.actorOf(Auction.props, "auction")
|
|
|
|
|
|
|
|
|
|
val route =
|
|
|
|
|
path("auction") {
|
|
|
|
|
put {
|
|
|
|
|
parameter("bid".as[Int], "user") { (bid, user) =>
|
|
|
|
|
// place a bid, fire-and-forget
|
|
|
|
|
auction ! Bid(user, bid)
|
!htp #18919 #19519 New JavaDSL for Akka HTTP (#20518)
* !htt #18919 #19519 Align Java HTTP server DSL with Scala
This commits replaces the Java HTTP server DSL with a Java-8 centric one
which exposes all scala DSL concepts to be usable from Java, including
custom directives, (un)marshallers, rejections, headers, and type safety
for path and query parameters.
* Add RequestContext and RouteResult to Java DSL
fix websockets
WIP bring java docs up to date.
This applies some updates to the root-level documentation
* [htp] Fix java documentation to correctly mention timeouts
Timeouts are configured the same in Java and Scala. Hence, linking to the
scala docs for timeouts from Java.
* =htc fix optionalHeaderValueByType in Java
* =htt #20200 fix java testkit always using NoLogging instead logger
* +htt actually run new javadsl tests, allow overriding config
* =htt improve javadsl test infra with more details when fails
* =htt fix bug in wrong path matcher exposed
* +htp add missing remaining path matcher
* =htp Java DSL cookie tests fixed
* =htt Java DSL ParameterDirectivesTest fixed
Protect the tweets from scalariform
Incorrect response expectations in cache condition directives spec fixed
* =htt Path directives for Java DSL
* +!htt PathMatchers rewritten, made uniform and tests passing
* Bugfix in java reject and a little test-boyscouting
* Revert "Incorrect response expectations in cache condition directives spec fixed"
This reverts commit cd50e89d45db010309f8249b090ea654ebb11c7a.
* +htc HttpAPIsTest is compile time only, not for running
Also, moved from the client package since not strictly a client test.
SecurityDirectives passing
Two faulty tests and two actual bugs.
Fix for cache condition spec not working
* Not sending in Unit instad of the implicit magnet in the test
* HeaderMagnet now works as expected
* Java API added for - and + on DateTime
PetStore example and test fixed
* Annotations to make marshalling work without default constructor
* Made model class immutable
Incorrect tests fixed
Some scaladoc boyscouting as bonus
* =htt RequestValTest sprinkled out across multiple directive tests
Client ip extraction test with incorrect header name fixed.
* =htt Incorrect CodingDirectivesTest fixed.
* =htt Bugfix for Java Unmarshaller.firstOf and fixes to JavaRouteTest
* =htt MarshallerTest fixed
* Missing seal signature added to JavaDSL
* More consistent (with Scala) test kit setup for Java
* missing Javadocs added
* Thread.sleep in default exception handler removed
* =htt copy directive docs, prepare for finishing it up
* +htt SecurityDirectives.authorize variants and test coverage added
* +htt Custom headers in Java DSL
* =htt WIP on java docs
* +htp add missing parameterOrDefault directive
Fixed a lot of doc warnings
* =htc intense progress on javadsl docs
* =htc #20470 Link to issue about docs and fix compile error
compile, migration guide
don't mima check http-experimental
* =htt Java DSL doc warnings fixed.
Only `Could not lex literal_block` ones left now
* =htc fix mima settings
* =doc fix MethodDirectives doc test with custom method
* =htc fix coding directives spec after bad merge
* =htc fix concat being corresponding to route() in javadsl
* =htt Disable consistency check for route/concat as it fails only on ci server
* !htt Minor fixes to PathMatchers
2016-05-16 10:38:40 +02:00
|
|
|
complete((StatusCodes.Accepted, "bid placed"))
|
2016-02-22 11:45:48 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
get {
|
|
|
|
|
implicit val timeout: Timeout = 5.seconds
|
|
|
|
|
|
|
|
|
|
// query the actor for the current auction state
|
|
|
|
|
val bids: Future[Bids] = (auction ? GetBids).mapTo[Bids]
|
|
|
|
|
complete(bids)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
|
|
|
|
|
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
|
|
|
|
|
StdIn.readLine() // let it run until user presses return
|
|
|
|
|
bindingFuture
|
|
|
|
|
.flatMap(_.unbind()) // trigger unbinding from the port
|
2016-06-02 14:06:57 +02:00
|
|
|
.onComplete(_ => system.terminate()) // and shutdown when done
|
2016-02-22 11:45:48 +01:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//#actor-interaction
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-05-11 23:05:18 +02:00
|
|
|
}
|