Merge branch 'master' into wip-sync-2.4.8-artery-patriknw
This commit is contained in:
commit
ccb5d1ba04
358 changed files with 9913 additions and 2030 deletions
|
|
@ -50,6 +50,19 @@ class ActorWithArgs(arg: String) extends Actor {
|
|||
def receive = { case _ => () }
|
||||
}
|
||||
|
||||
//#actor-with-value-class-argument
|
||||
class Argument(val value: String) extends AnyVal
|
||||
class ValueClassActor(arg: Argument) extends Actor {
|
||||
def receive = { case _ => () }
|
||||
}
|
||||
|
||||
object ValueClassActor {
|
||||
def props1(arg: Argument) = Props(classOf[ValueClassActor], arg) // fails at runtime
|
||||
def props2(arg: Argument) = Props(classOf[ValueClassActor], arg.value) // ok
|
||||
def props3(arg: Argument) = Props(new ValueClassActor(arg)) // ok
|
||||
}
|
||||
//#actor-with-value-class-argument
|
||||
|
||||
class DemoActorWrapper extends Actor {
|
||||
//#props-factory
|
||||
object DemoActor {
|
||||
|
|
@ -312,7 +325,7 @@ class ActorDocSpec extends AkkaSpec("""
|
|||
|
||||
val props1 = Props[MyActor]
|
||||
val props2 = Props(new ActorWithArgs("arg")) // careful, see below
|
||||
val props3 = Props(classOf[ActorWithArgs], "arg")
|
||||
val props3 = Props(classOf[ActorWithArgs], "arg") // no support for value class arguments
|
||||
//#creating-props
|
||||
|
||||
//#creating-props-deprecated
|
||||
|
|
@ -618,4 +631,4 @@ class ActorDocSpec extends AkkaSpec("""
|
|||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
44
akka-docs/rst/scala/code/docs/actor/PropsEdgeCaseSpec.scala
Normal file
44
akka-docs/rst/scala/code/docs/actor/PropsEdgeCaseSpec.scala
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package docs.actor
|
||||
|
||||
import akka.actor.{ Actor, Props }
|
||||
import docs.CompileOnlySpec
|
||||
import org.scalatest.WordSpec
|
||||
|
||||
//#props-edge-cases-value-class
|
||||
case class MyValueClass(v: Int) extends AnyVal
|
||||
|
||||
//#props-edge-cases-value-class
|
||||
|
||||
class PropsEdgeCaseSpec extends WordSpec with CompileOnlySpec {
|
||||
"value-class-edge-case-example" in compileOnlySpec {
|
||||
//#props-edge-cases-value-class-example
|
||||
class ValueActor(value: MyValueClass) extends Actor {
|
||||
def receive = {
|
||||
case multiplier: Long => sender() ! (value.v * multiplier)
|
||||
}
|
||||
}
|
||||
val valueClassProp = Props(classOf[ValueActor], MyValueClass(5)) // Unsupported
|
||||
//#props-edge-cases-value-class-example
|
||||
|
||||
//#props-edge-cases-default-values
|
||||
class DefaultValueActor(a: Int, b: Int = 5) extends Actor {
|
||||
def receive = {
|
||||
case x: Int => sender() ! ((a + x) * b)
|
||||
}
|
||||
}
|
||||
|
||||
val defaultValueProp1 = Props(classOf[DefaultValueActor], 2.0) // Unsupported
|
||||
|
||||
class DefaultValueActor2(b: Int = 5) extends Actor {
|
||||
def receive = {
|
||||
case x: Int => sender() ! (x * b)
|
||||
}
|
||||
}
|
||||
val defaultValueProp2 = Props[DefaultValueActor2] // Unsupported
|
||||
val defaultValueProp3 = Props(classOf[DefaultValueActor2]) // Unsupported
|
||||
//#props-edge-cases-default-values
|
||||
}
|
||||
}
|
||||
|
|
@ -4,37 +4,146 @@
|
|||
|
||||
package docs.http.scaladsl
|
||||
|
||||
import akka.Done
|
||||
import akka.actor.{ ActorLogging, ActorSystem }
|
||||
import akka.stream.{ ActorMaterializerSettings }
|
||||
import akka.http.scaladsl.model.HttpEntity.Strict
|
||||
import akka.http.scaladsl.model.HttpMessage.DiscardedEntity
|
||||
import akka.stream.{ IOResult, Materializer }
|
||||
import akka.stream.scaladsl.{ Framing, Sink }
|
||||
import akka.util.ByteString
|
||||
import docs.CompileOnlySpec
|
||||
import org.scalatest.{ Matchers, WordSpec }
|
||||
|
||||
class HttpClientExampleSpec extends WordSpec with Matchers {
|
||||
import scala.concurrent.{ ExecutionContextExecutor, Future }
|
||||
|
||||
"outgoing-connection-example" in {
|
||||
pending // compile-time only test
|
||||
class HttpClientExampleSpec extends WordSpec with Matchers with CompileOnlySpec {
|
||||
|
||||
"manual-entity-consume-example-1" in compileOnlySpec {
|
||||
//#manual-entity-consume-example-1
|
||||
import java.io.File
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.stream.scaladsl.Framing
|
||||
import akka.stream.scaladsl.FileIO
|
||||
import akka.http.scaladsl.model._
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val dispatcher = system.dispatcher
|
||||
implicit val materializer = ActorMaterializer()
|
||||
|
||||
val response: HttpResponse = ???
|
||||
|
||||
response.entity.dataBytes
|
||||
.via(Framing.delimiter(ByteString("\n"), maximumFrameLength = 256))
|
||||
.map(transformEachLine)
|
||||
.runWith(FileIO.toPath(new File("/tmp/example.out").toPath))
|
||||
|
||||
def transformEachLine(line: ByteString): ByteString = ???
|
||||
|
||||
//#manual-entity-consume-example-1
|
||||
}
|
||||
|
||||
"manual-entity-consume-example-2" in compileOnlySpec {
|
||||
//#manual-entity-consume-example-2
|
||||
import java.io.File
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.http.scaladsl.model._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val dispatcher = system.dispatcher
|
||||
implicit val materializer = ActorMaterializer()
|
||||
|
||||
case class ExamplePerson(name: String)
|
||||
def parse(line: ByteString): ExamplePerson = ???
|
||||
|
||||
val response: HttpResponse = ???
|
||||
|
||||
// toStrict to enforce all data be loaded into memory from the connection
|
||||
val strictEntity: Future[HttpEntity.Strict] = response.entity.toStrict(3.seconds)
|
||||
|
||||
// while API remains the same to consume dataBytes, now they're in memory already:
|
||||
val transformedData: Future[ExamplePerson] =
|
||||
strictEntity flatMap { e =>
|
||||
e.dataBytes
|
||||
.runFold(ByteString.empty) { case (acc, b) => acc ++ b }
|
||||
.map(parse)
|
||||
}
|
||||
|
||||
//#manual-entity-consume-example-2
|
||||
}
|
||||
|
||||
"manual-entity-discard-example-1" in compileOnlySpec {
|
||||
//#manual-entity-discard-example-1
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.http.scaladsl.model._
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val dispatcher = system.dispatcher
|
||||
implicit val materializer = ActorMaterializer()
|
||||
|
||||
val response1: HttpResponse = ??? // obtained from an HTTP call (see examples below)
|
||||
|
||||
val discarded: DiscardedEntity = response1.discardEntityBytes()
|
||||
discarded.future.onComplete { case done => println("Entity discarded completely!") }
|
||||
|
||||
//#manual-entity-discard-example-1
|
||||
}
|
||||
"manual-entity-discard-example-2" in compileOnlySpec {
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.http.scaladsl.model._
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val dispatcher = system.dispatcher
|
||||
implicit val materializer = ActorMaterializer()
|
||||
|
||||
//#manual-entity-discard-example-2
|
||||
val response1: HttpResponse = ??? // obtained from an HTTP call (see examples below)
|
||||
|
||||
val discardingComplete: Future[Done] = response1.entity.dataBytes.runWith(Sink.ignore)
|
||||
discardingComplete.onComplete { case done => println("Entity discarded completely!") }
|
||||
//#manual-entity-discard-example-2
|
||||
}
|
||||
|
||||
"outgoing-connection-example" in compileOnlySpec {
|
||||
//#outgoing-connection-example
|
||||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.stream.scaladsl._
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.util.{ Failure, Success }
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
object WebClient {
|
||||
def main(args: Array[String]): Unit = {
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
implicit val executionContext = system.dispatcher
|
||||
|
||||
val connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] =
|
||||
Http().outgoingConnection("akka.io")
|
||||
val responseFuture: Future[HttpResponse] =
|
||||
Source.single(HttpRequest(uri = "/"))
|
||||
.via(connectionFlow)
|
||||
.runWith(Sink.head)
|
||||
val connectionFlow: Flow[HttpRequest, HttpResponse, Future[Http.OutgoingConnection]] =
|
||||
Http().outgoingConnection("akka.io")
|
||||
val responseFuture: Future[HttpResponse] =
|
||||
Source.single(HttpRequest(uri = "/"))
|
||||
.via(connectionFlow)
|
||||
.runWith(Sink.head)
|
||||
|
||||
responseFuture.andThen {
|
||||
case Success(_) => println("request succeded")
|
||||
case Failure(_) => println("request failed")
|
||||
}.andThen {
|
||||
case _ => system.terminate()
|
||||
}
|
||||
}
|
||||
}
|
||||
//#outgoing-connection-example
|
||||
}
|
||||
|
||||
"host-level-example" in {
|
||||
pending // compile-time only test
|
||||
"host-level-example" in compileOnlySpec {
|
||||
//#host-level-example
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.model._
|
||||
|
|
@ -55,14 +164,14 @@ class HttpClientExampleSpec extends WordSpec with Matchers {
|
|||
//#host-level-example
|
||||
}
|
||||
|
||||
"single-request-example" in {
|
||||
pending // compile-time only test
|
||||
"single-request-example" in compileOnlySpec {
|
||||
//#single-request-example
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.stream.ActorMaterializer
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.util.{ Failure, Success }
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
|
|
@ -72,8 +181,7 @@ class HttpClientExampleSpec extends WordSpec with Matchers {
|
|||
//#single-request-example
|
||||
}
|
||||
|
||||
"single-request-in-actor-example" in {
|
||||
pending // compile-time only test
|
||||
"single-request-in-actor-example" in compileOnlySpec {
|
||||
//#single-request-in-actor-example
|
||||
import akka.actor.Actor
|
||||
import akka.http.scaladsl.Http
|
||||
|
|
|
|||
|
|
@ -5,15 +5,13 @@
|
|||
package docs.http.scaladsl
|
||||
|
||||
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 }
|
||||
import akka.http.scaladsl.model.{ RequestEntity, StatusCodes }
|
||||
import akka.stream.scaladsl.Sink
|
||||
import akka.testkit.TestActors
|
||||
import docs.CompileOnlySpec
|
||||
import org.scalatest.{ Matchers, WordSpec }
|
||||
import scala.language.postfixOps
|
||||
|
||||
import scala.language.postfixOps
|
||||
import scala.concurrent.{ ExecutionContext, Future }
|
||||
|
||||
class HttpServerExampleSpec extends WordSpec with Matchers
|
||||
|
|
@ -44,37 +42,50 @@ class HttpServerExampleSpec extends WordSpec with Matchers
|
|||
"binding-failure-high-level-example" in compileOnlySpec {
|
||||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.Http.ServerBinding
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.stream.ActorMaterializer
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
// needed for the future onFailure in the end
|
||||
implicit val executionContext = system.dispatcher
|
||||
import scala.concurrent.Future
|
||||
|
||||
val handler = get {
|
||||
complete("Hello world!")
|
||||
object WebServer {
|
||||
def main(args: Array[String]) {
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
// needed for the future onFailure in the end
|
||||
implicit val executionContext = system.dispatcher
|
||||
|
||||
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)
|
||||
|
||||
bindingFuture.onFailure {
|
||||
case ex: Exception =>
|
||||
log.error(ex, "Failed to bind to {}:{}!", host, port)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
|
||||
bindingFuture.onFailure {
|
||||
case ex: Exception =>
|
||||
log.error(ex, "Failed to bind to {}:{}!", host, port)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// mock values:
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.actor.ActorSystem
|
||||
val handleConnections: Sink[Http.IncomingConnection, Future[Http.ServerBinding]] =
|
||||
val handleConnections = {
|
||||
import akka.stream.scaladsl.Sink
|
||||
Sink.ignore.mapMaterializedValue(_ => Future.failed(new Exception("")))
|
||||
}
|
||||
|
||||
"binding-failure-handling" in compileOnlySpec {
|
||||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.Http.ServerBinding
|
||||
import akka.stream.ActorMaterializer
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
// needed for the future onFailure in the end
|
||||
|
|
@ -102,11 +113,8 @@ class HttpServerExampleSpec extends WordSpec with Matchers
|
|||
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
|
||||
import akka.stream.scaladsl.Flow
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
|
|
@ -132,8 +140,9 @@ class HttpServerExampleSpec extends WordSpec with Matchers
|
|||
"connection-stream-failure-handling" in compileOnlySpec {
|
||||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.model.{ ContentTypes, HttpEntity }
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.stream.scaladsl.Flow
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
|
|
@ -152,7 +161,7 @@ class HttpServerExampleSpec extends WordSpec with Matchers
|
|||
val httpEcho = Flow[HttpRequest]
|
||||
.via(reactToConnectionFailure)
|
||||
.map { request =>
|
||||
// simple text "echo" response:
|
||||
// simple streaming (!) "echo" response:
|
||||
HttpResponse(entity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, request.entity.dataBytes))
|
||||
}
|
||||
|
||||
|
|
@ -188,7 +197,8 @@ class HttpServerExampleSpec extends WordSpec with Matchers
|
|||
case HttpRequest(GET, Uri.Path("/crash"), _, _, _) =>
|
||||
sys.error("BOOM!")
|
||||
|
||||
case _: HttpRequest =>
|
||||
case r: HttpRequest =>
|
||||
r.discardEntityBytes() // important to drain incoming HTTP Entity stream
|
||||
HttpResponse(404, entity = "Unknown resource!")
|
||||
}
|
||||
|
||||
|
|
@ -203,6 +213,7 @@ class HttpServerExampleSpec extends WordSpec with Matchers
|
|||
}
|
||||
|
||||
"low-level-server-example" in compileOnlySpec {
|
||||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.model.HttpMethods._
|
||||
import akka.http.scaladsl.model._
|
||||
|
|
@ -229,7 +240,8 @@ class HttpServerExampleSpec extends WordSpec with Matchers
|
|||
case HttpRequest(GET, Uri.Path("/crash"), _, _, _) =>
|
||||
sys.error("BOOM!")
|
||||
|
||||
case _: HttpRequest =>
|
||||
case r: HttpRequest =>
|
||||
r.discardEntityBytes() // important to drain incoming HTTP Entity stream
|
||||
HttpResponse(404, entity = "Unknown resource!")
|
||||
}
|
||||
|
||||
|
|
@ -286,7 +298,9 @@ class HttpServerExampleSpec extends WordSpec with Matchers
|
|||
}
|
||||
|
||||
"minimal-routing-example" in compileOnlySpec {
|
||||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.stream.ActorMaterializer
|
||||
import scala.io.StdIn
|
||||
|
|
@ -319,13 +333,14 @@ class HttpServerExampleSpec extends WordSpec with Matchers
|
|||
|
||||
"long-routing-example" in compileOnlySpec {
|
||||
//#long-routing-example
|
||||
import akka.actor.ActorRef
|
||||
import akka.actor.{ActorRef, ActorSystem}
|
||||
import akka.http.scaladsl.coding.Deflate
|
||||
import akka.http.scaladsl.marshalling.ToResponseMarshaller
|
||||
import akka.http.scaladsl.model.StatusCodes.MovedPermanently
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller
|
||||
import akka.pattern.ask
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.util.Timeout
|
||||
|
||||
// types used by the API routes
|
||||
|
|
@ -427,6 +442,7 @@ class HttpServerExampleSpec extends WordSpec with Matchers
|
|||
|
||||
"stream random numbers" in compileOnlySpec {
|
||||
//#stream-random-numbers
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.scaladsl._
|
||||
import akka.util.ByteString
|
||||
import akka.http.scaladsl.Http
|
||||
|
|
@ -483,15 +499,15 @@ class HttpServerExampleSpec extends WordSpec with Matchers
|
|||
"interact with an actor" in compileOnlySpec {
|
||||
//#actor-interaction
|
||||
import akka.actor.ActorSystem
|
||||
import akka.actor.Props
|
||||
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.model.StatusCodes
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
|
||||
import akka.pattern.ask
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.util.Timeout
|
||||
import spray.json.DefaultJsonProtocol._
|
||||
import scala.concurrent.duration._
|
||||
import scala.io.StdIn
|
||||
|
||||
object WebServer {
|
||||
|
|
@ -541,6 +557,126 @@ class HttpServerExampleSpec extends WordSpec with Matchers
|
|||
}
|
||||
//#actor-interaction
|
||||
}
|
||||
|
||||
"consume entity using entity directive" in compileOnlySpec {
|
||||
//#consume-entity-directive
|
||||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
|
||||
import akka.stream.ActorMaterializer
|
||||
import spray.json.DefaultJsonProtocol._
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
// needed for the future flatMap/onComplete in the end
|
||||
implicit val executionContext = system.dispatcher
|
||||
|
||||
final case class Bid(userId: String, bid: Int)
|
||||
|
||||
// these are from spray-json
|
||||
implicit val bidFormat = jsonFormat2(Bid)
|
||||
|
||||
val route =
|
||||
path("bid") {
|
||||
put {
|
||||
entity(as[Bid]) { bid =>
|
||||
// incoming entity is fully consumed and converted into a Bid
|
||||
complete("The bid was: " + bid)
|
||||
}
|
||||
}
|
||||
}
|
||||
//#consume-entity-directive
|
||||
}
|
||||
|
||||
"consume entity using raw dataBytes to file" in compileOnlySpec {
|
||||
//#consume-raw-dataBytes
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.scaladsl.FileIO
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.stream.ActorMaterializer
|
||||
import java.io.File
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
// needed for the future flatMap/onComplete in the end
|
||||
implicit val executionContext = system.dispatcher
|
||||
|
||||
val route =
|
||||
(put & path("lines")) {
|
||||
withoutSizeLimit {
|
||||
extractDataBytes { bytes =>
|
||||
val finishedWriting = bytes.runWith(FileIO.toPath(new File("/tmp/example.out").toPath))
|
||||
|
||||
// we only want to respond once the incoming data has been handled:
|
||||
onComplete(finishedWriting) { ioResult =>
|
||||
complete("Finished writing data: " + ioResult)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//#consume-raw-dataBytes
|
||||
}
|
||||
|
||||
"drain entity using request#discardEntityBytes" in compileOnlySpec {
|
||||
//#discard-discardEntityBytes
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.scaladsl.FileIO
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.http.scaladsl.model.HttpRequest
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
// needed for the future flatMap/onComplete in the end
|
||||
implicit val executionContext = system.dispatcher
|
||||
|
||||
val route =
|
||||
(put & path("lines")) {
|
||||
withoutSizeLimit {
|
||||
extractRequest { r: HttpRequest =>
|
||||
val finishedWriting = r.discardEntityBytes().future
|
||||
|
||||
// we only want to respond once the incoming data has been handled:
|
||||
onComplete(finishedWriting) { done =>
|
||||
complete("Drained all data from connection... (" + done + ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//#discard-discardEntityBytes
|
||||
}
|
||||
|
||||
"discard entity manually" in compileOnlySpec {
|
||||
//#discard-close-connections
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.scaladsl.Sink
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.http.scaladsl.model.headers.Connection
|
||||
import akka.stream.ActorMaterializer
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
// needed for the future flatMap/onComplete in the end
|
||||
implicit val executionContext = system.dispatcher
|
||||
|
||||
val route =
|
||||
(put & path("lines")) {
|
||||
withoutSizeLimit {
|
||||
extractDataBytes { data =>
|
||||
// Closing connections, method 1 (eager):
|
||||
// we deem this request as illegal, and close the connection right away:
|
||||
data.runWith(Sink.cancelled) // "brutally" closes the connection
|
||||
|
||||
// Closing connections, method 2 (graceful):
|
||||
// consider draining connection and replying with `Connection: Close` header
|
||||
// if you want the client to close after this request/reply cycle instead:
|
||||
respondWithHeader(Connection("close"))
|
||||
complete(StatusCodes.Forbidden -> "Not allowed!")
|
||||
}
|
||||
}
|
||||
}
|
||||
//#discard-close-connections
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,13 +9,12 @@ import akka.http.scaladsl.Http
|
|||
import akka.stream.ActorMaterializer
|
||||
import akka.util.ByteString
|
||||
import com.typesafe.sslconfig.akka.AkkaSSLConfig
|
||||
import docs.CompileOnlySpec
|
||||
import org.scalatest.{ Matchers, WordSpec }
|
||||
|
||||
class HttpsExamplesSpec extends WordSpec with Matchers {
|
||||
|
||||
"disable SNI for connection" in {
|
||||
pending // compile-time only test
|
||||
class HttpsExamplesSpec extends WordSpec with Matchers with CompileOnlySpec {
|
||||
|
||||
"disable SNI for connection" in compileOnlySpec {
|
||||
val unsafeHost = "example.com"
|
||||
//#disable-sni-connection
|
||||
implicit val system = ActorSystem()
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport
|
|||
import akka.http.scaladsl.server.Directives
|
||||
import org.scalatest.{ Matchers, WordSpec }
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
class SprayJsonExampleSpec extends WordSpec with Matchers {
|
||||
|
||||
def compileOnlySpec(body: => Unit) = ()
|
||||
|
|
@ -53,6 +51,7 @@ class SprayJsonExampleSpec extends WordSpec with Matchers {
|
|||
"second-spray-json-example" in compileOnlySpec {
|
||||
//#second-spray-json-example
|
||||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.Done
|
||||
import akka.http.scaladsl.server.Route
|
||||
|
|
@ -61,6 +60,10 @@ class SprayJsonExampleSpec extends WordSpec with Matchers {
|
|||
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
|
||||
import spray.json.DefaultJsonProtocol._
|
||||
|
||||
import scala.io.StdIn
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
object WebServer {
|
||||
|
||||
// domain model
|
||||
|
|
@ -80,6 +83,8 @@ class SprayJsonExampleSpec extends WordSpec with Matchers {
|
|||
// needed to run the route
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
// needed for the future map/flatmap in the end
|
||||
implicit val executionContext = system.dispatcher
|
||||
|
||||
val route: Route =
|
||||
get {
|
||||
|
|
@ -104,6 +109,13 @@ class SprayJsonExampleSpec extends WordSpec with Matchers {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
.onComplete(_ ⇒ system.terminate()) // and shutdown when done
|
||||
|
||||
}
|
||||
}
|
||||
//#second-spray-json-example
|
||||
|
|
|
|||
|
|
@ -3,17 +3,14 @@
|
|||
*/
|
||||
package docs.http.scaladsl
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl.model.headers.{ Authorization, BasicHttpCredentials }
|
||||
import docs.CompileOnlySpec
|
||||
import org.scalatest.{ Matchers, WordSpec }
|
||||
|
||||
import scala.concurrent.Promise
|
||||
|
||||
class WebSocketClientExampleSpec extends WordSpec with Matchers with CompileOnlySpec {
|
||||
|
||||
"singleWebSocket-request-example" in compileOnlySpec {
|
||||
//#single-WebSocket-request
|
||||
import akka.actor.ActorSystem
|
||||
import akka.{ Done, NotUsed }
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.stream.ActorMaterializer
|
||||
|
|
@ -23,59 +20,60 @@ class WebSocketClientExampleSpec extends WordSpec with Matchers with CompileOnly
|
|||
|
||||
import scala.concurrent.Future
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
import system.dispatcher
|
||||
object SingleWebSocketRequest {
|
||||
def main(args: Array[String]) = {
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
import system.dispatcher
|
||||
|
||||
// print each incoming strict text message
|
||||
val printSink: Sink[Message, Future[Done]] =
|
||||
Sink.foreach {
|
||||
case message: TextMessage.Strict =>
|
||||
println(message.text)
|
||||
}
|
||||
// print each incoming strict text message
|
||||
val printSink: Sink[Message, Future[Done]] =
|
||||
Sink.foreach {
|
||||
case message: TextMessage.Strict =>
|
||||
println(message.text)
|
||||
}
|
||||
|
||||
val helloSource: Source[Message, NotUsed] =
|
||||
Source.single(TextMessage("hello world!"))
|
||||
val helloSource: Source[Message, NotUsed] =
|
||||
Source.single(TextMessage("hello world!"))
|
||||
|
||||
// the Future[Done] is the materialized value of Sink.foreach
|
||||
// and it is completed when the stream completes
|
||||
val flow: Flow[Message, Message, Future[Done]] =
|
||||
Flow.fromSinkAndSourceMat(printSink, helloSource)(Keep.left)
|
||||
// the Future[Done] is the materialized value of Sink.foreach
|
||||
// and it is completed when the stream completes
|
||||
val flow: Flow[Message, Message, Future[Done]] =
|
||||
Flow.fromSinkAndSourceMat(printSink, helloSource)(Keep.left)
|
||||
|
||||
// upgradeResponse is a Future[WebSocketUpgradeResponse] that
|
||||
// completes or fails when the connection succeeds or fails
|
||||
// and closed is a Future[Done] representing the stream completion from above
|
||||
val (upgradeResponse, closed) =
|
||||
Http().singleWebSocketRequest(WebSocketRequest("ws://echo.websocket.org"), flow)
|
||||
// upgradeResponse is a Future[WebSocketUpgradeResponse] that
|
||||
// completes or fails when the connection succeeds or fails
|
||||
// and closed is a Future[Done] representing the stream completion from above
|
||||
val (upgradeResponse, closed) =
|
||||
Http().singleWebSocketRequest(WebSocketRequest("ws://echo.websocket.org"), flow)
|
||||
|
||||
val connected = upgradeResponse.map { upgrade =>
|
||||
// just like a regular http request we can get 404 NotFound,
|
||||
// with a response body, that will be available from upgrade.response
|
||||
if (upgrade.response.status == StatusCodes.OK) {
|
||||
Done
|
||||
} else {
|
||||
throw new RuntimeException(s"Connection failed: ${upgrade.response.status}")
|
||||
val connected = upgradeResponse.map { upgrade =>
|
||||
// just like a regular http request we can access response status which is available via upgrade.response.status
|
||||
// status code 101 (Switching Protocols) indicates that server support WebSockets
|
||||
if (upgrade.response.status == StatusCodes.SwitchingProtocols) {
|
||||
Done
|
||||
} else {
|
||||
throw new RuntimeException(s"Connection failed: ${upgrade.response.status}")
|
||||
}
|
||||
}
|
||||
|
||||
// in a real application you would not side effect here
|
||||
// and handle errors more carefully
|
||||
connected.onComplete(println)
|
||||
closed.foreach(_ => println("closed"))
|
||||
}
|
||||
}
|
||||
|
||||
// in a real application you would not side effect here
|
||||
// and handle errors more carefully
|
||||
connected.onComplete(println)
|
||||
closed.foreach(_ => println("closed"))
|
||||
|
||||
//#single-WebSocket-request
|
||||
}
|
||||
|
||||
"half-closed-WebSocket-closing-example" in compileOnlySpec {
|
||||
import akka.actor.ActorSystem
|
||||
import akka.{ Done, NotUsed }
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.stream.scaladsl._
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.model.ws._
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
import system.dispatcher
|
||||
|
|
@ -97,14 +95,13 @@ class WebSocketClientExampleSpec extends WordSpec with Matchers with CompileOnly
|
|||
}
|
||||
|
||||
"half-closed-WebSocket-working-example" in compileOnlySpec {
|
||||
import akka.{ Done, NotUsed }
|
||||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.stream.scaladsl._
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.model.ws._
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.Promise
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
|
|
@ -130,14 +127,14 @@ class WebSocketClientExampleSpec extends WordSpec with Matchers with CompileOnly
|
|||
}
|
||||
|
||||
"half-closed-WebSocket-finite-working-example" in compileOnlySpec {
|
||||
import akka.actor.ActorSystem
|
||||
import akka.{ Done, NotUsed }
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.stream.scaladsl._
|
||||
import akka.http.scaladsl.model._
|
||||
import akka.http.scaladsl.model.ws._
|
||||
|
||||
import scala.concurrent.Future
|
||||
import scala.concurrent.Promise
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
|
|
@ -163,11 +160,14 @@ class WebSocketClientExampleSpec extends WordSpec with Matchers with CompileOnly
|
|||
}
|
||||
|
||||
"authorized-singleWebSocket-request-example" in compileOnlySpec {
|
||||
import akka.actor.ActorSystem
|
||||
import akka.NotUsed
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.stream.scaladsl._
|
||||
import akka.http.scaladsl.model.headers.{ Authorization, BasicHttpCredentials }
|
||||
import akka.http.scaladsl.model.ws._
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
import collection.immutable.Seq
|
||||
|
|
@ -187,6 +187,7 @@ class WebSocketClientExampleSpec extends WordSpec with Matchers with CompileOnly
|
|||
|
||||
"WebSocketClient-flow-example" in compileOnlySpec {
|
||||
//#WebSocket-client-flow
|
||||
import akka.actor.ActorSystem
|
||||
import akka.Done
|
||||
import akka.http.scaladsl.Http
|
||||
import akka.stream.ActorMaterializer
|
||||
|
|
@ -196,48 +197,51 @@ class WebSocketClientExampleSpec extends WordSpec with Matchers with CompileOnly
|
|||
|
||||
import scala.concurrent.Future
|
||||
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
import system.dispatcher
|
||||
object WebSocketClientFlow {
|
||||
def main(args: Array[String]) = {
|
||||
implicit val system = ActorSystem()
|
||||
implicit val materializer = ActorMaterializer()
|
||||
import system.dispatcher
|
||||
|
||||
// Future[Done] is the materialized value of Sink.foreach,
|
||||
// emitted when the stream completes
|
||||
val incoming: Sink[Message, Future[Done]] =
|
||||
Sink.foreach[Message] {
|
||||
case message: TextMessage.Strict =>
|
||||
println(message.text)
|
||||
}
|
||||
// Future[Done] is the materialized value of Sink.foreach,
|
||||
// emitted when the stream completes
|
||||
val incoming: Sink[Message, Future[Done]] =
|
||||
Sink.foreach[Message] {
|
||||
case message: TextMessage.Strict =>
|
||||
println(message.text)
|
||||
}
|
||||
|
||||
// send this as a message over the WebSocket
|
||||
val outgoing = Source.single(TextMessage("hello world!"))
|
||||
// send this as a message over the WebSocket
|
||||
val outgoing = Source.single(TextMessage("hello world!"))
|
||||
|
||||
// flow to use (note: not re-usable!)
|
||||
val webSocketFlow = Http().webSocketClientFlow(WebSocketRequest("ws://echo.websocket.org"))
|
||||
// flow to use (note: not re-usable!)
|
||||
val webSocketFlow = Http().webSocketClientFlow(WebSocketRequest("ws://echo.websocket.org"))
|
||||
|
||||
// the materialized value is a tuple with
|
||||
// upgradeResponse is a Future[WebSocketUpgradeResponse] that
|
||||
// completes or fails when the connection succeeds or fails
|
||||
// and closed is a Future[Done] with the stream completion from the incoming sink
|
||||
val (upgradeResponse, closed) =
|
||||
outgoing
|
||||
.viaMat(webSocketFlow)(Keep.right) // keep the materialized Future[WebSocketUpgradeResponse]
|
||||
.toMat(incoming)(Keep.both) // also keep the Future[Done]
|
||||
.run()
|
||||
// the materialized value is a tuple with
|
||||
// upgradeResponse is a Future[WebSocketUpgradeResponse] that
|
||||
// completes or fails when the connection succeeds or fails
|
||||
// and closed is a Future[Done] with the stream completion from the incoming sink
|
||||
val (upgradeResponse, closed) =
|
||||
outgoing
|
||||
.viaMat(webSocketFlow)(Keep.right) // keep the materialized Future[WebSocketUpgradeResponse]
|
||||
.toMat(incoming)(Keep.both) // also keep the Future[Done]
|
||||
.run()
|
||||
|
||||
// just like a regular http request we can get 404 NotFound etc.
|
||||
// that will be available from upgrade.response
|
||||
val connected = upgradeResponse.flatMap { upgrade =>
|
||||
if (upgrade.response.status == StatusCodes.OK) {
|
||||
Future.successful(Done)
|
||||
} else {
|
||||
throw new RuntimeException(s"Connection failed: ${upgrade.response.status}")
|
||||
// just like a regular http request we can access response status which is available via upgrade.response.status
|
||||
// status code 101 (Switching Protocols) indicates that server support WebSockets
|
||||
val connected = upgradeResponse.flatMap { upgrade =>
|
||||
if (upgrade.response.status == StatusCodes.SwitchingProtocols) {
|
||||
Future.successful(Done)
|
||||
} else {
|
||||
throw new RuntimeException(s"Connection failed: ${upgrade.response.status}")
|
||||
}
|
||||
}
|
||||
|
||||
// in a real application you would not side effect here
|
||||
connected.onComplete(println)
|
||||
closed.foreach(_ => println("closed"))
|
||||
}
|
||||
}
|
||||
|
||||
// in a real application you would not side effect here
|
||||
connected.onComplete(println)
|
||||
closed.foreach(_ => println("closed"))
|
||||
|
||||
//#WebSocket-client-flow
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
/**
|
||||
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package docs.http.scaladsl.server
|
||||
|
||||
import akka.actor.ActorSystem
|
||||
import akka.http.scaladsl.server.{ Directives, Route }
|
||||
import docs.CompileOnlySpec
|
||||
import org.scalatest.WordSpec
|
||||
|
||||
import scala.concurrent.Future
|
||||
|
||||
class BlockingInHttpExamplesSpec extends WordSpec with CompileOnlySpec
|
||||
with Directives {
|
||||
|
||||
compileOnlySpec {
|
||||
val system: ActorSystem = ???
|
||||
|
||||
//#blocking-example-in-default-dispatcher
|
||||
// BAD (due to blocking in Future, on default dispatcher)
|
||||
implicit val defaultDispatcher = system.dispatcher
|
||||
|
||||
val routes: Route = post {
|
||||
complete {
|
||||
Future { // uses defaultDispatcher
|
||||
Thread.sleep(5000) // will block on default dispatcher,
|
||||
System.currentTimeMillis().toString // Starving the routing infrastructure
|
||||
}
|
||||
}
|
||||
}
|
||||
//#
|
||||
}
|
||||
|
||||
compileOnlySpec {
|
||||
val system: ActorSystem = ???
|
||||
|
||||
//#blocking-example-in-dedicated-dispatcher
|
||||
// GOOD (the blocking is now isolated onto a dedicated dispatcher):
|
||||
implicit val blockingDispatcher = system.dispatchers.lookup("my-blocking-dispatcher")
|
||||
|
||||
val routes: Route = post {
|
||||
complete {
|
||||
Future { // uses the good "blocking dispatcher" that we configured,
|
||||
// instead of the default dispatcher- the blocking is isolated.
|
||||
Thread.sleep(5000)
|
||||
System.currentTimeMillis().toString
|
||||
}
|
||||
}
|
||||
}
|
||||
//#
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ abstract class HttpsServerExampleSpec extends WordSpec with Matchers
|
|||
tmf.init(ks)
|
||||
|
||||
val sslContext: SSLContext = SSLContext.getInstance("TLS")
|
||||
sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, SecureRandom.getInstanceStrong)
|
||||
sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, new SecureRandom)
|
||||
val https: HttpsConnectionContext = ConnectionContext.https(sslContext)
|
||||
|
||||
// sets default context to HTTPS – all Http() bound servers for this ActorSystem will use HTTPS from now on
|
||||
|
|
|
|||
|
|
@ -6,11 +6,11 @@ package docs.http.scaladsl.server
|
|||
|
||||
import akka.http.scaladsl.model.ws.BinaryMessage
|
||||
import akka.stream.scaladsl.Sink
|
||||
import docs.CompileOnlySpec
|
||||
import org.scalatest.{ Matchers, WordSpec }
|
||||
|
||||
class WebSocketExampleSpec extends WordSpec with Matchers {
|
||||
"core-example" in {
|
||||
pending // compile-time only test
|
||||
class WebSocketExampleSpec extends WordSpec with Matchers with CompileOnlySpec {
|
||||
"core-example" in compileOnlySpec {
|
||||
//#websocket-example-using-core
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.ActorMaterializer
|
||||
|
|
@ -49,7 +49,9 @@ class WebSocketExampleSpec extends WordSpec with Matchers {
|
|||
case Some(upgrade) => upgrade.handleMessages(greeterWebSocketService)
|
||||
case None => HttpResponse(400, entity = "Not a valid websocket request!")
|
||||
}
|
||||
case _: HttpRequest => HttpResponse(404, entity = "Unknown resource!")
|
||||
case r: HttpRequest =>
|
||||
r.discardEntityBytes() // important to drain incoming HTTP Entity stream
|
||||
HttpResponse(404, entity = "Unknown resource!")
|
||||
}
|
||||
//#websocket-request-handling
|
||||
|
||||
|
|
@ -64,8 +66,7 @@ class WebSocketExampleSpec extends WordSpec with Matchers {
|
|||
.flatMap(_.unbind()) // trigger unbinding from the port
|
||||
.onComplete(_ => system.terminate()) // and shutdown when done
|
||||
}
|
||||
"routing-example" in {
|
||||
pending // compile-time only test
|
||||
"routing-example" in compileOnlySpec {
|
||||
import akka.actor.ActorSystem
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.stream.scaladsl.{ Source, Flow }
|
||||
|
|
@ -85,6 +86,7 @@ class WebSocketExampleSpec extends WordSpec with Matchers {
|
|||
.collect {
|
||||
case tm: TextMessage => TextMessage(Source.single("Hello ") ++ tm.textStream)
|
||||
// ignore binary messages
|
||||
// TODO #20096 in case a Streamed message comes in, we should runWith(Sink.ignore) its data
|
||||
}
|
||||
|
||||
//#websocket-routing
|
||||
|
|
|
|||
|
|
@ -303,13 +303,11 @@ class BasicDirectivesExamplesSpec extends RoutingSpec {
|
|||
"mapRouteResult" in {
|
||||
//#mapRouteResult
|
||||
// this directive is a joke, don't do that :-)
|
||||
val makeEverythingOk = mapRouteResult { r =>
|
||||
r match {
|
||||
case Complete(response) =>
|
||||
// "Everything is OK!"
|
||||
Complete(response.copy(status = 200))
|
||||
case _ => r
|
||||
}
|
||||
val makeEverythingOk = mapRouteResult {
|
||||
case Complete(response) =>
|
||||
// "Everything is OK!"
|
||||
Complete(response.copy(status = 200))
|
||||
case r => r
|
||||
}
|
||||
|
||||
val route =
|
||||
|
|
@ -591,11 +589,9 @@ class BasicDirectivesExamplesSpec extends RoutingSpec {
|
|||
//#mapRouteResultWith-0
|
||||
case object MyCustomRejection extends Rejection
|
||||
val rejectRejections = // not particularly useful directive
|
||||
mapRouteResultWith { res =>
|
||||
res match {
|
||||
case Rejected(_) => Future(Rejected(List(AuthorizationFailedRejection)))
|
||||
case _ => Future(res)
|
||||
}
|
||||
mapRouteResultWith {
|
||||
case Rejected(_) => Future(Rejected(List(AuthorizationFailedRejection)))
|
||||
case res => Future(res)
|
||||
}
|
||||
val route =
|
||||
rejectRejections {
|
||||
|
|
@ -694,7 +690,7 @@ class BasicDirectivesExamplesSpec extends RoutingSpec {
|
|||
|
||||
// tests:
|
||||
Get("/") ~> route ~> check {
|
||||
responseAs[String] shouldEqual s"RoutingSettings.renderVanityFooter = true"
|
||||
responseAs[String] shouldEqual "RoutingSettings.renderVanityFooter = true"
|
||||
}
|
||||
//#
|
||||
}
|
||||
|
|
@ -767,7 +763,7 @@ class BasicDirectivesExamplesSpec extends RoutingSpec {
|
|||
pathPrefix("123") {
|
||||
ignoring456 {
|
||||
path("abc") {
|
||||
complete(s"Content")
|
||||
complete("Content")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -799,5 +795,36 @@ class BasicDirectivesExamplesSpec extends RoutingSpec {
|
|||
}
|
||||
//#
|
||||
}
|
||||
"extractRequestEntity-example" in {
|
||||
//#extractRequestEntity-example
|
||||
val route =
|
||||
extractRequestEntity { entity =>
|
||||
complete(s"Request entity content-type is ${entity.contentType}")
|
||||
}
|
||||
|
||||
// tests:
|
||||
val httpEntity = HttpEntity(ContentTypes.`text/plain(UTF-8)`, "req")
|
||||
Post("/abc", httpEntity) ~> route ~> check {
|
||||
responseAs[String] shouldEqual s"Request entity content-type is text/plain; charset=UTF-8"
|
||||
}
|
||||
//#
|
||||
}
|
||||
"extractDataBytes-example" in {
|
||||
//#extractDataBytes-example
|
||||
val route =
|
||||
extractDataBytes { data ⇒
|
||||
val sum = data.runFold(0) { (acc, i) ⇒ acc + i.utf8String.toInt }
|
||||
onSuccess(sum) { s ⇒
|
||||
complete(HttpResponse(entity = HttpEntity(s.toString)))
|
||||
}
|
||||
}
|
||||
|
||||
// tests:
|
||||
val dataBytes = Source.fromIterator(() ⇒ Iterator.range(1, 10).map(x ⇒ ByteString(x.toString)))
|
||||
Post("/abc", HttpEntity(ContentTypes.`text/plain(UTF-8)`, data = dataBytes)) ~> route ~> check {
|
||||
responseAs[String] shouldEqual "45"
|
||||
}
|
||||
//#
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class FileAndResourceDirectivesExamplesSpec extends RoutingSpec {
|
|||
|
||||
val route =
|
||||
path("logs" / Segment) { name =>
|
||||
getFromFile(".log") // uses implicit ContentTypeResolver
|
||||
getFromFile(s"$name.log") // uses implicit ContentTypeResolver
|
||||
}
|
||||
|
||||
// tests:
|
||||
|
|
@ -32,7 +32,7 @@ class FileAndResourceDirectivesExamplesSpec extends RoutingSpec {
|
|||
|
||||
val route =
|
||||
path("logs" / Segment) { name =>
|
||||
getFromResource(".log") // uses implicit ContentTypeResolver
|
||||
getFromResource(s"$name.log") // uses implicit ContentTypeResolver
|
||||
}
|
||||
|
||||
// tests:
|
||||
|
|
@ -46,6 +46,7 @@ class FileAndResourceDirectivesExamplesSpec extends RoutingSpec {
|
|||
listDirectoryContents("/tmp")
|
||||
} ~
|
||||
path("custom") {
|
||||
// implement your custom renderer here
|
||||
val renderer = new DirectoryRenderer {
|
||||
override def marshaller(renderVanityFooter: Boolean): ToEntityMarshaller[DirectoryListing] = ???
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
|
|||
responseAs[String] shouldEqual "Client's ip is 192.168.3.12"
|
||||
}
|
||||
}
|
||||
|
||||
"rejectEmptyResponse-example" in {
|
||||
val route = rejectEmptyResponse {
|
||||
path("even" / IntNumber) { i =>
|
||||
|
|
@ -42,6 +43,7 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
|
|||
responseAs[String] shouldEqual "Number 28 is even."
|
||||
}
|
||||
}
|
||||
|
||||
"requestEntityEmptyPresent-example" in {
|
||||
val route =
|
||||
requestEntityEmpty {
|
||||
|
|
@ -59,6 +61,7 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
|
|||
responseAs[String] shouldEqual "request entity empty"
|
||||
}
|
||||
}
|
||||
|
||||
"selectPreferredLanguage-example" in {
|
||||
val request = Get() ~> `Accept-Language`(
|
||||
Language("en-US"),
|
||||
|
|
@ -78,6 +81,7 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
|
|||
}
|
||||
} ~> check { responseAs[String] shouldEqual "de-DE" }
|
||||
}
|
||||
|
||||
"validate-example" in {
|
||||
val route =
|
||||
extractUri { uri =>
|
||||
|
|
@ -94,4 +98,84 @@ class MiscDirectivesExamplesSpec extends RoutingSpec {
|
|||
rejection shouldEqual ValidationRejection("Path too long: '/abcdefghijkl'", None)
|
||||
}
|
||||
}
|
||||
|
||||
"withSizeLimit-example" in {
|
||||
val route = withSizeLimit(500) {
|
||||
entity(as[String]) { _ ⇒
|
||||
complete(HttpResponse())
|
||||
}
|
||||
}
|
||||
|
||||
// tests:
|
||||
def entityOfSize(size: Int) =
|
||||
HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size)
|
||||
|
||||
Post("/abc", entityOfSize(500)) ~> route ~> check {
|
||||
status shouldEqual StatusCodes.OK
|
||||
}
|
||||
|
||||
Post("/abc", entityOfSize(501)) ~> Route.seal(route) ~> check {
|
||||
status shouldEqual StatusCodes.BadRequest
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
"withSizeLimit-execution-moment-example" in {
|
||||
val route = withSizeLimit(500) {
|
||||
complete(HttpResponse())
|
||||
}
|
||||
|
||||
// tests:
|
||||
def entityOfSize(size: Int) =
|
||||
HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size)
|
||||
|
||||
Post("/abc", entityOfSize(500)) ~> route ~> check {
|
||||
status shouldEqual StatusCodes.OK
|
||||
}
|
||||
|
||||
Post("/abc", entityOfSize(501)) ~> route ~> check {
|
||||
status shouldEqual StatusCodes.OK
|
||||
}
|
||||
}
|
||||
|
||||
"withSizeLimit-nested-example" in {
|
||||
val route =
|
||||
withSizeLimit(500) {
|
||||
withSizeLimit(800) {
|
||||
entity(as[String]) { _ ⇒
|
||||
complete(HttpResponse())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tests:
|
||||
def entityOfSize(size: Int) =
|
||||
HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size)
|
||||
Post("/abc", entityOfSize(800)) ~> route ~> check {
|
||||
status shouldEqual StatusCodes.OK
|
||||
}
|
||||
|
||||
Post("/abc", entityOfSize(801)) ~> Route.seal(route) ~> check {
|
||||
status shouldEqual StatusCodes.BadRequest
|
||||
}
|
||||
}
|
||||
|
||||
"withoutSizeLimit-example" in {
|
||||
val route =
|
||||
withoutSizeLimit {
|
||||
entity(as[String]) { _ ⇒
|
||||
complete(HttpResponse())
|
||||
}
|
||||
}
|
||||
|
||||
// tests:
|
||||
def entityOfSize(size: Int) =
|
||||
HttpEntity(ContentTypes.`text/plain(UTF-8)`, "0" * size)
|
||||
|
||||
// will work even if you have configured akka.http.parsing.max-content-length = 500
|
||||
Post("/abc", entityOfSize(501)) ~> route ~> check {
|
||||
status shouldEqual StatusCodes.OK
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ class RouteDirectivesExamplesSpec extends RoutingSpec {
|
|||
complete(StatusCodes.OK)
|
||||
} ~
|
||||
path("c") {
|
||||
complete(StatusCodes.Created, "bar")
|
||||
complete(StatusCodes.Created -> "bar")
|
||||
} ~
|
||||
path("d") {
|
||||
complete(201, "bar")
|
||||
complete(201 -> "bar")
|
||||
} ~
|
||||
path("e") {
|
||||
complete(StatusCodes.Created, List(`Content-Type`(`text/plain(UTF-8)`)), "bar")
|
||||
|
|
|
|||
|
|
@ -5,24 +5,65 @@
|
|||
package docs.http.scaladsl.server.directives
|
||||
|
||||
import akka.http.scaladsl.model.{ HttpResponse, StatusCodes }
|
||||
import akka.http.scaladsl.server.RoutingSpec
|
||||
import akka.http.scaladsl.server.Route
|
||||
import docs.CompileOnlySpec
|
||||
|
||||
import akka.http.scaladsl.{ Http, TestUtils }
|
||||
import akka.http.scaladsl.server.Directives._
|
||||
import akka.stream.ActorMaterializer
|
||||
import akka.http.scaladsl.model.HttpEntity._
|
||||
import akka.http.scaladsl.model._
|
||||
import com.typesafe.config.{ Config, ConfigFactory }
|
||||
import org.scalatest.concurrent.ScalaFutures
|
||||
import scala.concurrent.duration._
|
||||
import scala.concurrent.{ Future, Promise }
|
||||
import akka.testkit.AkkaSpec
|
||||
|
||||
class TimeoutDirectivesExamplesSpec extends RoutingSpec with CompileOnlySpec {
|
||||
private[this] object TimeoutDirectivesTestConfig {
|
||||
val testConf: Config = ConfigFactory.parseString("""
|
||||
akka.loggers = ["akka.testkit.TestEventListener"]
|
||||
akka.loglevel = ERROR
|
||||
akka.stdout-loglevel = ERROR
|
||||
windows-connection-abort-workaround-enabled = auto
|
||||
akka.log-dead-letters = OFF
|
||||
akka.http.server.request-timeout = 1000s""")
|
||||
// large timeout - 1000s (please note - setting to infinite will disable Timeout-Access header
|
||||
// and withRequestTimeout will not work)
|
||||
}
|
||||
|
||||
class TimeoutDirectivesExamplesSpec extends AkkaSpec(TimeoutDirectivesTestConfig.testConf)
|
||||
with ScalaFutures with CompileOnlySpec {
|
||||
//#testSetup
|
||||
import system.dispatcher
|
||||
implicit val materializer = ActorMaterializer()
|
||||
|
||||
def slowFuture(): Future[String] = Promise[String].future // move to Future.never in Scala 2.12
|
||||
|
||||
def runRoute(route: Route, routePath: String): HttpResponse = {
|
||||
val (_, hostname, port) = TestUtils.temporaryServerHostnameAndPort()
|
||||
val binding = Http().bindAndHandle(route, hostname, port)
|
||||
|
||||
val response = Http().singleRequest(HttpRequest(uri = s"http://$hostname:$port/$routePath")).futureValue
|
||||
|
||||
binding.flatMap(_.unbind()).futureValue
|
||||
|
||||
response
|
||||
}
|
||||
|
||||
//#
|
||||
|
||||
"Request Timeout" should {
|
||||
"be configurable in routing layer" in compileOnlySpec {
|
||||
"be configurable in routing layer" in {
|
||||
//#withRequestTimeout-plain
|
||||
val route =
|
||||
path("timeout") {
|
||||
withRequestTimeout(3.seconds) {
|
||||
withRequestTimeout(1.seconds) { // modifies the global akka.http.server.request-timeout for this request
|
||||
val response: Future[String] = slowFuture() // very slow
|
||||
complete(response)
|
||||
}
|
||||
}
|
||||
|
||||
// check
|
||||
runRoute(route, "timeout").status should ===(StatusCodes.ServiceUnavailable) // the timeout response
|
||||
//#
|
||||
}
|
||||
"without timeout" in compileOnlySpec {
|
||||
|
|
@ -34,14 +75,16 @@ class TimeoutDirectivesExamplesSpec extends RoutingSpec with CompileOnlySpec {
|
|||
complete(response)
|
||||
}
|
||||
}
|
||||
|
||||
// no check as there is no time-out, the future would time out failing the test
|
||||
//#
|
||||
}
|
||||
|
||||
"allow mapping the response while setting the timeout" in compileOnlySpec {
|
||||
"allow mapping the response while setting the timeout" in {
|
||||
//#withRequestTimeout-with-handler
|
||||
val timeoutResponse = HttpResponse(
|
||||
StatusCodes.EnhanceYourCalm,
|
||||
entity = "Unable to serve response within time limit, please enchance your calm.")
|
||||
entity = "Unable to serve response within time limit, please enhance your calm.")
|
||||
|
||||
val route =
|
||||
path("timeout") {
|
||||
|
|
@ -51,30 +94,33 @@ class TimeoutDirectivesExamplesSpec extends RoutingSpec with CompileOnlySpec {
|
|||
complete(response)
|
||||
}
|
||||
}
|
||||
|
||||
// check
|
||||
runRoute(route, "timeout").status should ===(StatusCodes.EnhanceYourCalm) // the timeout response
|
||||
//#
|
||||
}
|
||||
|
||||
// make it compile only to avoid flaking in slow builds
|
||||
"allow mapping the response" in compileOnlySpec {
|
||||
pending // compile only spec since requires actuall Http server to be run
|
||||
|
||||
//#withRequestTimeoutResponse
|
||||
val timeoutResponse = HttpResponse(
|
||||
StatusCodes.EnhanceYourCalm,
|
||||
entity = "Unable to serve response within time limit, please enchance your calm.")
|
||||
entity = "Unable to serve response within time limit, please enhance your calm.")
|
||||
|
||||
val route =
|
||||
path("timeout") {
|
||||
withRequestTimeout(1.milli) {
|
||||
withRequestTimeout(100.milli) { // racy! for a very short timeout like 1.milli you can still get 503
|
||||
withRequestTimeoutResponse(request => timeoutResponse) {
|
||||
val response: Future[String] = slowFuture() // very slow
|
||||
complete(response)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check
|
||||
runRoute(route, "timeout").status should ===(StatusCodes.EnhanceYourCalm) // the timeout response
|
||||
//#
|
||||
}
|
||||
}
|
||||
|
||||
def slowFuture(): Future[String] = Promise[String].future
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,9 +93,12 @@ object BidiFlowDocSpec {
|
|||
}
|
||||
|
||||
override def onUpstreamFinish(): Unit = {
|
||||
// either we are done
|
||||
if (stash.isEmpty) completeStage()
|
||||
// or we still have bytes to emit
|
||||
// wait with completion and let run() complete when the
|
||||
// rest of the stash has been sent downstream
|
||||
else if (isAvailable(out)) run()
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
108
akka-docs/rst/scala/code/docs/stream/KillSwitchDocSpec.scala
Normal file
108
akka-docs/rst/scala/code/docs/stream/KillSwitchDocSpec.scala
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
package docs.stream
|
||||
|
||||
import akka.stream.scaladsl._
|
||||
import akka.stream.{ ActorMaterializer, DelayOverflowStrategy, KillSwitches }
|
||||
import akka.testkit.AkkaSpec
|
||||
import docs.CompileOnlySpec
|
||||
|
||||
import scala.concurrent.Await
|
||||
import scala.concurrent.duration._
|
||||
|
||||
class KillSwitchDocSpec extends AkkaSpec with CompileOnlySpec {
|
||||
|
||||
implicit val materializer = ActorMaterializer()
|
||||
|
||||
"Unique kill switch" must {
|
||||
|
||||
"control graph completion with shutdown" in compileOnlySpec {
|
||||
|
||||
// format: OFF
|
||||
//#unique-shutdown
|
||||
val countingSrc = Source(Stream.from(1)).delay(1.second, DelayOverflowStrategy.backpressure)
|
||||
val lastSnk = Sink.last[Int]
|
||||
|
||||
val (killSwitch, last) = countingSrc
|
||||
.viaMat(KillSwitches.single)(Keep.right)
|
||||
.toMat(lastSnk)(Keep.both)
|
||||
.run()
|
||||
|
||||
doSomethingElse()
|
||||
|
||||
killSwitch.shutdown()
|
||||
|
||||
Await.result(last, 1.second) shouldBe 2
|
||||
//#unique-shutdown
|
||||
// format: ON
|
||||
}
|
||||
|
||||
"control graph completion with abort" in compileOnlySpec {
|
||||
|
||||
// format: OFF
|
||||
//#unique-abort
|
||||
val countingSrc = Source(Stream.from(1)).delay(1.second, DelayOverflowStrategy.backpressure)
|
||||
val lastSnk = Sink.last[Int]
|
||||
|
||||
val (killSwitch, last) = countingSrc
|
||||
.viaMat(KillSwitches.single)(Keep.right)
|
||||
.toMat(lastSnk)(Keep.both).run()
|
||||
|
||||
val error = new RuntimeException("boom!")
|
||||
killSwitch.abort(error)
|
||||
|
||||
Await.result(last.failed, 1.second) shouldBe error
|
||||
//#unique-abort
|
||||
// format: ON
|
||||
}
|
||||
}
|
||||
|
||||
"Shared kill switch" must {
|
||||
|
||||
"control graph completion with shutdown" in compileOnlySpec {
|
||||
// format: OFF
|
||||
//#shared-shutdown
|
||||
val countingSrc = Source(Stream.from(1)).delay(1.second, DelayOverflowStrategy.backpressure)
|
||||
val lastSnk = Sink.last[Int]
|
||||
val sharedKillSwitch = KillSwitches.shared("my-kill-switch")
|
||||
|
||||
val last = countingSrc
|
||||
.via(sharedKillSwitch.flow)
|
||||
.runWith(lastSnk)
|
||||
|
||||
val delayedLast = countingSrc
|
||||
.delay(1.second, DelayOverflowStrategy.backpressure)
|
||||
.via(sharedKillSwitch.flow)
|
||||
.runWith(lastSnk)
|
||||
|
||||
doSomethingElse()
|
||||
|
||||
sharedKillSwitch.shutdown()
|
||||
|
||||
Await.result(last, 1.second) shouldBe 2
|
||||
Await.result(delayedLast, 1.second) shouldBe 1
|
||||
//#shared-shutdown
|
||||
// format: ON
|
||||
}
|
||||
|
||||
"control graph completion with abort" in compileOnlySpec {
|
||||
|
||||
// format: OFF
|
||||
//#shared-abort
|
||||
val countingSrc = Source(Stream.from(1)).delay(1.second)
|
||||
val lastSnk = Sink.last[Int]
|
||||
val sharedKillSwitch = KillSwitches.shared("my-kill-switch")
|
||||
|
||||
val last1 = countingSrc.via(sharedKillSwitch.flow).runWith(lastSnk)
|
||||
val last2 = countingSrc.via(sharedKillSwitch.flow).runWith(lastSnk)
|
||||
|
||||
val error = new RuntimeException("boom!")
|
||||
sharedKillSwitch.abort(error)
|
||||
|
||||
Await.result(last1.failed, 1.second) shouldBe error
|
||||
Await.result(last2.failed, 1.second) shouldBe error
|
||||
//#shared-abort
|
||||
// format: ON
|
||||
}
|
||||
}
|
||||
|
||||
private def doSomethingElse() = ???
|
||||
}
|
||||
|
|
@ -3,19 +3,22 @@
|
|||
*/
|
||||
package docs.stream
|
||||
|
||||
//#imports
|
||||
//#stream-imports
|
||||
import akka.stream._
|
||||
import akka.stream.scaladsl._
|
||||
//#imports
|
||||
//#stream-imports
|
||||
|
||||
//#other-imports
|
||||
import akka.{ NotUsed, Done }
|
||||
import akka.actor.ActorSystem
|
||||
import akka.util.ByteString
|
||||
|
||||
import org.scalatest._
|
||||
import org.scalatest.concurrent._
|
||||
import scala.concurrent._
|
||||
import scala.concurrent.duration._
|
||||
import java.nio.file.Paths
|
||||
//#other-imports
|
||||
|
||||
import org.scalatest._
|
||||
import org.scalatest.concurrent._
|
||||
|
||||
class QuickStartDocSpec extends WordSpec with BeforeAndAfterAll with ScalaFutures {
|
||||
implicit val patience = PatienceConfig(5.seconds)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue