parent
f07041091f
commit
3f8dacfd44
14 changed files with 244 additions and 9 deletions
|
|
@ -89,6 +89,7 @@ Directive Description
|
||||||
:ref:`-mapUnmatchedPath-java-` Transforms the ``unmatchedPath`` of the ``RequestContext`` using a ``Uri.Path ⇒ Uri.Path`` function
|
:ref:`-mapUnmatchedPath-java-` Transforms the ``unmatchedPath`` of the ``RequestContext`` using a ``Uri.Path ⇒ Uri.Path`` function
|
||||||
:ref:`-method-java-` Rejects all requests whose HTTP method does not match the given one
|
:ref:`-method-java-` Rejects all requests whose HTTP method does not match the given one
|
||||||
:ref:`-onComplete-java-` "Unwraps" a ``CompletionStage<T>`` and runs the inner route after future completion with the future's value as an extraction of type ``Try<T>``
|
:ref:`-onComplete-java-` "Unwraps" a ``CompletionStage<T>`` and runs the inner route after future completion with the future's value as an extraction of type ``Try<T>``
|
||||||
|
:ref:`-onCompleteWithBreaker-java-` "Unwraps" a ``CompletionStage<T>`` inside a ``CircuitBreaker`` and runs the inner route after future completion with the future's value as an extraction of type ``Try<T>``
|
||||||
:ref:`-onSuccess-java-` "Unwraps" a ``CompletionStage<T>`` and runs the inner route after future completion with the future's value as an extraction of type ``T``
|
:ref:`-onSuccess-java-` "Unwraps" a ``CompletionStage<T>`` and runs the inner route after future completion with the future's value as an extraction of type ``T``
|
||||||
:ref:`-optionalCookie-java-` Extracts the ``HttpCookiePair`` with the given name as an ``Option<HttpCookiePair>``
|
:ref:`-optionalCookie-java-` Extracts the ``HttpCookiePair`` with the given name as an ``Option<HttpCookiePair>``
|
||||||
:ref:`-optionalHeaderValue-java-` Extracts an optional HTTP header value using a given ``HttpHeader ⇒ Option<T>`` function
|
:ref:`-optionalHeaderValue-java-` Extracts an optional HTTP header value using a given ``HttpHeader ⇒ Option<T>`` function
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ Future directives can be used to run inner routes once the provided ``Future[T]`
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
onComplete
|
onComplete
|
||||||
|
onCompleteWithBreaker
|
||||||
onSuccess
|
onSuccess
|
||||||
completeOrRecoverWith
|
completeOrRecoverWith
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
.. _-onCompleteWithBreaker-java-:
|
||||||
|
|
||||||
|
onCompleteWithBreaker
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
Evaluates its parameter of type ``CompletionStage<T>`` protecting it with the specified ``CircuitBreaker``.
|
||||||
|
Refer to :ref:`Akka Circuit Breaker<circuit-breaker>` for a detailed description of this pattern.
|
||||||
|
|
||||||
|
If the ``CircuitBreaker`` is open, the request is rejected with a ``CircuitBreakerOpenRejection``.
|
||||||
|
Note that in this case the request's entity databytes stream is cancelled, and the connection is closed
|
||||||
|
as a consequence.
|
||||||
|
|
||||||
|
Otherwise, the same behaviour provided by :ref:`-onComplete-java-` is to be expected.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
TODO: Example snippets for JavaDSL are subject to community contributions! Help us complete the docs, read more about it here: `write example snippets for Akka HTTP Java DSL #20466 <https://github.com/akka/akka/issues/20466>`_.
|
||||||
|
|
@ -5,17 +5,17 @@
|
||||||
package docs.http.scaladsl.server.directives
|
package docs.http.scaladsl.server.directives
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
import akka.http.scaladsl.marshalling.ToResponseMarshallable
|
|
||||||
import docs.http.scaladsl.server.RoutingSpec
|
import docs.http.scaladsl.server.RoutingSpec
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
import scala.util.{ Success, Failure }
|
import scala.concurrent.duration._
|
||||||
import akka.http.scaladsl.server.ExceptionHandler
|
import scala.util.{Failure, Success}
|
||||||
import akka.actor.{ Actor, Props }
|
import akka.http.scaladsl.server.{CircuitBreakerOpenRejection, ExceptionHandler, Route}
|
||||||
import akka.util.Timeout
|
import akka.util.Timeout
|
||||||
import akka.http.scaladsl.model._
|
import akka.http.scaladsl.model._
|
||||||
import akka.http.scaladsl.server.Route
|
|
||||||
import StatusCodes._
|
import StatusCodes._
|
||||||
|
import akka.pattern.CircuitBreaker
|
||||||
|
|
||||||
// format: OFF
|
// format: OFF
|
||||||
|
|
||||||
|
|
@ -54,6 +54,47 @@ class FutureDirectivesExamplesSpec extends RoutingSpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"onCompleteWithBreaker" in {
|
||||||
|
def divide(a: Int, b: Int): Future[Int] = Future {
|
||||||
|
a / b
|
||||||
|
}
|
||||||
|
|
||||||
|
val resetTimeout = 1.second
|
||||||
|
val breaker = new CircuitBreaker(system.scheduler,
|
||||||
|
maxFailures = 1,
|
||||||
|
callTimeout = 5.seconds,
|
||||||
|
resetTimeout
|
||||||
|
)
|
||||||
|
|
||||||
|
val route =
|
||||||
|
path("divide" / IntNumber / IntNumber) { (a, b) =>
|
||||||
|
onCompleteWithBreaker(breaker)(divide(a, b)) {
|
||||||
|
case Success(value) => complete(s"The result was $value")
|
||||||
|
case Failure(ex) => complete((InternalServerError, s"An error occurred: ${ex.getMessage}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// tests:
|
||||||
|
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"
|
||||||
|
} // opens the circuit breaker
|
||||||
|
|
||||||
|
Get("/divide/10/2") ~> route ~> check {
|
||||||
|
rejection shouldBe a[CircuitBreakerOpenRejection]
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.sleep(resetTimeout.toMillis + 200)
|
||||||
|
|
||||||
|
Get("/divide/10/2") ~> route ~> check {
|
||||||
|
responseAs[String] shouldEqual "The result was 5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"onSuccess" in {
|
"onSuccess" in {
|
||||||
val route =
|
val route =
|
||||||
path("success") {
|
path("success") {
|
||||||
|
|
|
||||||
|
|
@ -134,6 +134,9 @@ Directive Description
|
||||||
:ref:`-method-` Rejects all requests whose HTTP method does not match the given one
|
:ref:`-method-` Rejects all requests whose HTTP method does not match the given one
|
||||||
:ref:`-onComplete-` "Unwraps" a ``Future[T]`` and runs the inner route after future completion
|
:ref:`-onComplete-` "Unwraps" a ``Future[T]`` and runs the inner route after future completion
|
||||||
with the future's value as an extraction of type ``Try[T]``
|
with the future's value as an extraction of type ``Try[T]``
|
||||||
|
:ref:`-onCompleteWithBreaker-` "Unwraps" a ``Future[T]`` inside a ``CircuitBreaker`` and runs the inner
|
||||||
|
route after future completion with the future's value as an extraction of
|
||||||
|
type ``Try[T]``
|
||||||
:ref:`-onSuccess-` "Unwraps" a ``Future[T]`` and runs the inner route after future completion
|
:ref:`-onSuccess-` "Unwraps" a ``Future[T]`` and runs the inner route after future completion
|
||||||
with the future's value as an extraction of type ``T``
|
with the future's value as an extraction of type ``T``
|
||||||
:ref:`-optionalCookie-` Extracts the ``HttpCookiePair`` with the given name as an
|
:ref:`-optionalCookie-` Extracts the ``HttpCookiePair`` with the given name as an
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ Future directives can be used to run inner routes once the provided ``Future[T]`
|
||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
onComplete
|
onComplete
|
||||||
|
onCompleteWithBreaker
|
||||||
onSuccess
|
onSuccess
|
||||||
completeOrRecoverWith
|
completeOrRecoverWith
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
.. _-onCompleteWithBreaker-:
|
||||||
|
|
||||||
|
onCompleteWithBreaker
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Signature
|
||||||
|
---------
|
||||||
|
|
||||||
|
.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/server/directives/FutureDirectives.scala
|
||||||
|
:snippet: onCompleteWithBreaker
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
Evaluates its parameter of type ``Future[T]`` protecting it with the specified ``CircuitBreaker``.
|
||||||
|
Refer to :ref:`Akka Circuit Breaker<circuit-breaker>` for a detailed description of this pattern.
|
||||||
|
|
||||||
|
If the ``CircuitBreaker`` is open, the request is rejected with a ``CircuitBreakerOpenRejection``.
|
||||||
|
Note that in this case the request's entity databytes stream is cancelled, and the connection is closed
|
||||||
|
as a consequence.
|
||||||
|
|
||||||
|
Otherwise, the same behaviour provided by :ref:`-onComplete-` is to be expected.
|
||||||
|
|
||||||
|
Example
|
||||||
|
-------
|
||||||
|
|
||||||
|
.. includecode2:: ../../../../code/docs/http/scaladsl/server/directives/FutureDirectivesExamplesSpec.scala
|
||||||
|
:snippet: onCompleteWithBreaker
|
||||||
|
|
@ -6,10 +6,15 @@ package akka.http.scaladsl.server
|
||||||
package directives
|
package directives
|
||||||
|
|
||||||
import akka.http.scaladsl.model.StatusCodes
|
import akka.http.scaladsl.model.StatusCodes
|
||||||
|
import akka.pattern.{ CircuitBreaker, CircuitBreakerOpenException }
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
import akka.testkit.EventFilter
|
import akka.testkit.EventFilter
|
||||||
|
import org.scalatest.Inside
|
||||||
|
|
||||||
class FutureDirectivesSpec extends RoutingSpec {
|
import scala.concurrent.duration._
|
||||||
|
|
||||||
|
class FutureDirectivesSpec extends RoutingSpec with Inside {
|
||||||
|
|
||||||
class TestException(msg: String) extends Exception(msg)
|
class TestException(msg: String) extends Exception(msg)
|
||||||
object TestException extends Exception("XXX")
|
object TestException extends Exception("XXX")
|
||||||
|
|
@ -19,6 +24,12 @@ class FutureDirectivesSpec extends RoutingSpec {
|
||||||
case e: TestException ⇒ complete((StatusCodes.InternalServerError, "Oops. " + e))
|
case e: TestException ⇒ complete((StatusCodes.InternalServerError, "Oops. " + e))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait TestWithCircuitBreaker {
|
||||||
|
val breakerResetTimeout = 500.millis
|
||||||
|
val breaker = new CircuitBreaker(system.scheduler, maxFailures = 1, callTimeout = 10.seconds, breakerResetTimeout)
|
||||||
|
def openBreaker() = breaker.withCircuitBreaker(Future.failed(new Exception("boom")))
|
||||||
|
}
|
||||||
|
|
||||||
"The `onComplete` directive" should {
|
"The `onComplete` directive" should {
|
||||||
"unwrap a Future in the success case" in {
|
"unwrap a Future in the success case" in {
|
||||||
var i = 0
|
var i = 0
|
||||||
|
|
@ -50,6 +61,52 @@ class FutureDirectivesSpec extends RoutingSpec {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
"The `onCompleteWithBreaker` directive" should {
|
||||||
|
"unwrap a Future in the success case" in new TestWithCircuitBreaker {
|
||||||
|
var i = 0
|
||||||
|
def nextNumber() = { i += 1; i }
|
||||||
|
val route = onCompleteWithBreaker(breaker)(Future.successful(nextNumber())) { echoComplete }
|
||||||
|
Get() ~> route ~> check {
|
||||||
|
responseAs[String] shouldEqual "Success(1)"
|
||||||
|
}
|
||||||
|
Get() ~> route ~> check {
|
||||||
|
responseAs[String] shouldEqual "Success(2)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"unwrap a Future in the failure case" in new TestWithCircuitBreaker {
|
||||||
|
Get() ~> onCompleteWithBreaker(breaker)(Future.failed[String](new RuntimeException("no"))) { echoComplete } ~> check {
|
||||||
|
responseAs[String] shouldEqual "Failure(java.lang.RuntimeException: no)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"fail fast if the circuit breaker is open" in new TestWithCircuitBreaker {
|
||||||
|
openBreaker()
|
||||||
|
Get() ~> onCompleteWithBreaker(breaker)(Future.successful(1)) { echoComplete } ~> check {
|
||||||
|
inside(rejection) {
|
||||||
|
case CircuitBreakerOpenRejection(_) ⇒
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"stop failing fast when the circuit breaker closes" in new TestWithCircuitBreaker {
|
||||||
|
openBreaker()
|
||||||
|
Thread.sleep(breakerResetTimeout.toMillis + 200)
|
||||||
|
Get() ~> onCompleteWithBreaker(breaker)(Future.successful(1)) { echoComplete } ~> check {
|
||||||
|
responseAs[String] shouldEqual "Success(1)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"catch an exception in the success case" in new TestWithCircuitBreaker {
|
||||||
|
Get() ~> onCompleteWithBreaker(breaker)(Future.successful("ok")) { throwTestException("EX when ") } ~> check {
|
||||||
|
status shouldEqual StatusCodes.InternalServerError
|
||||||
|
responseAs[String] shouldEqual s"Oops. akka.http.scaladsl.server.directives.FutureDirectivesSpec$$TestException: EX when Success(ok)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"catch an exception in the failure case" in new TestWithCircuitBreaker {
|
||||||
|
Get() ~> onCompleteWithBreaker(breaker)(Future.failed[String](new RuntimeException("no"))) { throwTestException("EX when ") } ~> check {
|
||||||
|
status shouldEqual StatusCodes.InternalServerError
|
||||||
|
responseAs[String] shouldEqual s"Oops. akka.http.scaladsl.server.directives.FutureDirectivesSpec$$TestException: EX when Failure(java.lang.RuntimeException: no)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
"The `onSuccess` directive" should {
|
"The `onSuccess` directive" should {
|
||||||
"unwrap a Future in the success case" in {
|
"unwrap a Future in the success case" in {
|
||||||
Get() ~> onSuccess(Future.successful("yes")) { echoComplete } ~> check {
|
Get() ~> onSuccess(Future.successful("yes")) { echoComplete } ~> check {
|
||||||
|
|
|
||||||
|
|
@ -56,4 +56,15 @@ class RejectionHandlerBuilder(asScala: server.RejectionHandler.Builder) {
|
||||||
asScala.handleNotFound(route.delegate)
|
asScala.handleNotFound(route.delegate)
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method for handling rejections created by created by the onCompleteWithBreaker directive.
|
||||||
|
* Signals that the request was rejected because the supplied circuit breaker is open and requests are failing fast.
|
||||||
|
*
|
||||||
|
* Use to customise the error response being written instead of the default [[akka.http.javadsl.model.StatusCodes.SERVICE_UNAVAILABLE]] response.
|
||||||
|
*/
|
||||||
|
def handleCircuitBreakerOpenRejection(handler: function.Function[CircuitBreakerOpenRejection, Route]): RejectionHandlerBuilder = {
|
||||||
|
asScala.handleCircuitBreakerOpenRejection(t => handler.apply(t).delegate)
|
||||||
|
this
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,8 +16,10 @@ import akka.http.javadsl.model.headers.HttpChallenge
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
import java.util.function.{ Function ⇒ JFunction }
|
import java.util.function.{ Function ⇒ JFunction }
|
||||||
import java.lang.{ Iterable ⇒ JIterable }
|
import java.lang.{ Iterable ⇒ JIterable }
|
||||||
|
|
||||||
import akka.http.scaladsl
|
import akka.http.scaladsl
|
||||||
import akka.japi.Util
|
import akka.japi.Util
|
||||||
|
import akka.pattern.CircuitBreakerOpenException
|
||||||
|
|
||||||
import scala.compat.java8.OptionConverters._
|
import scala.compat.java8.OptionConverters._
|
||||||
import scala.collection.immutable
|
import scala.collection.immutable
|
||||||
|
|
@ -250,6 +252,14 @@ trait ValidationRejection extends Rejection {
|
||||||
def getCause: Optional[Throwable]
|
def getCause: Optional[Throwable]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rejection created by the `onCompleteWithBreaker` directive.
|
||||||
|
* Signals that the request was rejected because the supplied circuit breaker is open and requests are failing fast.
|
||||||
|
*/
|
||||||
|
trait CircuitBreakerOpenRejection extends Rejection {
|
||||||
|
def cause: CircuitBreakerOpenException
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A special Rejection that serves as a container for a transformation function on rejections.
|
* A special Rejection that serves as a container for a transformation function on rejections.
|
||||||
* It is used by some directives to "cancel" rejections that are added by later directives of a similar type.
|
* It is used by some directives to "cancel" rejections that are added by later directives of a similar type.
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import scala.concurrent.ExecutionContext.Implicits.global
|
||||||
import scala.util.Try
|
import scala.util.Try
|
||||||
import akka.http.javadsl.server.{ Marshaller, Route }
|
import akka.http.javadsl.server.{ Marshaller, Route }
|
||||||
import akka.http.scaladsl.server.directives.{ CompleteOrRecoverWithMagnet, FutureDirectives ⇒ D }
|
import akka.http.scaladsl.server.directives.{ CompleteOrRecoverWithMagnet, FutureDirectives ⇒ D }
|
||||||
|
import akka.pattern.CircuitBreaker
|
||||||
|
|
||||||
abstract class FutureDirectives extends FormFieldDirectives {
|
abstract class FutureDirectives extends FormFieldDirectives {
|
||||||
|
|
||||||
|
|
@ -28,6 +29,22 @@ abstract class FutureDirectives extends FormFieldDirectives {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Unwraps" a `CompletionStage[T]` and runs the inner route after future
|
||||||
|
* completion with the future's value as an extraction of type `T` if
|
||||||
|
* the supplied `CircuitBreaker` is closed.
|
||||||
|
*
|
||||||
|
* If the supplied [[CircuitBreaker]] is open the request is rejected
|
||||||
|
* with a [[akka.http.javadsl.server.CircuitBreakerOpenRejection]].
|
||||||
|
*
|
||||||
|
* @group future
|
||||||
|
*/
|
||||||
|
def onCompleteWithBreaker[T](breaker: CircuitBreaker, f: Supplier[CompletionStage[T]], inner: JFunction[Try[T], Route]) = RouteAdapter {
|
||||||
|
D.onCompleteWithBreaker(breaker)(f.get.toScala.recover(unwrapCompletionException)) { value ⇒
|
||||||
|
inner(value).delegate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Unwraps" a `CompletionStage<T>` and runs the inner route after stage
|
* "Unwraps" a `CompletionStage<T>` and runs the inner route after stage
|
||||||
* completion with the stage's value as an extraction of type `T`.
|
* completion with the stage's value as an extraction of type `T`.
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import akka.http.impl.util.JavaMapping._
|
||||||
import akka.http.impl.util.JavaMapping.Implicits._
|
import akka.http.impl.util.JavaMapping.Implicits._
|
||||||
import akka.http.javadsl.server.RoutingJavaMapping
|
import akka.http.javadsl.server.RoutingJavaMapping
|
||||||
import RoutingJavaMapping._
|
import RoutingJavaMapping._
|
||||||
|
import akka.pattern.CircuitBreakerOpenException
|
||||||
|
|
||||||
import scala.collection.JavaConverters._
|
import scala.collection.JavaConverters._
|
||||||
import scala.compat.java8.OptionConverters
|
import scala.compat.java8.OptionConverters
|
||||||
|
|
@ -256,6 +257,13 @@ final case class TransformationRejection(transform: immutable.Seq[Rejection] ⇒
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rejection created by the `onCompleteWithBreaker` directive.
|
||||||
|
* Signals that the request was rejected because the supplied circuit breaker is open and requests are failing fast.
|
||||||
|
*/
|
||||||
|
final case class CircuitBreakerOpenRejection(cause: CircuitBreakerOpenException)
|
||||||
|
extends jserver.CircuitBreakerOpenRejection with Rejection
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Throwable wrapping a Rejection.
|
* A Throwable wrapping a Rejection.
|
||||||
* Can be used for marshalling `Future[T]` or `Try[T]` instances, whose failure side is supposed to trigger a route
|
* Can be used for marshalling `Future[T]` or `Try[T]` instances, whose failure side is supposed to trigger a route
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,15 @@ object RejectionHandler {
|
||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method for handling rejections created by created by the onCompleteWithBreaker directive.
|
||||||
|
* Signals that the request was rejected because the supplied circuit breaker is open and requests are failing fast.
|
||||||
|
*
|
||||||
|
* Use to customise the error response being written instead of the default [[ServiceUnavailable]] response.
|
||||||
|
*/
|
||||||
|
def handleCircuitBreakerOpenRejection(handler: CircuitBreakerOpenRejection => Route): this.type =
|
||||||
|
handle { case r: CircuitBreakerOpenRejection => handler(r) }
|
||||||
|
|
||||||
def result(): RejectionHandler =
|
def result(): RejectionHandler =
|
||||||
new BuiltRejectionHandler(cases.result(), notFound, isDefault)
|
new BuiltRejectionHandler(cases.result(), notFound, isDefault)
|
||||||
}
|
}
|
||||||
|
|
@ -165,7 +174,11 @@ object RejectionHandler {
|
||||||
}
|
}
|
||||||
.handle {
|
.handle {
|
||||||
case TooManyRangesRejection(_) ⇒
|
case TooManyRangesRejection(_) ⇒
|
||||||
complete((RequestedRangeNotSatisfiable, "Request contains too many ranges."))
|
complete((RequestedRangeNotSatisfiable, "Request contains too many ranges"))
|
||||||
|
}
|
||||||
|
.handle {
|
||||||
|
case CircuitBreakerOpenRejection(_) ⇒
|
||||||
|
complete(ServiceUnavailable)
|
||||||
}
|
}
|
||||||
.handle {
|
.handle {
|
||||||
case UnsatisfiableRangeRejection(unsatisfiableRanges, actualEntityLength) ⇒
|
case UnsatisfiableRangeRejection(unsatisfiableRanges, actualEntityLength) ⇒
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,15 @@
|
||||||
package akka.http.scaladsl.server
|
package akka.http.scaladsl.server
|
||||||
package directives
|
package directives
|
||||||
|
|
||||||
import scala.concurrent.Future
|
|
||||||
import scala.util.{ Failure, Success, Try }
|
|
||||||
import akka.http.scaladsl.marshalling.ToResponseMarshaller
|
import akka.http.scaladsl.marshalling.ToResponseMarshaller
|
||||||
|
import akka.http.scaladsl.server.Directives._
|
||||||
import akka.http.scaladsl.server.util.Tupler
|
import akka.http.scaladsl.server.util.Tupler
|
||||||
import akka.http.scaladsl.util.FastFuture._
|
import akka.http.scaladsl.util.FastFuture._
|
||||||
|
import akka.pattern.{ CircuitBreaker, CircuitBreakerOpenException }
|
||||||
|
import akka.stream.scaladsl.Sink
|
||||||
|
|
||||||
|
import scala.concurrent.Future
|
||||||
|
import scala.util.{ Failure, Success, Try }
|
||||||
|
|
||||||
// format: OFF
|
// format: OFF
|
||||||
|
|
||||||
|
|
@ -19,6 +23,8 @@ import akka.http.scaladsl.util.FastFuture._
|
||||||
*/
|
*/
|
||||||
trait FutureDirectives {
|
trait FutureDirectives {
|
||||||
|
|
||||||
|
import RouteDirectives._
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Unwraps" a `Future[T]` and runs the inner route after future
|
* "Unwraps" a `Future[T]` and runs the inner route after future
|
||||||
* completion with the future's value as an extraction of type `Try[T]`.
|
* completion with the future's value as an extraction of type `Try[T]`.
|
||||||
|
|
@ -31,6 +37,26 @@ trait FutureDirectives {
|
||||||
future.fast.transformWith(t ⇒ inner(Tuple1(t))(ctx))
|
future.fast.transformWith(t ⇒ inner(Tuple1(t))(ctx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Unwraps" a `Future[T]` and runs the inner route after future
|
||||||
|
* completion with the future's value as an extraction of type `T` if
|
||||||
|
* the supplied `CircuitBreaker` is closed.
|
||||||
|
*
|
||||||
|
* If the supplied [[CircuitBreaker]] is open the request is rejected
|
||||||
|
* with a [[CircuitBreakerOpenRejection]].
|
||||||
|
*
|
||||||
|
* @group future
|
||||||
|
*/
|
||||||
|
def onCompleteWithBreaker[T](breaker: CircuitBreaker)(future: ⇒ Future[T]): Directive1[Try[T]] =
|
||||||
|
onComplete(breaker.withCircuitBreaker(future)).flatMap {
|
||||||
|
case Failure(ex: CircuitBreakerOpenException) ⇒
|
||||||
|
extractRequestContext.flatMap { ctx ⇒
|
||||||
|
ctx.request.entity.dataBytes.runWith(Sink.cancelled)(ctx.materializer)
|
||||||
|
reject(CircuitBreakerOpenRejection(ex))
|
||||||
|
}
|
||||||
|
case x ⇒ provide(x)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* "Unwraps" a `Future[T]` and runs the inner route after future
|
* "Unwraps" a `Future[T]` and runs the inner route after future
|
||||||
* completion with the future's value as an extraction of type `T`.
|
* completion with the future's value as an extraction of type `T`.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue