+doc,htc #18600,18597 documents where/how to deal with failure in Http
This commit is contained in:
parent
2f2e07666e
commit
573a69e2b9
20 changed files with 532 additions and 60 deletions
|
|
@ -1,10 +1,10 @@
|
||||||
.. _stream-java-api:
|
.. _stream-java-api:
|
||||||
|
|
||||||
Java Documentation
|
Java Documentation
|
||||||
===================
|
==================
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 3
|
||||||
|
|
||||||
java/stream-index
|
java/stream-index
|
||||||
java/http/index
|
java/http/index
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
package docs.http.javadsl.server;
|
||||||
|
|
||||||
|
//#binding-failure-high-level-example
|
||||||
|
import akka.actor.ActorSystem;
|
||||||
|
import akka.dispatch.OnFailure;
|
||||||
|
import akka.http.javadsl.model.ContentTypes;
|
||||||
|
import akka.http.javadsl.server.*;
|
||||||
|
import akka.http.javadsl.server.values.Parameters;
|
||||||
|
import akka.http.scaladsl.Http;
|
||||||
|
import scala.concurrent.Future;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public class HighLevelServerBindFailureExample {
|
||||||
|
public static void main(String[] args) throws IOException {
|
||||||
|
// boot up server using the route as defined below
|
||||||
|
final ActorSystem system = ActorSystem.create();
|
||||||
|
|
||||||
|
// HttpApp.bindRoute expects a route being provided by HttpApp.createRoute
|
||||||
|
Future<Http.ServerBinding> bindingFuture =
|
||||||
|
new HighLevelServerExample().bindRoute("localhost", 8080, system);
|
||||||
|
|
||||||
|
bindingFuture.onFailure(new OnFailure() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable failure) throws Throwable {
|
||||||
|
System.err.println("Something very bad happened! " + failure.getMessage());
|
||||||
|
system.shutdown();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
system.shutdown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//#binding-failure-high-level-example
|
||||||
|
|
@ -5,26 +5,43 @@
|
||||||
package docs.http.javadsl.server;
|
package docs.http.javadsl.server;
|
||||||
|
|
||||||
import akka.actor.ActorSystem;
|
import akka.actor.ActorSystem;
|
||||||
|
import akka.dispatch.OnFailure;
|
||||||
|
import akka.http.impl.util.JavaMapping;
|
||||||
import akka.http.impl.util.Util;
|
import akka.http.impl.util.Util;
|
||||||
import akka.http.javadsl.Http;
|
import akka.http.javadsl.Http;
|
||||||
import akka.http.javadsl.IncomingConnection;
|
import akka.http.javadsl.IncomingConnection;
|
||||||
import akka.http.javadsl.ServerBinding;
|
import akka.http.javadsl.ServerBinding;
|
||||||
import akka.http.javadsl.model.*;
|
import akka.http.javadsl.model.*;
|
||||||
|
import akka.http.javadsl.model.ContentTypes;
|
||||||
|
import akka.http.javadsl.model.HttpRequest;
|
||||||
|
import akka.http.javadsl.model.HttpResponse;
|
||||||
|
import akka.http.javadsl.model.Uri;
|
||||||
|
import akka.japi.JavaPartialFunction;
|
||||||
import akka.japi.function.Function;
|
import akka.japi.function.Function;
|
||||||
import akka.japi.function.Procedure;
|
import akka.japi.function.Procedure;
|
||||||
import akka.stream.ActorMaterializer;
|
import akka.stream.ActorMaterializer;
|
||||||
import akka.stream.Materializer;
|
import akka.stream.Materializer;
|
||||||
|
import akka.stream.javadsl.Flow;
|
||||||
import akka.stream.javadsl.Sink;
|
import akka.stream.javadsl.Sink;
|
||||||
import akka.stream.javadsl.Source;
|
import akka.stream.javadsl.Source;
|
||||||
|
import akka.stream.stage.Context;
|
||||||
|
import akka.stream.stage.PushStage;
|
||||||
|
import akka.stream.stage.SyncDirective;
|
||||||
|
import akka.stream.stage.TerminationDirective;
|
||||||
|
import akka.util.ByteString;
|
||||||
|
import scala.Function1;
|
||||||
import scala.concurrent.Await;
|
import scala.concurrent.Await;
|
||||||
import scala.concurrent.Future;
|
import scala.concurrent.Future;
|
||||||
import scala.concurrent.duration.FiniteDuration;
|
import scala.concurrent.duration.FiniteDuration;
|
||||||
|
import scala.runtime.BoxedUnit;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class HttpServerExampleDocTest {
|
public class HttpServerExampleDocTest {
|
||||||
|
|
||||||
public static void bindingExample() throws Exception {
|
public static void bindingExample() throws Exception {
|
||||||
//#binding-example
|
//#binding-example
|
||||||
ActorSystem system = ActorSystem.create();
|
ActorSystem system = ActorSystem.create();
|
||||||
|
|
@ -45,6 +62,116 @@ public class HttpServerExampleDocTest {
|
||||||
//#binding-example
|
//#binding-example
|
||||||
Await.result(serverBindingFuture, new FiniteDuration(3, TimeUnit.SECONDS));
|
Await.result(serverBindingFuture, new FiniteDuration(3, TimeUnit.SECONDS));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void bindingFailureExample() throws Exception {
|
||||||
|
//#binding-failure-handling
|
||||||
|
ActorSystem system = ActorSystem.create();
|
||||||
|
Materializer materializer = ActorMaterializer.create(system);
|
||||||
|
|
||||||
|
Source<IncomingConnection, Future<ServerBinding>> serverSource =
|
||||||
|
Http.get(system).bind("localhost", 80, materializer);
|
||||||
|
|
||||||
|
Future<ServerBinding> serverBindingFuture =
|
||||||
|
serverSource.to(Sink.foreach(
|
||||||
|
new Procedure<IncomingConnection>() {
|
||||||
|
@Override
|
||||||
|
public void apply(IncomingConnection connection) throws Exception {
|
||||||
|
System.out.println("Accepted new connection from " + connection.remoteAddress());
|
||||||
|
// ... and then actually handle the connection
|
||||||
|
}
|
||||||
|
})).run(materializer);
|
||||||
|
|
||||||
|
serverBindingFuture.onFailure(new OnFailure() {
|
||||||
|
@Override
|
||||||
|
public void onFailure(Throwable failure) throws Throwable {
|
||||||
|
// possibly report the failure somewhere...
|
||||||
|
}
|
||||||
|
}, system.dispatcher());
|
||||||
|
//#binding-failure-handling
|
||||||
|
Await.result(serverBindingFuture, new FiniteDuration(3, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void connectionSourceFailureExample() throws Exception {
|
||||||
|
//#incoming-connections-source-failure-handling
|
||||||
|
ActorSystem system = ActorSystem.create();
|
||||||
|
Materializer materializer = ActorMaterializer.create(system);
|
||||||
|
|
||||||
|
Source<IncomingConnection, Future<ServerBinding>> serverSource =
|
||||||
|
Http.get(system).bind("localhost", 8080, materializer);
|
||||||
|
|
||||||
|
Flow<IncomingConnection, IncomingConnection, BoxedUnit> failureDetection =
|
||||||
|
Flow.of(IncomingConnection.class).transform(() ->
|
||||||
|
new PushStage<IncomingConnection, IncomingConnection>() {
|
||||||
|
@Override
|
||||||
|
public SyncDirective onPush(IncomingConnection elem, Context<IncomingConnection> ctx) {
|
||||||
|
return ctx.push(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TerminationDirective onUpstreamFailure(Throwable cause, Context<IncomingConnection> ctx) {
|
||||||
|
// signal the failure to external monitoring service!
|
||||||
|
return super.onUpstreamFailure(cause, ctx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<ServerBinding> serverBindingFuture =
|
||||||
|
serverSource
|
||||||
|
.via(failureDetection) // feed signals through our custom stage
|
||||||
|
.to(Sink.foreach(
|
||||||
|
new Procedure<IncomingConnection>() {
|
||||||
|
@Override
|
||||||
|
public void apply(IncomingConnection connection) throws Exception {
|
||||||
|
System.out.println("Accepted new connection from " + connection.remoteAddress());
|
||||||
|
// ... and then actually handle the connection
|
||||||
|
}
|
||||||
|
})).run(materializer);
|
||||||
|
//#incoming-connections-source-failure-handling
|
||||||
|
Await.result(serverBindingFuture, new FiniteDuration(3, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void connectionStreamFailureExample() throws Exception {
|
||||||
|
//#connection-stream-failure-handling
|
||||||
|
ActorSystem system = ActorSystem.create();
|
||||||
|
Materializer materializer = ActorMaterializer.create(system);
|
||||||
|
|
||||||
|
Source<IncomingConnection, Future<ServerBinding>> serverSource =
|
||||||
|
Http.get(system).bind("localhost", 8080, materializer);
|
||||||
|
|
||||||
|
Flow<HttpRequest, HttpRequest, BoxedUnit> failureDetection =
|
||||||
|
Flow.of(HttpRequest.class).transform(() ->
|
||||||
|
new PushStage<HttpRequest, HttpRequest>() {
|
||||||
|
@Override
|
||||||
|
public SyncDirective onPush(HttpRequest elem, Context<HttpRequest> ctx) {
|
||||||
|
return ctx.push(elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TerminationDirective onUpstreamFailure(Throwable cause, Context<HttpRequest> ctx) {
|
||||||
|
// signal the failure to external monitoring service!
|
||||||
|
return super.onUpstreamFailure(cause, ctx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Flow<HttpRequest, HttpResponse, BoxedUnit> httpEcho =
|
||||||
|
Flow.of(HttpRequest.class)
|
||||||
|
.via(failureDetection)
|
||||||
|
.map(request -> {
|
||||||
|
Source<ByteString, ?> bytes = request.entity().getDataBytes();
|
||||||
|
HttpEntity entity = HttpEntities.create(ContentTypes.TEXT_PLAIN, (Source<ByteString, Object>) bytes);
|
||||||
|
return HttpResponse.create()
|
||||||
|
.withEntity(entity);
|
||||||
|
});
|
||||||
|
|
||||||
|
Future<ServerBinding> serverBindingFuture =
|
||||||
|
serverSource.to(Sink.foreach(con -> {
|
||||||
|
System.out.println("Accepted new connection from " + con.remoteAddress());
|
||||||
|
con.handleWith(httpEcho, materializer);
|
||||||
|
}
|
||||||
|
)).run(materializer);
|
||||||
|
//#connection-stream-failure-handling
|
||||||
|
Await.result(serverBindingFuture, new FiniteDuration(3, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
public static void fullServerExample() throws Exception {
|
public static void fullServerExample() throws Exception {
|
||||||
//#full-server-example
|
//#full-server-example
|
||||||
ActorSystem system = ActorSystem.create();
|
ActorSystem system = ActorSystem.create();
|
||||||
|
|
|
||||||
|
|
@ -42,11 +42,11 @@ HttpRequest
|
||||||
|
|
||||||
An ``HttpRequest`` consists of
|
An ``HttpRequest`` consists of
|
||||||
|
|
||||||
- a method (GET, POST, etc.)
|
- a method (GET, POST, etc.)
|
||||||
- a URI
|
- a URI
|
||||||
- a seq of headers
|
- a seq of headers
|
||||||
- an entity (body data)
|
- an entity (body data)
|
||||||
- a protocol
|
- a protocol
|
||||||
|
|
||||||
Here are some examples how to construct an ``HttpRequest``:
|
Here are some examples how to construct an ``HttpRequest``:
|
||||||
|
|
||||||
|
|
@ -64,10 +64,10 @@ HttpResponse
|
||||||
|
|
||||||
An ``HttpResponse`` consists of
|
An ``HttpResponse`` consists of
|
||||||
|
|
||||||
- a status code
|
- a status code
|
||||||
- a list of headers
|
- a list of headers
|
||||||
- an entity (body data)
|
- an entity (body data)
|
||||||
- a protocol
|
- a protocol
|
||||||
|
|
||||||
Here are some examples how to construct an ``HttpResponse``:
|
Here are some examples how to construct an ``HttpResponse``:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,11 +31,11 @@ akka-http-jackson
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
configuration
|
|
||||||
http-model
|
http-model
|
||||||
server-side/low-level-server-side-api
|
server-side/low-level-server-side-api
|
||||||
server-side/websocket-support
|
server-side/websocket-support
|
||||||
routing-dsl/index
|
routing-dsl/index
|
||||||
client-side/index
|
client-side/index
|
||||||
|
configuration
|
||||||
|
|
||||||
.. _jackson: https://github.com/FasterXML/jackson
|
.. _jackson: https://github.com/FasterXML/jackson
|
||||||
|
|
@ -6,10 +6,10 @@ Directives
|
||||||
A directive is a wrapper for a route or a list of alternative routes that adds one or more of the following
|
A directive is a wrapper for a route or a list of alternative routes that adds one or more of the following
|
||||||
functionality to its nested route(s):
|
functionality to its nested route(s):
|
||||||
|
|
||||||
* it filters the request and lets only matching requests pass (e.g. the `get` directive lets only GET-requests pass)
|
* it filters the request and lets only matching requests pass (e.g. the `get` directive lets only GET-requests pass)
|
||||||
* it modifies the request or the ``RequestContext`` (e.g. the `path` directives filters on the unmatched path and then
|
* it modifies the request or the ``RequestContext`` (e.g. the `path` directives filters on the unmatched path and then
|
||||||
passes an updated ``RequestContext`` unmatched path)
|
passes an updated ``RequestContext`` unmatched path)
|
||||||
* it modifies the response coming out of the nested route
|
* it modifies the response coming out of the nested route
|
||||||
|
|
||||||
akka-http provides a set of predefined directives for various tasks. You can access them by either extending from
|
akka-http provides a set of predefined directives for various tasks. You can access them by either extending from
|
||||||
``akka.http.javadsl.server.AllDirectives`` or by importing them statically with
|
``akka.http.javadsl.server.AllDirectives`` or by importing them statically with
|
||||||
|
|
@ -46,7 +46,7 @@ MethodDirectives
|
||||||
MiscDirectives
|
MiscDirectives
|
||||||
Contains directives that validate a request by user-defined logic.
|
Contains directives that validate a request by user-defined logic.
|
||||||
|
|
||||||
:ref:`PathDirectives-java`
|
:ref:`path-directives-java`
|
||||||
Contains directives to match and filter on the URI path of the incoming request.
|
Contains directives to match and filter on the URI path of the incoming request.
|
||||||
|
|
||||||
RangeDirectives
|
RangeDirectives
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
.. _PathDirectives-java:
|
.. _path-directives-java:
|
||||||
|
|
||||||
PathDirectives
|
PathDirectives
|
||||||
==============
|
==============
|
||||||
|
|
|
||||||
|
|
@ -74,3 +74,27 @@ quickly without running them over the network and helps with writing assertions
|
||||||
Read more about :ref:`http-testkit-java`.
|
Read more about :ref:`http-testkit-java`.
|
||||||
|
|
||||||
.. _DRY: http://en.wikipedia.org/wiki/Don%27t_repeat_yourself
|
.. _DRY: http://en.wikipedia.org/wiki/Don%27t_repeat_yourself
|
||||||
|
|
||||||
|
.. _handling-http-server-failures-high-level-scala:
|
||||||
|
|
||||||
|
Handling HTTP Server failures in the High-Level API
|
||||||
|
---------------------------------------------------
|
||||||
|
There are various situations when failure may occur while initialising or running an Akka HTTP server.
|
||||||
|
Akka by default will log all these failures, however sometimes one may want to react to failures in addition
|
||||||
|
to them just being logged, for example by shutting down the actor system, or notifying some external monitoring
|
||||||
|
end-point explicitly.
|
||||||
|
|
||||||
|
Bind failures
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
For example the server might be unable to bind to the given port. For example when the port
|
||||||
|
is already taken by another application, or if the port is privileged (i.e. only usable by ``root``).
|
||||||
|
In this case the "binding future" will fail immediatly, and we can react to if by listening on the Future's completion:
|
||||||
|
|
||||||
|
.. includecode:: ../../code/docs/http/javadsl/server/HighLevelServerBindFailureExample.java
|
||||||
|
:include: binding-failure-high-level-example
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
For a more low-level overview of the kinds of failures that can happen and also more fine-grained control over them
|
||||||
|
refer to the :ref:`handling-http-server-failures-low-level-java` documentation.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -93,9 +93,9 @@ this connection.
|
||||||
|
|
||||||
Requests are handled by calling one of the ``handleWithXXX`` methods with a handler, which can either be
|
Requests are handled by calling one of the ``handleWithXXX`` methods with a handler, which can either be
|
||||||
|
|
||||||
- a ``Flow<HttpRequest, HttpResponse, ?>`` for ``handleWith``,
|
- a ``Flow<HttpRequest, HttpResponse, ?>`` for ``handleWith``,
|
||||||
- a function ``Function<HttpRequest, HttpResponse>`` for ``handleWithSyncHandler``,
|
- a function ``Function<HttpRequest, HttpResponse>`` for ``handleWithSyncHandler``,
|
||||||
- a function ``Function<HttpRequest, Future<HttpResponse>>`` for ``handleWithAsyncHandler``.
|
- a function ``Function<HttpRequest, Future<HttpResponse>>`` for ``handleWithAsyncHandler``.
|
||||||
|
|
||||||
Here is a complete example:
|
Here is a complete example:
|
||||||
|
|
||||||
|
|
@ -165,4 +165,64 @@ that is a stage that "upgrades" a potentially encrypted raw connection to the HT
|
||||||
|
|
||||||
You create an instance of the layer by calling one of the two overloads of the ``Http.get(system).serverLayer`` method,
|
You create an instance of the layer by calling one of the two overloads of the ``Http.get(system).serverLayer`` method,
|
||||||
which also allows for varying degrees of configuration. Note, that the returned instance is not reusable and can only
|
which also allows for varying degrees of configuration. Note, that the returned instance is not reusable and can only
|
||||||
be materialized once.
|
be materialized once.
|
||||||
|
|
||||||
|
.. _handling-http-server-failures-low-level-java:
|
||||||
|
|
||||||
|
Handling HTTP Server failures in the Low-Level API
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
There are various situations when failure may occur while initialising or running an Akka HTTP server.
|
||||||
|
Akka by default will log all these failures, however sometimes one may want to react to failures in addition to them
|
||||||
|
just being logged, for example by shutting down the actor system, or notifying some external monitoring end-point explicitly.
|
||||||
|
|
||||||
|
There are multiple things that can fail when creating and materializing an HTTP Server (similarily, the same applied to
|
||||||
|
a plain streaming ``Tcp`` server). The types of failures that can happen on different layers of the stack, starting
|
||||||
|
from being unable to start the server, and ending with failing to unmarshal an HttpRequest, examples of failures include
|
||||||
|
(from outer-most, to inner-most):
|
||||||
|
|
||||||
|
- Failure to ``bind`` to the specified address/port,
|
||||||
|
- Failure while accepting new ``IncommingConnection`` s, for example when the OS has run out of file descriptors or memory,
|
||||||
|
- Failure while handling a connection, for example if the incoming ``HttpRequest`` is malformed.
|
||||||
|
|
||||||
|
This section describes how to handle each failure situation, and in which situations these failures may occur.
|
||||||
|
|
||||||
|
Bind failures
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The first type of failure is when the server is unable to bind to the given port. For example when the port
|
||||||
|
is already taken by another application, or if the port is privileged (i.e. only usable by ``root``).
|
||||||
|
In this case the "binding future" will fail immediatly, and we can react to if by listening on the Future's completion:
|
||||||
|
|
||||||
|
.. includecode:: ../../code/docs/http/javadsl/server/HttpServerExampleDocTest.java
|
||||||
|
:include: binding-failure-handling
|
||||||
|
|
||||||
|
Once the server has successfully bound to a port, the ``Source<IncomingConnection, ?>`` starts running and emiting
|
||||||
|
new incoming connections. This source technically can signal a failure as well, however this should only happen in very
|
||||||
|
dramantic situations such as running out of file descriptors or memory available to the system, such that it's not able
|
||||||
|
to accept a new incoming connection. Handling failures in Akka Streams is pretty stright forward, as failures are signaled
|
||||||
|
through the stream starting from the stage which failed, all the way downstream to the final stages.
|
||||||
|
|
||||||
|
Connections Source failures
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
In the example below we add a custom ``PushStage`` (see :ref:`stream-customize-java`) in order to react to the
|
||||||
|
stream's failure. We signal a ``failureMonitor`` actor with the cause why the stream is going down, and let the Actor
|
||||||
|
handle the rest – maybe it'll decide to restart the server or shutdown the ActorSystem, that however is not our concern anymore.
|
||||||
|
|
||||||
|
.. includecode:: ../../code/docs/http/javadsl/server/HttpServerExampleDocTest.java
|
||||||
|
:include: incoming-connections-source-failure-handling
|
||||||
|
|
||||||
|
Connection failures
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The third type of failure that can occur is when the connection has been properly established,
|
||||||
|
however afterwards is terminated abruptly – for example by the client aborting the underlying TCP connection.
|
||||||
|
To handle this failure we can use the same pattern as in the previous snippet, however apply it to the connection's Flow:
|
||||||
|
|
||||||
|
.. includecode:: ../../code/docs/http/javadsl/server/HttpServerExampleDocTest.java
|
||||||
|
:include: connection-stream-failure-handling
|
||||||
|
|
||||||
|
These failures can be described more or less infrastructure related, they are failing bindings or connections.
|
||||||
|
Most of the time you won't need to dive into those very deeply, as Akka will simply log errors of this kind
|
||||||
|
anyway, which is a reasonable default for such problems.
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ Scala Documentation
|
||||||
===================
|
===================
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
:maxdepth: 2
|
:maxdepth: 3
|
||||||
|
|
||||||
scala/stream-index
|
scala/stream-index
|
||||||
scala/http/index
|
scala/http/index
|
||||||
|
|
|
||||||
|
|
@ -3,20 +3,32 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package docs.http.scaladsl
|
package docs.http.scaladsl
|
||||||
/*
|
|
||||||
import scala.concurrent.Future
|
import akka.actor.{ ActorRef, ActorSystem }
|
||||||
import org.scalatest.{ WordSpec, Matchers }
|
import akka.event.LoggingAdapter
|
||||||
import akka.actor.ActorSystem
|
import akka.http.scaladsl.Http
|
||||||
|
import akka.http.scaladsl.Http.ServerBinding
|
||||||
|
import akka.http.scaladsl.model._
|
||||||
|
import akka.stream.ActorMaterializer
|
||||||
|
import akka.stream.scaladsl.{ Flow, Sink }
|
||||||
|
import akka.stream.stage.{ Context, PushStage }
|
||||||
|
import org.scalatest.{ Matchers, WordSpec }
|
||||||
|
|
||||||
|
import scala.concurrent.{ ExecutionContext, Future }
|
||||||
|
|
||||||
class HttpServerExampleSpec extends WordSpec with Matchers {
|
class HttpServerExampleSpec extends WordSpec with Matchers {
|
||||||
|
|
||||||
|
// never actually called
|
||||||
|
val log: LoggingAdapter = null
|
||||||
|
|
||||||
"binding-example" in {
|
"binding-example" in {
|
||||||
|
import akka.http.scaladsl.Http
|
||||||
import akka.stream.ActorMaterializer
|
import akka.stream.ActorMaterializer
|
||||||
import akka.stream.scaladsl._
|
import akka.stream.scaladsl._
|
||||||
import akka.http.scaladsl.Http
|
|
||||||
|
|
||||||
implicit val system = ActorSystem()
|
implicit val system = ActorSystem()
|
||||||
implicit val materializer = ActorMaterializer()
|
implicit val materializer = ActorMaterializer()
|
||||||
|
implicit val ec = system.dispatcher
|
||||||
|
|
||||||
val serverSource: Source[Http.IncomingConnection, Future[Http.ServerBinding]] =
|
val serverSource: Source[Http.IncomingConnection, Future[Http.ServerBinding]] =
|
||||||
Http().bind(interface = "localhost", port = 8080)
|
Http().bind(interface = "localhost", port = 8080)
|
||||||
|
|
@ -27,12 +39,124 @@ class HttpServerExampleSpec extends WordSpec with Matchers {
|
||||||
}).run()
|
}).run()
|
||||||
}
|
}
|
||||||
|
|
||||||
"full-server-example" in {
|
"binding-failure-high-level-example" in {
|
||||||
|
import akka.http.scaladsl.Http
|
||||||
|
import akka.http.scaladsl.server.Directives._
|
||||||
import akka.stream.ActorMaterializer
|
import akka.stream.ActorMaterializer
|
||||||
import akka.stream.scaladsl.Sink
|
|
||||||
|
implicit val system = ActorSystem()
|
||||||
|
implicit val materializer = ActorMaterializer()
|
||||||
|
implicit val ec = 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// mock values:
|
||||||
|
val handleConnections: Sink[Http.IncomingConnection, Future[Http.ServerBinding]] =
|
||||||
|
Sink.ignore.mapMaterializedValue(_ => Future.failed(new Exception("")))
|
||||||
|
|
||||||
|
"binding-failure-handling" in {
|
||||||
|
implicit val system = ActorSystem()
|
||||||
|
implicit val materializer = ActorMaterializer()
|
||||||
|
implicit val ec = system.dispatcher
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
|
||||||
|
bindingFuture onFailure {
|
||||||
|
case ex: Exception =>
|
||||||
|
log.error(ex, "Failed to bind to {}:{}!", host, port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"incoming-connections-source-failure-handling" in {
|
||||||
|
implicit val system = ActorSystem()
|
||||||
|
implicit val materializer = ActorMaterializer()
|
||||||
|
implicit val ec = system.dispatcher
|
||||||
|
|
||||||
|
import Http._
|
||||||
|
val (host, port) = ("localhost", 8080)
|
||||||
|
val serverSource = Http().bind(host, port)
|
||||||
|
|
||||||
|
val failureMonitor: ActorRef = ???
|
||||||
|
|
||||||
|
val reactToTopLevelFailures = Flow[IncomingConnection]
|
||||||
|
.transform { () =>
|
||||||
|
new PushStage[IncomingConnection, IncomingConnection] {
|
||||||
|
override def onPush(elem: IncomingConnection, ctx: Context[IncomingConnection]) =
|
||||||
|
ctx.push(elem)
|
||||||
|
|
||||||
|
override def onUpstreamFailure(cause: Throwable, ctx: Context[IncomingConnection]) = {
|
||||||
|
failureMonitor ! cause
|
||||||
|
super.onUpstreamFailure(cause, ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
serverSource
|
||||||
|
.via(reactToTopLevelFailures)
|
||||||
|
.to(handleConnections) // Sink[Http.IncomingConnection, _]
|
||||||
|
.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
"connection-stream-failure-handling" in {
|
||||||
|
implicit val system = ActorSystem()
|
||||||
|
implicit val materializer = ActorMaterializer()
|
||||||
|
implicit val ec = system.dispatcher
|
||||||
|
|
||||||
|
val (host, port) = ("localhost", 8080)
|
||||||
|
val serverSource = Http().bind(host, port)
|
||||||
|
|
||||||
|
val reactToConnectionFailure = Flow[HttpRequest]
|
||||||
|
.transform { () =>
|
||||||
|
new PushStage[HttpRequest, HttpRequest] {
|
||||||
|
override def onPush(elem: HttpRequest, ctx: Context[HttpRequest]) =
|
||||||
|
ctx.push(elem)
|
||||||
|
|
||||||
|
override def onUpstreamFailure(cause: Throwable, ctx: Context[HttpRequest]) = {
|
||||||
|
// handle the failure somehow
|
||||||
|
super.onUpstreamFailure(cause, ctx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val httpEcho = Flow[HttpRequest]
|
||||||
|
.via(reactToConnectionFailure)
|
||||||
|
.map { request =>
|
||||||
|
// simple text "echo" response:
|
||||||
|
HttpResponse(entity = HttpEntity(ContentTypes.`text/plain`, request.entity.dataBytes))
|
||||||
|
}
|
||||||
|
|
||||||
|
serverSource
|
||||||
|
.runForeach { con =>
|
||||||
|
con.handleWith(httpEcho)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"full-server-example" in {
|
||||||
import akka.http.scaladsl.Http
|
import akka.http.scaladsl.Http
|
||||||
import akka.http.scaladsl.model.HttpMethods._
|
import akka.http.scaladsl.model.HttpMethods._
|
||||||
import akka.http.scaladsl.model._
|
import akka.http.scaladsl.model._
|
||||||
|
import akka.stream.ActorMaterializer
|
||||||
|
import akka.stream.scaladsl.Sink
|
||||||
|
|
||||||
implicit val system = ActorSystem()
|
implicit val system = ActorSystem()
|
||||||
implicit val materializer = ActorMaterializer()
|
implicit val materializer = ActorMaterializer()
|
||||||
|
|
@ -65,10 +189,10 @@ class HttpServerExampleSpec extends WordSpec with Matchers {
|
||||||
}
|
}
|
||||||
|
|
||||||
"low-level-server-example" in {
|
"low-level-server-example" in {
|
||||||
import akka.stream.ActorMaterializer
|
|
||||||
import akka.http.scaladsl.Http
|
import akka.http.scaladsl.Http
|
||||||
import akka.http.scaladsl.model.HttpMethods._
|
import akka.http.scaladsl.model.HttpMethods._
|
||||||
import akka.http.scaladsl.model._
|
import akka.http.scaladsl.model._
|
||||||
|
import akka.stream.ActorMaterializer
|
||||||
|
|
||||||
implicit val system = ActorSystem()
|
implicit val system = ActorSystem()
|
||||||
implicit val materializer = ActorMaterializer()
|
implicit val materializer = ActorMaterializer()
|
||||||
|
|
@ -94,10 +218,10 @@ class HttpServerExampleSpec extends WordSpec with Matchers {
|
||||||
// format: OFF
|
// format: OFF
|
||||||
|
|
||||||
"high-level-server-example" in {
|
"high-level-server-example" in {
|
||||||
import akka.stream.ActorMaterializer
|
|
||||||
import akka.http.scaladsl.Http
|
import akka.http.scaladsl.Http
|
||||||
import akka.http.scaladsl.server.Directives._
|
|
||||||
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._
|
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._
|
||||||
|
import akka.http.scaladsl.server.Directives._
|
||||||
|
import akka.stream.ActorMaterializer
|
||||||
|
|
||||||
implicit val system = ActorSystem()
|
implicit val system = ActorSystem()
|
||||||
implicit val materializer = ActorMaterializer()
|
implicit val materializer = ActorMaterializer()
|
||||||
|
|
@ -124,14 +248,15 @@ class HttpServerExampleSpec extends WordSpec with Matchers {
|
||||||
}
|
}
|
||||||
|
|
||||||
"minimal-routing-example" in {
|
"minimal-routing-example" in {
|
||||||
import akka.stream.ActorMaterializer
|
|
||||||
import akka.http.scaladsl.Http
|
import akka.http.scaladsl.Http
|
||||||
import akka.http.scaladsl.server.Directives._
|
|
||||||
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._
|
import akka.http.scaladsl.marshallers.xml.ScalaXmlSupport._
|
||||||
|
import akka.http.scaladsl.server.Directives._
|
||||||
|
import akka.stream.ActorMaterializer
|
||||||
|
|
||||||
object Main extends App {
|
object Main extends App {
|
||||||
implicit val system = ActorSystem("my-system")
|
implicit val system = ActorSystem("my-system")
|
||||||
implicit val materializer = ActorMaterializer()
|
implicit val materializer = ActorMaterializer()
|
||||||
|
implicit val ec = system.dispatcher
|
||||||
|
|
||||||
val route =
|
val route =
|
||||||
path("hello") {
|
path("hello") {
|
||||||
|
|
@ -145,9 +270,7 @@ class HttpServerExampleSpec extends WordSpec with Matchers {
|
||||||
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
|
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
|
||||||
|
|
||||||
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
|
println(s"Server online at http://localhost:8080/\nPress RETURN to stop...")
|
||||||
Console.readLine()
|
Console.readLine() // for the future transformations
|
||||||
|
|
||||||
import system.dispatcher // for the future transformations
|
|
||||||
bindingFuture
|
bindingFuture
|
||||||
.flatMap(_.unbind()) // trigger unbinding from the port
|
.flatMap(_.unbind()) // trigger unbinding from the port
|
||||||
.onComplete(_ ⇒ system.shutdown()) // and shutdown when done
|
.onComplete(_ ⇒ system.shutdown()) // and shutdown when done
|
||||||
|
|
@ -156,13 +279,13 @@ class HttpServerExampleSpec extends WordSpec with Matchers {
|
||||||
|
|
||||||
"long-routing-example" in {
|
"long-routing-example" in {
|
||||||
import akka.actor.ActorRef
|
import akka.actor.ActorRef
|
||||||
import akka.util.Timeout
|
|
||||||
import akka.pattern.ask
|
|
||||||
import akka.http.scaladsl.marshalling.ToResponseMarshaller
|
|
||||||
import akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller
|
|
||||||
import akka.http.scaladsl.model.StatusCodes.MovedPermanently
|
|
||||||
import akka.http.scaladsl.coding.Deflate
|
import akka.http.scaladsl.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.server.Directives._
|
||||||
|
import akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller
|
||||||
|
import akka.pattern.ask
|
||||||
|
import akka.util.Timeout
|
||||||
|
|
||||||
// types used by the API routes
|
// types used by the API routes
|
||||||
type Money = Double // only for demo purposes, don't try this at home!
|
type Money = Double // only for demo purposes, don't try this at home!
|
||||||
|
|
@ -174,6 +297,9 @@ class HttpServerExampleSpec extends WordSpec with Matchers {
|
||||||
implicit val orderUM: FromRequestUnmarshaller[Order] = ???
|
implicit val orderUM: FromRequestUnmarshaller[Order] = ???
|
||||||
implicit val orderM: ToResponseMarshaller[Seq[Order]] = ???
|
implicit val orderM: ToResponseMarshaller[Seq[Order]] = ???
|
||||||
implicit val timeout: Timeout = ??? // for actor asks
|
implicit val timeout: Timeout = ??? // for actor asks
|
||||||
|
implicit val ec: ExecutionContext = ???
|
||||||
|
implicit val mat: ActorMaterializer = ???
|
||||||
|
implicit val sys: ActorSystem = ???
|
||||||
|
|
||||||
val route = {
|
val route = {
|
||||||
path("orders") {
|
path("orders") {
|
||||||
|
|
@ -255,4 +381,3 @@ class HttpServerExampleSpec extends WordSpec with Matchers {
|
||||||
def processOrderRequest(id: Int, complete: Order => Unit): Unit = ???
|
def processOrderRequest(id: Int, complete: Order => Unit): Unit = ???
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
@ -3,19 +3,15 @@
|
||||||
*/
|
*/
|
||||||
package docs.stream.io
|
package docs.stream.io
|
||||||
|
|
||||||
import java.net.InetSocketAddress
|
|
||||||
import java.util.concurrent.atomic.AtomicReference
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
|
||||||
import akka.stream._
|
import akka.stream._
|
||||||
import akka.stream.scaladsl.Tcp._
|
import akka.stream.scaladsl.Tcp._
|
||||||
import akka.stream.scaladsl._
|
import akka.stream.scaladsl._
|
||||||
import akka.stream.stage.Context
|
import akka.stream.stage.{ Context, PushStage, SyncDirective }
|
||||||
import akka.stream.stage.PushStage
|
|
||||||
import akka.stream.stage.SyncDirective
|
|
||||||
import akka.stream.testkit.AkkaSpec
|
import akka.stream.testkit.AkkaSpec
|
||||||
import akka.testkit.TestProbe
|
import akka.testkit.TestProbe
|
||||||
import akka.util.ByteString
|
import akka.util.ByteString
|
||||||
import docs.stream.cookbook.RecipeParseLines
|
|
||||||
import docs.utils.TestUtils
|
import docs.utils.TestUtils
|
||||||
|
|
||||||
import scala.concurrent.Future
|
import scala.concurrent.Future
|
||||||
|
|
@ -31,8 +27,14 @@ class StreamTcpDocSpec extends AkkaSpec {
|
||||||
"simple server connection" in {
|
"simple server connection" in {
|
||||||
{
|
{
|
||||||
//#echo-server-simple-bind
|
//#echo-server-simple-bind
|
||||||
val connections: Source[IncomingConnection, Future[ServerBinding]] =
|
val binding: Future[ServerBinding] =
|
||||||
Tcp().bind("127.0.0.1", 8888)
|
Tcp().bind("127.0.0.1", 8888).to(Sink.ignore).run()
|
||||||
|
|
||||||
|
binding.map { b =>
|
||||||
|
b.unbind() onComplete {
|
||||||
|
case _ => // ...
|
||||||
|
}
|
||||||
|
}
|
||||||
//#echo-server-simple-bind
|
//#echo-server-simple-bind
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -167,4 +167,68 @@ On the server-side the stand-alone HTTP layer forms a ``BidiFlow`` that is defin
|
||||||
:snippet: server-layer
|
:snippet: server-layer
|
||||||
|
|
||||||
You create an instance of ``Http.ServerLayer`` by calling one of the two overloads of the ``Http().serverLayer`` method,
|
You create an instance of ``Http.ServerLayer`` by calling one of the two overloads of the ``Http().serverLayer`` method,
|
||||||
which also allows for varying degrees of configuration.
|
which also allows for varying degrees of configuration.
|
||||||
|
|
||||||
|
.. _handling-http-server-failures-low-level-scala:
|
||||||
|
|
||||||
|
Handling HTTP Server failures in the Low-Level API
|
||||||
|
--------------------------------------------------
|
||||||
|
|
||||||
|
There are various situations when failure may occur while initialising or running an Akka HTTP server.
|
||||||
|
Akka by default will log all these failures, however sometimes one may want to react to failures in addition to them
|
||||||
|
just being logged, for example by shutting down the actor system, or notifying some external monitoring end-point explicitly.
|
||||||
|
|
||||||
|
There are multiple things that can fail when creating and materializing an HTTP Server (similarily, the same applied to
|
||||||
|
a plain streaming ``Tcp()`` server). The types of failures that can happen on different layers of the stack, starting
|
||||||
|
from being unable to start the server, and ending with failing to unmarshal an HttpRequest, examples of failures include
|
||||||
|
(from outer-most, to inner-most):
|
||||||
|
|
||||||
|
- Failure to ``bind`` to the specified address/port,
|
||||||
|
- Failure while accepting new ``IncommingConnection`` s, for example when the OS has run out of file descriptors or memory,
|
||||||
|
- Failure while handling a connection, for example if the incoming ``HttpRequest`` is malformed.
|
||||||
|
|
||||||
|
This section describes how to handle each failure situation, and in which situations these failures may occur.
|
||||||
|
|
||||||
|
Bind failures
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The first type of failure is when the server is unable to bind to the given port. For example when the port
|
||||||
|
is already taken by another application, or if the port is privileged (i.e. only usable by ``root``).
|
||||||
|
In this case the "binding future" will fail immediatly, and we can react to if by listening on the Future's completion:
|
||||||
|
|
||||||
|
.. includecode2:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala
|
||||||
|
:snippet: binding-failure-handling
|
||||||
|
|
||||||
|
Once the server has successfully bound to a port, the ``Source[IncomingConnection, _]`` starts running and emiting
|
||||||
|
new incoming connections. This source technically can signal a failure as well, however this should only happen in very
|
||||||
|
dramantic situations such as running out of file descriptors or memory available to the system, such that it's not able
|
||||||
|
to accept a new incoming connection. Handling failures in Akka Streams is pretty stright forward, as failures are signaled
|
||||||
|
through the stream starting from the stage which failed, all the way downstream to the final stages.
|
||||||
|
|
||||||
|
Connections Source failures
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
In the example below we add a custom ``PushStage`` (see :ref:`stream-customize-scala`) in order to react to the
|
||||||
|
stream's failure. We signal a ``failureMonitor`` actor with the cause why the stream is going down, and let the Actor
|
||||||
|
handle the rest – maybe it'll decide to restart the server or shutdown the ActorSystem, that however is not our concern anymore.
|
||||||
|
|
||||||
|
.. includecode2:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala
|
||||||
|
:snippet: incoming-connections-source-failure-handling
|
||||||
|
|
||||||
|
Connection failures
|
||||||
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The third type of failure that can occur is when the connection has been properly established,
|
||||||
|
however afterwards is terminated abruptly – for example by the client aborting the underlying TCP connection.
|
||||||
|
To handle this failure we can use the same pattern as in the previous snippet, however apply it to the connection's Flow:
|
||||||
|
|
||||||
|
.. includecode2:: ../code/docs/http/scaladsl/HttpServerExampleSpec.scala
|
||||||
|
:snippet: connection-stream-failure-handling
|
||||||
|
|
||||||
|
These failures can be described more or less infrastructure related, they are failing bindings or connections.
|
||||||
|
Most of the time you won't need to dive into those very deeply, as Akka will simply log errors of this kind
|
||||||
|
anyway, which is a reasonable default for such problems.
|
||||||
|
|
||||||
|
In order to learn more about handling exceptions in the actual routing layer, which is where your application code
|
||||||
|
comes into the picture, refer to :ref:`exception-handling-scala` which focuses explicitly on explaining how exceptions
|
||||||
|
thrown in routes can be handled and transformed into :class:`HttpResponse` s with apropriate error codes and human-readable failure descriptions.
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ Description
|
||||||
Using this directive is an alternative to using a global implicitly defined ``ExceptionHandler`` that
|
Using this directive is an alternative to using a global implicitly defined ``ExceptionHandler`` that
|
||||||
applies to the complete route.
|
applies to the complete route.
|
||||||
|
|
||||||
See :ref:`Exception Handling` for general information about options for handling exceptions.
|
See :ref:`exception-handling-scala` for general information about options for handling exceptions.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
-------
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ failWith
|
||||||
========
|
========
|
||||||
|
|
||||||
Bubbles up the given error through the route structure where it is dealt with by the closest ``handleExceptions``
|
Bubbles up the given error through the route structure where it is dealt with by the closest ``handleExceptions``
|
||||||
directive and its ``ExceptionHandler``.
|
directive and its :class:`ExceptionHandler`.
|
||||||
|
|
||||||
|
|
||||||
Signature
|
Signature
|
||||||
|
|
@ -19,12 +19,12 @@ Description
|
||||||
|
|
||||||
``failWith`` explicitly raises an exception that gets bubbled up through the route structure to be picked up by the
|
``failWith`` explicitly raises an exception that gets bubbled up through the route structure to be picked up by the
|
||||||
nearest ``handleExceptions`` directive. Using ``failWith`` rather than simply throwing an exception enables the route
|
nearest ``handleExceptions`` directive. Using ``failWith`` rather than simply throwing an exception enables the route
|
||||||
structure's :ref:`Exception Handling` mechanism to deal with the exception even if the current route is executed
|
structure's :ref:`exception-handling-scala` mechanism to deal with the exception even if the current route is executed
|
||||||
asynchronously on another thread (e.g. in a ``Future`` or separate actor).
|
asynchronously on another thread (e.g. in a ``Future`` or separate actor).
|
||||||
|
|
||||||
If no ``handleExceptions`` is present above the respective location in the
|
If no ``handleExceptions`` is present above the respective location in the
|
||||||
route structure the top-level routing logic will handle the exception and translate it into a corresponding
|
route structure the top-level routing logic will handle the exception and translate it into a corresponding
|
||||||
``HttpResponse`` using the in-scope ``ExceptionHandler`` (see also the :ref:`Exception Handling` chapter).
|
``HttpResponse`` using the in-scope ``ExceptionHandler`` (see also the :ref:`exception-handling-scala` chapter).
|
||||||
|
|
||||||
There is one notable special case: If the given exception is a ``RejectionError`` exception it is *not* bubbled up,
|
There is one notable special case: If the given exception is a ``RejectionError`` exception it is *not* bubbled up,
|
||||||
but rather the wrapped exception is unpacked and "executed". This allows the "tunneling" of a rejection via an
|
but rather the wrapped exception is unpacked and "executed". This allows the "tunneling" of a rejection via an
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
.. _Exception Handling:
|
.. _exception-handling-scala:
|
||||||
|
|
||||||
Exception Handling
|
Exception Handling
|
||||||
==================
|
==================
|
||||||
|
|
|
||||||
|
|
@ -43,4 +43,34 @@ not really do anything useful but its definition should give you a feel for what
|
||||||
the Routing DSL will look like:
|
the Routing DSL will look like:
|
||||||
|
|
||||||
.. includecode2:: ../../code/docs/http/scaladsl/HttpServerExampleSpec.scala
|
.. includecode2:: ../../code/docs/http/scaladsl/HttpServerExampleSpec.scala
|
||||||
:snippet: long-routing-example
|
:snippet: long-routing-example
|
||||||
|
|
||||||
|
.. _handling-http-server-failures-high-level-scala:
|
||||||
|
|
||||||
|
Handling HTTP Server failures in the High-Level API
|
||||||
|
---------------------------------------------------
|
||||||
|
There are various situations when failure may occur while initialising or running an Akka HTTP server.
|
||||||
|
Akka by default will log all these failures, however sometimes one may want to react to failures in addition
|
||||||
|
to them just being logged, for example by shutting down the actor system, or notifying some external monitoring
|
||||||
|
end-point explicitly.
|
||||||
|
|
||||||
|
Bind failures
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
For example the server might be unable to bind to the given port. For example when the port
|
||||||
|
is already taken by another application, or if the port is privileged (i.e. only usable by ``root``).
|
||||||
|
In this case the "binding future" will fail immediatly, and we can react to if by listening on the Future's completion:
|
||||||
|
|
||||||
|
.. includecode2:: ../../code/docs/http/scaladsl/HttpServerExampleSpec.scala
|
||||||
|
:snippet: binding-failure-high-level-example
|
||||||
|
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
For a more low-level overview of the kinds of failures that can happen and also more fine-grained control over them
|
||||||
|
refer to the :ref:`handling-http-server-failures-low-level-scala` documentation.
|
||||||
|
|
||||||
|
Failures and exceptions inside the Routing DSL
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Exception handling within the Routing DSL is done by providing :class:`ExceptionHandler` s which are documented in-depth
|
||||||
|
in the :ref:`exception-handling-scala` section of the documtnation. You can use them to transform exceptions into
|
||||||
|
:class:`HttpResponse` s with apropriate error codes and human-readable failure descriptions.
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ Generally when a route receives a request (or rather a ``RequestContext`` for it
|
||||||
|
|
||||||
- Complete the request by returning the value of ``requestContext.complete(...)``
|
- Complete the request by returning the value of ``requestContext.complete(...)``
|
||||||
- Reject the request by returning the value of ``requestContext.reject(...)`` (see :ref:`Rejections`)
|
- Reject the request by returning the value of ``requestContext.reject(...)`` (see :ref:`Rejections`)
|
||||||
- Fail the request by returning the value of ``requestContext.fail(...)`` or by just throwing an exception (see :ref:`Exception Handling`)
|
- Fail the request by returning the value of ``requestContext.fail(...)`` or by just throwing an exception (see :ref:`exception-handling-scala`)
|
||||||
- Do any kind of asynchronous processing and instantly return a ``Future[RouteResult]`` to be eventually completed later
|
- Do any kind of asynchronous processing and instantly return a ``Future[RouteResult]`` to be eventually completed later
|
||||||
|
|
||||||
The first case is pretty clear, by calling ``complete`` a given response is sent to the client as reaction to the
|
The first case is pretty clear, by calling ``complete`` a given response is sent to the client as reaction to the
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ package akka.http.javadsl.model;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import akka.http.impl.util.JavaAccessors;
|
import akka.http.impl.util.JavaAccessors;
|
||||||
import akka.http.scaladsl.model.*;
|
|
||||||
import akka.http.scaladsl.model.HttpEntity;
|
import akka.http.scaladsl.model.HttpEntity;
|
||||||
|
import akka.http.scaladsl.model.HttpEntity$;
|
||||||
import akka.util.ByteString;
|
import akka.util.ByteString;
|
||||||
import akka.stream.javadsl.Source;
|
import akka.stream.javadsl.Source;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
package akka.http.javadsl.model;
|
package akka.http.javadsl.model;
|
||||||
|
|
||||||
import akka.japi.Option;
|
import akka.japi.Option;
|
||||||
|
import akka.stream.javadsl.Source;
|
||||||
import akka.util.ByteString;
|
import akka.util.ByteString;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue