Merge pull request #17965 from spray/w/java-side-documentation
Java side documentation progress
This commit is contained in:
commit
95e2640902
52 changed files with 1520 additions and 32 deletions
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.javadsl;
|
||||
|
||||
import akka.http.javadsl.model.HttpRequest;
|
||||
import akka.http.javadsl.server.*;
|
||||
import akka.http.javadsl.server.values.Parameters;
|
||||
import akka.http.javadsl.server.values.PathMatchers;
|
||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||
import akka.http.javadsl.testkit.TestRoute;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HandlerExampleSpec extends JUnitRouteTest {
|
||||
@Test
|
||||
public void testSimpleHandler() {
|
||||
//#simple-handler-example-full
|
||||
class TestHandler extends akka.http.javadsl.server.AllDirectives {
|
||||
//#simple-handler
|
||||
Handler handler = new Handler() {
|
||||
@Override
|
||||
public RouteResult handle(RequestContext ctx) {
|
||||
return ctx.complete("This was a " + ctx.request().method().value() +
|
||||
" request to "+ctx.request().getUri());
|
||||
}
|
||||
};
|
||||
//#simple-handler
|
||||
|
||||
Route createRoute() {
|
||||
return route(
|
||||
get(
|
||||
handleWith(handler)
|
||||
),
|
||||
post(
|
||||
path("abc").route(
|
||||
handleWith(handler)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// actual testing code
|
||||
TestRoute r = testRoute(new TestHandler().createRoute());
|
||||
r.run(HttpRequest.GET("/test"))
|
||||
.assertStatusCode(200)
|
||||
.assertEntity("This was a GET request to /test");
|
||||
|
||||
r.run(HttpRequest.POST("/test"))
|
||||
.assertStatusCode(404);
|
||||
|
||||
r.run(HttpRequest.POST("/abc"))
|
||||
.assertStatusCode(200)
|
||||
.assertEntity("This was a POST request to /abc");
|
||||
//#simple-handler-example-full
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculator() {
|
||||
//#handler2-example-full
|
||||
class TestHandler extends akka.http.javadsl.server.AllDirectives {
|
||||
RequestVal<Integer> xParam = Parameters.intValue("x");
|
||||
RequestVal<Integer> yParam = Parameters.intValue("y");
|
||||
|
||||
RequestVal<Integer> xSegment = PathMatchers.integerNumber();
|
||||
RequestVal<Integer> ySegment = PathMatchers.integerNumber();
|
||||
|
||||
//#handler2
|
||||
Handler2<Integer, Integer> multiply =
|
||||
new Handler2<Integer, Integer>() {
|
||||
@Override
|
||||
public RouteResult handle(RequestContext ctx, Integer x, Integer y) {
|
||||
int result = x * y;
|
||||
return ctx.complete("x * y = " + result);
|
||||
}
|
||||
};
|
||||
|
||||
Route multiplyXAndYParam = handleWith(xParam, yParam, multiply);
|
||||
//#handler2
|
||||
|
||||
Route createRoute() {
|
||||
return route(
|
||||
get(
|
||||
pathPrefix("calculator").route(
|
||||
path("multiply").route(
|
||||
multiplyXAndYParam
|
||||
),
|
||||
path("path-multiply", xSegment, ySegment).route(
|
||||
handleWith(xSegment, ySegment, multiply)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// actual testing code
|
||||
TestRoute r = testRoute(new TestHandler().createRoute());
|
||||
r.run(HttpRequest.GET("/calculator/multiply?x=12&y=42"))
|
||||
.assertStatusCode(200)
|
||||
.assertEntity("x * y = 504");
|
||||
|
||||
r.run(HttpRequest.GET("/calculator/path-multiply/23/5"))
|
||||
.assertStatusCode(200)
|
||||
.assertEntity("x * y = 115");
|
||||
//#handler2-example-full
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCalculatorReflective() {
|
||||
//#reflective-example-full
|
||||
class TestHandler extends akka.http.javadsl.server.AllDirectives {
|
||||
RequestVal<Integer> xParam = Parameters.intValue("x");
|
||||
RequestVal<Integer> yParam = Parameters.intValue("y");
|
||||
|
||||
RequestVal<Integer> xSegment = PathMatchers.integerNumber();
|
||||
RequestVal<Integer> ySegment = PathMatchers.integerNumber();
|
||||
|
||||
|
||||
//#reflective
|
||||
public RouteResult multiply(RequestContext ctx, Integer x, Integer y) {
|
||||
int result = x * y;
|
||||
return ctx.complete("x * y = " + result);
|
||||
}
|
||||
|
||||
Route multiplyXAndYParam = handleWith(this, "multiply", xParam, yParam);
|
||||
//#reflective
|
||||
|
||||
Route createRoute() {
|
||||
return route(
|
||||
get(
|
||||
pathPrefix("calculator").route(
|
||||
path("multiply").route(
|
||||
multiplyXAndYParam
|
||||
),
|
||||
path("path-multiply", xSegment, ySegment).route(
|
||||
handleWith(this, "multiply", xSegment, ySegment)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// actual testing code
|
||||
TestRoute r = testRoute(new TestHandler().createRoute());
|
||||
r.run(HttpRequest.GET("/calculator/multiply?x=12&y=42"))
|
||||
.assertStatusCode(200)
|
||||
.assertEntity("x * y = 504");
|
||||
|
||||
r.run(HttpRequest.GET("/calculator/path-multiply/23/5"))
|
||||
.assertStatusCode(200)
|
||||
.assertEntity("x * y = 115");
|
||||
//#reflective-example-full
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2014 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.javadsl;
|
||||
|
||||
//#high-level-server-example
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.http.javadsl.model.MediaTypes;
|
||||
import akka.http.javadsl.server.*;
|
||||
import akka.http.javadsl.server.values.Parameters;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class HighLevelServerExample extends HttpApp {
|
||||
public static void main(String[] args) throws IOException {
|
||||
// boot up server using the route as defined below
|
||||
ActorSystem system = ActorSystem.create();
|
||||
|
||||
// HttpApp.bindRoute expects a route being provided by HttpApp.createRoute
|
||||
new HighLevelServerExample().bindRoute("localhost", 8080, system);
|
||||
System.out.println("Type RETURN to exit");
|
||||
System.in.read();
|
||||
system.shutdown();
|
||||
}
|
||||
|
||||
// A RequestVal is a type-safe representation of some aspect of the request.
|
||||
// In this case it represents the `name` URI parameter of type String.
|
||||
private RequestVal<String> name = Parameters.stringValue("name").withDefault("Mister X");
|
||||
|
||||
@Override
|
||||
public Route createRoute() {
|
||||
// This handler generates responses to `/hello?name=XXX` requests
|
||||
Route helloRoute =
|
||||
handleWith(name,
|
||||
// in Java 8 the following becomes simply
|
||||
// (ctx, name) -> ctx.complete("Hello " + name + "!")
|
||||
new Handler1<String>() {
|
||||
@Override
|
||||
public RouteResult handle(RequestContext ctx, String name) {
|
||||
return ctx.complete("Hello " + name + "!");
|
||||
}
|
||||
});
|
||||
|
||||
return
|
||||
// here the complete behavior for this server is defined
|
||||
route(
|
||||
// only handle GET requests
|
||||
get(
|
||||
// matches the empty path
|
||||
pathSingleSlash().route(
|
||||
// return a constant string with a certain content type
|
||||
complete(MediaTypes.TEXT_HTML.toContentType(),
|
||||
"<html><body>Hello world!</body></html>")
|
||||
),
|
||||
path("ping").route(
|
||||
// return a simple `text/plain` response
|
||||
complete("PONG!")
|
||||
),
|
||||
path("hello").route(
|
||||
// uses the route defined above
|
||||
helloRoute
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
//#high-level-server-example
|
||||
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.javadsl;
|
||||
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.http.impl.util.Util;
|
||||
import akka.http.javadsl.Http;
|
||||
import akka.http.javadsl.IncomingConnection;
|
||||
import akka.http.javadsl.ServerBinding;
|
||||
import akka.http.javadsl.model.*;
|
||||
import akka.http.javadsl.model.ws.Websocket;
|
||||
import akka.japi.function.Function;
|
||||
import akka.japi.function.Procedure;
|
||||
import akka.stream.ActorMaterializer;
|
||||
import akka.stream.Materializer;
|
||||
import akka.stream.javadsl.Flow;
|
||||
import akka.stream.javadsl.Sink;
|
||||
import akka.stream.javadsl.Source;
|
||||
import org.junit.Test;
|
||||
import scala.Function1;
|
||||
import scala.concurrent.Await;
|
||||
import scala.concurrent.Future;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class HttpServerExampleSpec {
|
||||
public static void bindingExample() {
|
||||
//#binding-example
|
||||
ActorSystem system = ActorSystem.create();
|
||||
Materializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
Source<IncomingConnection, Future<ServerBinding>> serverSource =
|
||||
Http.get(system).bind("localhost", 8080, 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);
|
||||
//#binding-example
|
||||
}
|
||||
public static void fullServerExample() throws Exception {
|
||||
//#full-server-example
|
||||
ActorSystem system = ActorSystem.create();
|
||||
//#full-server-example
|
||||
try {
|
||||
//#full-server-example
|
||||
final Materializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
Source<IncomingConnection, Future<ServerBinding>> serverSource =
|
||||
Http.get(system).bind("localhost", 8080, materializer);
|
||||
|
||||
//#request-handler
|
||||
final Function<HttpRequest, HttpResponse> requestHandler =
|
||||
new Function<HttpRequest, HttpResponse>() {
|
||||
private final HttpResponse NOT_FOUND =
|
||||
HttpResponse.create()
|
||||
.withStatus(404)
|
||||
.withEntity("Unknown resource!");
|
||||
|
||||
|
||||
@Override
|
||||
public HttpResponse apply(HttpRequest request) throws Exception {
|
||||
Uri uri = request.getUri();
|
||||
if (request.method() == HttpMethods.GET) {
|
||||
if (uri.path().equals("/"))
|
||||
return
|
||||
HttpResponse.create()
|
||||
.withEntity(MediaTypes.TEXT_HTML.toContentType(),
|
||||
"<html><body>Hello world!</body></html>");
|
||||
else if (uri.path().equals("/hello")) {
|
||||
String name = Util.getOrElse(uri.parameter("name"), "Mister X");
|
||||
|
||||
return
|
||||
HttpResponse.create()
|
||||
.withEntity("Hello " + name + "!");
|
||||
}
|
||||
else if (uri.path().equals("/ping"))
|
||||
return HttpResponse.create().withEntity("PONG!");
|
||||
else
|
||||
return NOT_FOUND;
|
||||
}
|
||||
else return NOT_FOUND;
|
||||
}
|
||||
};
|
||||
//#request-handler
|
||||
|
||||
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());
|
||||
|
||||
connection.handleWithSyncHandler(requestHandler, materializer);
|
||||
// this is equivalent to
|
||||
//connection.handleWith(Flow.of(HttpRequest.class).map(requestHandler), materializer);
|
||||
}
|
||||
})).run(materializer);
|
||||
//#full-server-example
|
||||
|
||||
Await.result(serverBindingFuture, new FiniteDuration(1, TimeUnit.SECONDS)); // will throw if binding fails
|
||||
System.out.println("Press ENTER to stop.");
|
||||
new BufferedReader(new InputStreamReader(System.in)).readLine();
|
||||
} finally {
|
||||
system.shutdown();
|
||||
}
|
||||
}
|
||||
public static void main(String[] args) throws Exception {
|
||||
fullServerExample();
|
||||
}
|
||||
}
|
||||
89
akka-docs-dev/rst/java/code/docs/http/javadsl/ModelSpec.java
Normal file
89
akka-docs-dev/rst/java/code/docs/http/javadsl/ModelSpec.java
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (C) 2009-2015 Typesafe Inc. <http://www.typesafe.com>
|
||||
*/
|
||||
|
||||
package docs.http.javadsl;
|
||||
|
||||
import akka.japi.Option;
|
||||
import akka.util.ByteString;
|
||||
import org.junit.Test;
|
||||
|
||||
//#import-model
|
||||
import akka.http.javadsl.model.*;
|
||||
import akka.http.javadsl.model.headers.*;
|
||||
//#import-model
|
||||
|
||||
public class ModelSpec {
|
||||
@Test
|
||||
public void testConstructRequest() {
|
||||
//#construct-request
|
||||
// construct a simple GET request to `homeUri`
|
||||
Uri homeUri = Uri.create("/home");
|
||||
HttpRequest request1 = HttpRequest.create().withUri(homeUri);
|
||||
|
||||
// construct simple GET request to "/index" using helper methods
|
||||
HttpRequest request2 = HttpRequest.GET("/index");
|
||||
|
||||
// construct simple POST request containing entity
|
||||
ByteString data = ByteString.fromString("abc");
|
||||
HttpRequest postRequest1 = HttpRequest.POST("/receive").withEntity(data);
|
||||
|
||||
// customize every detail of HTTP request
|
||||
//import HttpProtocols._
|
||||
//import MediaTypes._
|
||||
Authorization authorization = Authorization.basic("user", "pass");
|
||||
HttpRequest complexRequest =
|
||||
HttpRequest.PUT("/user")
|
||||
.withEntity(HttpEntities.create(MediaTypes.TEXT_PLAIN.toContentType(), "abc"))
|
||||
.addHeader(authorization)
|
||||
.withProtocol(HttpProtocols.HTTP_1_0);
|
||||
//#construct-request
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConstructResponse() {
|
||||
//#construct-response
|
||||
// simple OK response without data created using the integer status code
|
||||
HttpResponse ok = HttpResponse.create().withStatus(200);
|
||||
|
||||
// 404 response created using the named StatusCode constant
|
||||
HttpResponse notFound = HttpResponse.create().withStatus(StatusCodes.NOT_FOUND);
|
||||
|
||||
// 404 response with a body explaining the error
|
||||
HttpResponse notFoundCustom =
|
||||
HttpResponse.create()
|
||||
.withStatus(404)
|
||||
.withEntity("Unfortunately, the resource couldn't be found.");
|
||||
|
||||
// A redirecting response containing an extra header
|
||||
Location locationHeader = Location.create("http://example.com/other");
|
||||
HttpResponse redirectResponse =
|
||||
HttpResponse.create()
|
||||
.withStatus(StatusCodes.FOUND)
|
||||
.addHeader(locationHeader);
|
||||
//#construct-response
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDealWithHeaders() {
|
||||
//#headers
|
||||
// create a ``Location`` header
|
||||
Location locationHeader = Location.create("http://example.com/other");
|
||||
|
||||
// create an ``Authorization`` header with HTTP Basic authentication data
|
||||
Authorization authorization = Authorization.basic("user", "pass");
|
||||
//#headers
|
||||
}
|
||||
|
||||
//#headers
|
||||
|
||||
// a method that extracts basic HTTP credentials from a request
|
||||
private Option<BasicHttpCredentials> getCredentialsOfRequest(HttpRequest request) {
|
||||
Option<Authorization> auth = request.getHeader(Authorization.class);
|
||||
if (auth.isDefined() && auth.get().credentials() instanceof BasicHttpCredentials)
|
||||
return Option.some((BasicHttpCredentials) auth.get().credentials());
|
||||
else
|
||||
return Option.none();
|
||||
}
|
||||
//#headers
|
||||
}
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
.. _ConnectionLevelApi-java:
|
||||
|
||||
Connection-Level Client-Side API
|
||||
================================
|
||||
|
||||
TODO
|
||||
|
||||
For the time being, :ref:`see the Scala chapter on the same topic <ConnectionLevelApi>`.
|
||||
8
akka-docs-dev/rst/java/http/client-side/host-level.rst
Normal file
8
akka-docs-dev/rst/java/http/client-side/host-level.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
.. _HostLevelApi-java:
|
||||
|
||||
Host-Level Client-Side API
|
||||
==========================
|
||||
|
||||
TODO
|
||||
|
||||
For the time being, :ref:`see the Scala chapter on the same topic <HostLevelApi>`.
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
.. _clientSideHTTPS-java:
|
||||
|
||||
Client-Side HTTPS Support
|
||||
=========================
|
||||
|
||||
TODO
|
||||
|
||||
For the time being, :ref:`see the Scala chapter on the same topic <clientSideHTTPS>`.
|
||||
15
akka-docs-dev/rst/java/http/client-side/index.rst
Normal file
15
akka-docs-dev/rst/java/http/client-side/index.rst
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
.. _http-client-side-java:
|
||||
|
||||
Consuming HTTP-based Services (Client-Side)
|
||||
===========================================
|
||||
|
||||
...
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
connection-level
|
||||
host-level
|
||||
request-level
|
||||
https-support
|
||||
websocket-support
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
.. _RequestLevelApi-java:
|
||||
|
||||
Request-Level Client-Side API
|
||||
=============================
|
||||
|
||||
TODO
|
||||
|
||||
For the time being, :ref:`see the Scala chapter on the same topic <RequestLevelApi>`.
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
.. _client-side-websocket-support-java:
|
||||
|
||||
Client-Side WebSocket Support
|
||||
=============================
|
||||
|
||||
Not yet implemented see 17275_.
|
||||
|
||||
.. _17275: https://github.com/akka/akka/issues/17275
|
||||
28
akka-docs-dev/rst/java/http/configuration.rst
Normal file
28
akka-docs-dev/rst/java/http/configuration.rst
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
.. _akka-http-configuration-java:
|
||||
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Just like any other Akka module Akka HTTP is configured via `Typesafe Config`_.
|
||||
Usually this means that you provide an ``application.conf`` which contains all the application-specific settings that
|
||||
differ from the default ones provided by the reference configuration files from the individual Akka modules.
|
||||
|
||||
These are the relevant default configuration values for the Akka HTTP modules.
|
||||
|
||||
akka-http-core
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
.. literalinclude:: ../../../../akka-http-core/src/main/resources/reference.conf
|
||||
:language: none
|
||||
|
||||
|
||||
akka-http
|
||||
~~~~~~~~~
|
||||
|
||||
.. literalinclude:: ../../../../akka-http/src/main/resources/reference.conf
|
||||
:language: none
|
||||
|
||||
|
||||
The other Akka HTTP modules do not offer any configuration via `Typesafe Config`_.
|
||||
|
||||
.. _Typesafe Config: https://github.com/typesafehub/config
|
||||
233
akka-docs-dev/rst/java/http/http-model.rst
Normal file
233
akka-docs-dev/rst/java/http/http-model.rst
Normal file
|
|
@ -0,0 +1,233 @@
|
|||
.. _http-model-java:
|
||||
|
||||
HTTP Model
|
||||
==========
|
||||
|
||||
Akka HTTP model contains a deeply structured, fully immutable, case-class based model of all the major HTTP data
|
||||
structures, like HTTP requests, responses and common headers.
|
||||
It lives in the *akka-http-core* module and forms the basis for most of Akka HTTP's APIs.
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
Since akka-http-core provides the central HTTP data structures you will find the following import in quite a
|
||||
few places around the code base (and probably your own code as well):
|
||||
|
||||
.. includecode:: ../code/docs/http/javadsl/ModelSpec.java
|
||||
:include: import-model
|
||||
|
||||
This brings all of the most relevant types in scope, mainly:
|
||||
|
||||
- ``HttpRequest`` and ``HttpResponse``, the central message model
|
||||
- ``headers``, the package containing all the predefined HTTP header models and supporting types
|
||||
- Supporting types like ``Uri``, ``HttpMethods``, ``MediaTypes``, ``StatusCodes``, etc.
|
||||
|
||||
A common pattern is that the model of a certain entity is represented by an immutable type (class or trait),
|
||||
while the actual instances of the entity defined by the HTTP spec live in an accompanying object carrying the name of
|
||||
the type plus a trailing plural 's'.
|
||||
|
||||
For example:
|
||||
|
||||
- Defined ``HttpMethod`` instances are defined as static fields of the ``HttpMethods`` class.
|
||||
- Defined ``HttpCharset`` instances are defined as static fields of the ``HttpCharsets`` class.
|
||||
- Defined ``HttpEncoding`` instances are defined as static fields of the ``HttpEncodings`` class.
|
||||
- Defined ``HttpProtocol`` instances are defined as static fields of the ``HttpProtocols`` class.
|
||||
- Defined ``MediaType`` instances are defined as static fields of the ``MediaTypes`` class.
|
||||
- Defined ``StatusCode`` instances are defined as static fields of the ``StatusCodes`` class.
|
||||
|
||||
HttpRequest
|
||||
-----------
|
||||
|
||||
``HttpRequest`` and ``HttpResponse`` are the basic immutable classes representing HTTP messages.
|
||||
|
||||
An ``HttpRequest`` consists of
|
||||
|
||||
- a method (GET, POST, etc.)
|
||||
- a URI
|
||||
- a seq of headers
|
||||
- an entity (body data)
|
||||
- a protocol
|
||||
|
||||
Here are some examples how to construct an ``HttpRequest``:
|
||||
|
||||
.. includecode:: ../code/docs/http/javadsl/ModelSpec.java
|
||||
:include: construct-request
|
||||
|
||||
In its basic form ``HttpRequest.create`` creates an empty default GET request without headers which can then be
|
||||
transformed using one of the ``withX`` methods, ``addHeader``, or ``addHeaders``. Each of those will create a
|
||||
new immutable instance, so instances can be shared freely. There exist some overloads for ``HttpRequest.create`` that
|
||||
simplify creating requests for common cases. Also, to aid readability, there are predefined alternatives for ``create``
|
||||
named after HTTP methods to create a request with a given method and uri directly.
|
||||
|
||||
HttpResponse
|
||||
------------
|
||||
|
||||
An ``HttpResponse`` consists of
|
||||
|
||||
- a status code
|
||||
- a list of headers
|
||||
- an entity (body data)
|
||||
- a protocol
|
||||
|
||||
Here are some examples how to construct an ``HttpResponse``:
|
||||
|
||||
.. includecode:: ../code/docs/http/javadsl/ModelSpec.java
|
||||
:include: construct-response
|
||||
|
||||
In addition to the simple ``HttpEntities.create`` methods which create an entity from a fixed ``String`` or ``ByteString``
|
||||
as shown here the Akka HTTP model defines a number of subclasses of ``HttpEntity`` which allow body data to be specified as a
|
||||
stream of bytes. All of these types can be created using the method on ``HttpEntites``.
|
||||
|
||||
|
||||
.. _HttpEntity-java:
|
||||
|
||||
HttpEntity
|
||||
----------
|
||||
|
||||
An ``HttpEntity`` carries the data bytes of a message together with its Content-Type and, if known, its Content-Length.
|
||||
In Akka HTTP there are five different kinds of entities which model the various ways that message content can be
|
||||
received or sent:
|
||||
|
||||
HttpEntityStrict
|
||||
The simplest entity, which is used when all the entity are already available in memory.
|
||||
It wraps a plain ``ByteString`` and represents a standard, unchunked entity with a known ``Content-Length``.
|
||||
|
||||
|
||||
HttpEntityDefault
|
||||
The general, unchunked HTTP/1.1 message entity.
|
||||
It has a known length and presents its data as a ``Source[ByteString]`` which can be only materialized once.
|
||||
It is an error if the provided source doesn't produce exactly as many bytes as specified.
|
||||
The distinction of ``HttpEntityStrict`` and ``HttpEntityDefault`` is an API-only one. One the wire,
|
||||
both kinds of entities look the same.
|
||||
|
||||
|
||||
HttpEntityChunked
|
||||
The model for HTTP/1.1 `chunked content`__ (i.e. sent with ``Transfer-Encoding: chunked``).
|
||||
The content length is unknown and the individual chunks are presented as a ``Source[ChunkStreamPart]``.
|
||||
A ``ChunkStreamPart`` is either a non-empty chunk or the empty last chunk containing optional trailer headers.
|
||||
The stream consists of zero or more non-empty chunks parts and can be terminated by an optional last chunk.
|
||||
|
||||
|
||||
HttpEntityCloseDelimited
|
||||
An unchunked entity of unknown length that is implicitly delimited by closing the connection (``Connection: close``).
|
||||
Content data is presented as a ``Source[ByteString]``.
|
||||
Since the connection must be closed after sending an entity of this type it can only be used on the server-side for
|
||||
sending a response.
|
||||
Also, the main purpose of ``CloseDelimited`` entities is compatibility with HTTP/1.0 peers, which do not support
|
||||
chunked transfer encoding. If you are building a new application and are not constrained by legacy requirements you
|
||||
shouldn't rely on ``CloseDelimited`` entities, since implicit terminate-by-connection-close is not a robust way of
|
||||
signaling response end, especially in the presence of proxies. Additionally this type of entity prevents connection
|
||||
reuse which can seriously degrade performance. Use ``HttpEntityChunked`` instead!
|
||||
|
||||
|
||||
HttpEntityIndefiniteLength
|
||||
A streaming entity of unspecified length for use in a ``Multipart.BodyPart``.
|
||||
|
||||
__ http://tools.ietf.org/html/rfc7230#section-4.1
|
||||
|
||||
Entity types ``HttpEntityStrict``, ``HttpEntityDefault``, and ``HttpEntityChunked`` are a subtype of ``RequestEntity``
|
||||
which allows to use them for requests and responses. In contrast, ``HttpEntityCloseDelimited`` can only be used for responses.
|
||||
|
||||
Streaming entity types (i.e. all but ``HttpEntityStrict``) cannot be shared or serialized. To create a strict, sharable copy of an
|
||||
entity or message use ``HttpEntity.toStrict`` or ``HttpMessage.toStrict`` which returns a ``Future`` of the object with
|
||||
the body data collected into a ``ByteString``.
|
||||
|
||||
The class ``HttpEntities`` contains static methods to create entities from common types easily.
|
||||
|
||||
You can use the ``isX` methods of ``HttpEntity`` to find out of which subclass an entity is if you want to provide
|
||||
special handling for each of the subtypes. However, in many cases a recipient of an ``HttpEntity`` doesn't care about
|
||||
of which subtype an entity is (and how data is transported exactly on the HTTP layer). Therefore, a general
|
||||
``HttpEntity::getDataBytes: Source<ByteString, ?>`` is provided which allows access to the data of an entity regardless
|
||||
of its concrete subtype.
|
||||
|
||||
.. note::
|
||||
|
||||
When to use which subtype?
|
||||
- Use ``HttpEntityStrict`` if the amount of data is "small" and already available in memory (e.g. as a ``String`` or ``ByteString``)
|
||||
- Use ``HttpEntityDefault`` if the data is generated by a streaming data source and the size of the data is known
|
||||
- Use ``HttpEntityChunked`` for an entity of unknown length
|
||||
- Use ``HttpEntityCloseDelimited`` for a response as a legacy alternative to ``HttpEntityChunked`` if the client
|
||||
doesn't support chunked transfer encoding. Otherwise use ``HttpEntityChunked``!
|
||||
- In a ``Multipart.Bodypart`` use ``HttpEntityIndefiniteLength`` for content of unknown length.
|
||||
|
||||
.. caution::
|
||||
|
||||
When you receive a non-strict message from a connection then additional data is only read from the network when you
|
||||
request it by consuming the entity data stream. This means that, if you *don't* consume the entity stream then the
|
||||
connection will effectively be stalled. In particular, no subsequent message (request or response) will be read from
|
||||
the connection as the entity of the current message "blocks" the stream.
|
||||
Therefore you must make sure that you always consume the entity data, even in the case that you are not actually
|
||||
interested in it!
|
||||
|
||||
|
||||
Header Model
|
||||
------------
|
||||
|
||||
Akka HTTP contains a rich model of the most common HTTP headers. Parsing and rendering is done automatically so that
|
||||
applications don't need to care for the actual syntax of headers. Headers not modelled explicitly are represented
|
||||
as a ``RawHeader`` (which is essentially a String/String name/value pair).
|
||||
|
||||
See these examples of how to deal with headers:
|
||||
|
||||
.. includecode:: ../code/docs/http/javadsl/ModelSpec.java
|
||||
:include: headers
|
||||
|
||||
|
||||
HTTP Headers
|
||||
------------
|
||||
|
||||
When the Akka HTTP server receives an HTTP request it tries to parse all its headers into their respective
|
||||
model classes. Independently of whether this succeeds or not, the HTTP layer will
|
||||
always pass on all received headers to the application. Unknown headers as well as ones with invalid syntax (according
|
||||
to the header parser) will be made available as ``RawHeader`` instances. For the ones exhibiting parsing errors a
|
||||
warning message is logged depending on the value of the ``illegal-header-warnings`` config setting.
|
||||
|
||||
Some headers have special status in HTTP and are therefore treated differently from "regular" headers:
|
||||
|
||||
Content-Type
|
||||
The Content-Type of an HTTP message is modeled as the ``contentType`` field of the ``HttpEntity``.
|
||||
The ``Content-Type`` header therefore doesn't appear in the ``headers`` sequence of a message.
|
||||
Also, a ``Content-Type`` header instance that is explicitly added to the ``headers`` of a request or response will
|
||||
not be rendered onto the wire and trigger a warning being logged instead!
|
||||
|
||||
Transfer-Encoding
|
||||
Messages with ``Transfer-Encoding: chunked`` are represented as a ``HttpEntityChunked`` entity.
|
||||
As such chunked messages that do not have another deeper nested transfer encoding will not have a ``Transfer-Encoding``
|
||||
header in their ``headers`` list.
|
||||
Similarly, a ``Transfer-Encoding`` header instance that is explicitly added to the ``headers`` of a request or
|
||||
response will not be rendered onto the wire and trigger a warning being logged instead!
|
||||
|
||||
Content-Length
|
||||
The content length of a message is modelled via its :ref:`HttpEntity-java`. As such no ``Content-Length`` header will ever
|
||||
be part of a message's ``header`` sequence.
|
||||
Similarly, a ``Content-Length`` header instance that is explicitly added to the ``headers`` of a request or
|
||||
response will not be rendered onto the wire and trigger a warning being logged instead!
|
||||
|
||||
Server
|
||||
A ``Server`` header is usually added automatically to any response and its value can be configured via the
|
||||
``akka.http.server.server-header`` setting. Additionally an application can override the configured header with a
|
||||
custom one by adding it to the response's ``header`` sequence.
|
||||
|
||||
User-Agent
|
||||
A ``User-Agent`` header is usually added automatically to any request and its value can be configured via the
|
||||
``akka.http.client.user-agent-header`` setting. Additionally an application can override the configured header with a
|
||||
custom one by adding it to the request's ``header`` sequence.
|
||||
|
||||
Date
|
||||
The ``Date`` response header is added automatically but can be overridden by supplying it manually.
|
||||
|
||||
Connection
|
||||
On the server-side Akka HTTP watches for explicitly added ``Connection: close`` response headers and as such honors
|
||||
the potential wish of the application to close the connection after the respective response has been sent out.
|
||||
The actual logic for determining whether to close the connection is quite involved. It takes into account the
|
||||
request's method, protocol and potential ``Connection`` header as well as the response's protocol, entity and
|
||||
potential ``Connection`` header. See `this test`__ for a full table of what happens when.
|
||||
|
||||
__ @github@/akka-http-core/src/test/scala/akka/http/impl/engine/rendering/ResponseRendererSpec.scala#L422
|
||||
|
||||
|
||||
Parsing / Rendering
|
||||
-------------------
|
||||
|
||||
Parsing and rendering of HTTP data structures is heavily optimized and for most types there's currently no public API
|
||||
provided to parse (or render to) Strings or byte arrays.
|
||||
|
|
@ -3,4 +3,13 @@
|
|||
Akka HTTP
|
||||
=========
|
||||
|
||||
...
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
introduction
|
||||
configuration
|
||||
http-model
|
||||
server-side/low-level-server-side-api
|
||||
server-side/websocket-support
|
||||
routing-dsl/index
|
||||
client-side/index
|
||||
28
akka-docs-dev/rst/java/http/introduction.rst
Normal file
28
akka-docs-dev/rst/java/http/introduction.rst
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
Introduction
|
||||
============
|
||||
|
||||
The Akka HTTP modules implement a full server- and client-side HTTP stack on top of *akka-actor* and *akka-stream*. It's
|
||||
not a web-framework but rather a more general toolkit for providing and consuming HTTP-based services. While interaction
|
||||
with a browser is of course also in scope it is not the primary focus of Akka HTTP.
|
||||
|
||||
Akka HTTP follows a rather open design and many times offers several different API levels for "doing the same thing".
|
||||
You get to pick the API level of abstraction that is most suitable for your application.
|
||||
This means that, if you have trouble achieving something using a high-level API, there's a good chance that you can get
|
||||
it done with a low-level API, which offers more flexibility but might require you to write more application code.
|
||||
|
||||
Akka HTTP is structured into several modules:
|
||||
|
||||
akka-http-core
|
||||
A complete, mostly low-level, server- and client-side implementation of HTTP (incl. WebSockets)
|
||||
|
||||
akka-http
|
||||
Higher-level functionality, like (un)marshalling, (de)compression as well as a powerful DSL
|
||||
for defining HTTP-based APIs on the server-side
|
||||
|
||||
akka-http-testkit
|
||||
A test harness and set of utilities for verifying server-side service implementations
|
||||
|
||||
akka-http-jackson
|
||||
Predefined glue-code for (de)serializing custom types from/to JSON with jackson_
|
||||
|
||||
.. _jackson: https://github.com/FasterXML/jackson
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
.. _directives-java:
|
||||
|
||||
Directives
|
||||
==========
|
||||
|
||||
TODO
|
||||
88
akka-docs-dev/rst/java/http/routing-dsl/handlers.rst
Normal file
88
akka-docs-dev/rst/java/http/routing-dsl/handlers.rst
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
.. _handlers-java:
|
||||
|
||||
Handlers
|
||||
========
|
||||
|
||||
Handlers implement the actual application-defined logic for a certain trace in the routing tree. Most of the leaves of
|
||||
the routing tree will be routes created from handlers. Creating a ``Route`` from a handler is achieved using the
|
||||
``BasicDirectives.handleWith`` overloads. They come in several forms:
|
||||
|
||||
* with a single ``Handler`` argument
|
||||
* with a number ``n`` of ``RequestVal<T1>`` arguments and a ``HandlerN<T1, .., TN>`` argument
|
||||
* with a ``Class<?>`` and/or instance and a method name String argument and a variable number of ``RequestVal<?>``
|
||||
arguments
|
||||
|
||||
Simple Handler
|
||||
--------------
|
||||
|
||||
In its simplest form a ``Handler`` is a SAM class that defines application behavior
|
||||
by inspecting the ``RequestContext`` and returning a ``RouteResult``:
|
||||
|
||||
.. includecode:: /../../akka-http/src/main/scala/akka/http/javadsl/server/Handler.scala
|
||||
:include: handler
|
||||
|
||||
Such a handler inspects the ``RequestContext`` it receives and uses the ``RequestContext``'s methods to
|
||||
create a response:
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/HandlerExampleSpec.java
|
||||
:include: simple-handler
|
||||
|
||||
The handler can include any kind of logic but must return a ``RouteResult`` in the end which can only
|
||||
be created by using one of the ``RequestContext`` methods.
|
||||
|
||||
A handler instance can be used once or several times as shown in the full example:
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/HandlerExampleSpec.java
|
||||
:include: simple-handler-example-full
|
||||
|
||||
Handlers and Request Values
|
||||
---------------------------
|
||||
|
||||
In many cases, instead of manually inspecting the request, a handler will make use of :ref:`request-vals-java`
|
||||
to extract details from the request. This is possible using one of the other ``handleWith`` overloads that bind
|
||||
the values of one or more request values with a ``HandlerN`` instance to produce a ``Route``:
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/HandlerExampleSpec.java
|
||||
:include: handler2
|
||||
|
||||
The handler here implements multiplication of two integers. However, it doesn't need to specify where these
|
||||
parameters come from. In ``handleWith``, as many request values of the matching type have to be specified as the
|
||||
handler needs. This can be seen in the full example:
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/HandlerExampleSpec.java
|
||||
:include: handler2-example-full
|
||||
|
||||
Here, the handler is again being reused. First, in creating a route that expects URI parameters ``x`` and ``y``. This
|
||||
route is then used in the route structure. And second, the handler is used with another set of ``RequestVal`` in the
|
||||
route structure, this time representing segments from the URI path.
|
||||
|
||||
Handlers in Java 8
|
||||
------------------
|
||||
|
||||
In Java 8 handlers can be provided as function literals. The example from before then looks like this:
|
||||
|
||||
.. includecode:: /../../akka-http-tests-java8/src/test/java/docs/http/javadsl/server/HandlerExampleSpec.java
|
||||
:include: handler2-example-full
|
||||
|
||||
|
||||
Providing Handlers by Reflection
|
||||
--------------------------------
|
||||
|
||||
Using Java before Java 8, writing out handlers as (anonymous) classes can be unwieldy. Therefore, ``handleWith``
|
||||
overloads are provided that allow writing handler as simple methods and specifying them by name:
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/HandlerExampleSpec.java
|
||||
:include: reflective
|
||||
|
||||
The complete calculator example can then be written like this:
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/HandlerExampleSpec.java
|
||||
:include: reflective-example-full
|
||||
|
||||
There are alternative overloads for ``handleWith`` that take a ``Class`` instead of an object instance to refer to
|
||||
static methods. The referenced method must be publicly accessible.
|
||||
|
||||
Deferring Result Creation
|
||||
-------------------------
|
||||
|
||||
TODO
|
||||
17
akka-docs-dev/rst/java/http/routing-dsl/index.rst
Normal file
17
akka-docs-dev/rst/java/http/routing-dsl/index.rst
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
.. _http-high-level-server-side-api-java:
|
||||
|
||||
High-level Server-Side API
|
||||
==========================
|
||||
|
||||
...
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
overview
|
||||
routes
|
||||
directives/index
|
||||
request-vals/index
|
||||
handlers
|
||||
path-matchers
|
||||
marshalling
|
||||
6
akka-docs-dev/rst/java/http/routing-dsl/marshalling.rst
Normal file
6
akka-docs-dev/rst/java/http/routing-dsl/marshalling.rst
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.. _marshalling-java:
|
||||
|
||||
Marshalling & Unmarshalling
|
||||
===========================
|
||||
|
||||
TODO
|
||||
71
akka-docs-dev/rst/java/http/routing-dsl/overview.rst
Normal file
71
akka-docs-dev/rst/java/http/routing-dsl/overview.rst
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
.. _routing-java:
|
||||
|
||||
Routing DSL Overview
|
||||
====================
|
||||
|
||||
The Akka HTTP :ref:`http-low-level-server-side-api-java` provides a ``Flow``- or ``Function``-level interface that allows
|
||||
an application to respond to incoming HTTP requests by simply mapping requests to responses
|
||||
(excerpt from :ref:`Low-level server side example <http-low-level-server-side-example-java>`):
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/HttpServerExampleSpec.java
|
||||
:include: request-handler
|
||||
|
||||
While it'd be perfectly possible to define a complete REST API service purely by inspecting the incoming
|
||||
``HttpRequest`` this approach becomes somewhat unwieldy for larger services due to the amount of syntax "ceremony"
|
||||
required. Also, it doesn't help in keeping your service definition as DRY_ as you might like.
|
||||
|
||||
As an alternative Akka HTTP provides a flexible DSL for expressing your service behavior as a structure of
|
||||
composable elements (called :ref:`directives-java`) in a concise and readable way. Directives are assembled into a so called
|
||||
*route structure* which, at its top-level, can be used to create a handler ``Flow`` (or, alternatively, an
|
||||
async handler function) that can be directly supplied to a ``bind`` call.
|
||||
|
||||
Here's the complete example rewritten using the composable high-level API:
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/HighLevelServerExample.java
|
||||
:include: high-level-server-example
|
||||
|
||||
Heart of the high-level architecture is the route tree. It is a big expression of type ``Route``
|
||||
that is evaluated only once during startup time of your service. It completely describes how your service
|
||||
should react to any request.
|
||||
|
||||
The type ``Route`` is the basic building block of the route tree. It defines if and a how a request should
|
||||
be handled. Routes are composed to form the route tree in the following two ways.
|
||||
|
||||
A route can be wrapped by a "Directive" which adds some behavioral aspect to its wrapped "inner route". ``path("ping")`` is such
|
||||
a directive that implements a path filter, i.e. it only passes control to its inner route when the unmatched path
|
||||
matches ``"ping"``. Directives can be more versatile than this: A directive can also transform the request before
|
||||
passing it into its inner route or transform a response that comes out of its inner route. It's a general and powerful
|
||||
abstraction that allows to package any kind of HTTP processing into well-defined blocks that can be freely combined.
|
||||
akka-http defines a library of predefined directives and routes for all the various aspects of dealing with
|
||||
HTTP requests and responses.
|
||||
|
||||
Read more about :ref:`directives-java`.
|
||||
|
||||
The other way of composition is defining a list of ``Route`` alternatives. Alternative routes are tried one after
|
||||
the other until one route "accepts" the request and provides a response. Otherwise, a route can also "reject" a request,
|
||||
in which case further alternatives are explored. Alternatives are specified by passing a list of routes either
|
||||
to ``Directive.route()`` as in ``pathSingleSlash().route()`` or to directives that directly take a variable number
|
||||
of inner routes as argument like ``get()`` here.
|
||||
|
||||
Read more about :ref:`routes-java`.
|
||||
|
||||
Another important building block is a ``RequestVal<T>``. It represents a value that can be extracted from a
|
||||
request (like the URI parameter ``Parameters.stringValue("name")`` in the example) and which is then interpreted
|
||||
as a value of type ``T``. Examples of HTTP aspects represented by a ``RequestVal`` are URI parameters, HTTP form
|
||||
fields, details of the request like headers, URI, the entity, or authentication data.
|
||||
|
||||
Read more about :ref:`request-vals-java`.
|
||||
|
||||
The actual application-defined processing of a request is defined with a ``Handler`` instance or by specifying
|
||||
a handling method with reflection. A handler can receive the value of any request values and is converted into
|
||||
a ``Route`` by using one of the ``BasicDirectives.handleWith`` directives.
|
||||
|
||||
Read more about :ref:`handlers-java`.
|
||||
|
||||
Requests or responses often contain data that needs to be interpreted or rendered in some way.
|
||||
Akka-http provides the abstraction of ``Marshaller`` and ``Unmarshaller`` that define how domain model objects map
|
||||
to HTTP entities.
|
||||
|
||||
Read more about :ref:`marshalling-java`.
|
||||
|
||||
.. _DRY: http://en.wikipedia.org/wiki/Don%27t_repeat_yourself
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
.. _pathmatcher-dsl-java:
|
||||
|
||||
The PathMatcher DSL
|
||||
===================
|
||||
|
||||
TODO
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
.. _request-vals-java:
|
||||
|
||||
Request values
|
||||
==============
|
||||
|
||||
A request value of type ``RequestVal<T>`` is a typed structure that represents some aspect of the request
|
||||
that can be interpreted as a value of type ``T``. A ``RequestVal`` instance abstracts the knowledge about how
|
||||
to extract a certain value from the request and interpret it as a ``T``. It is used in combination with
|
||||
:ref:`handlers-java`.
|
||||
|
||||
The advantage of representing a request detail as a ``RequestVal`` instead of performing ad-hoc analysis of
|
||||
a request are:
|
||||
|
||||
* you can define an "inventory" of HTTP primitives for your application that you can reuse in many places of your
|
||||
application
|
||||
* automatic handling of errors when an expected value was not found in a request or if it could not be interpreted
|
||||
as the expected Java type
|
||||
|
||||
Note, that the Scala version of the routing DSL has no direct correspondent to RequestVals. Instead,
|
||||
a Scala-side ``Directive`` can have "extractions" that are reflected in the type of the ``Directive``.
|
||||
|
||||
Predefined Request values
|
||||
-------------------------
|
||||
|
||||
akka-http provides a set of predefined request values for request data commonly accessed in a web
|
||||
service.
|
||||
|
||||
These request values are defined:
|
||||
|
||||
* in ``RequestVals``: request values for basic data like URI components, request method, peer address, or the entity data
|
||||
* in ``Cookies``: request values representing cookies
|
||||
* in ``FormFields``: request values to access form fields unmarshalled to various primitive Java types
|
||||
* in ``Headers``:: request values to access request headers or header values
|
||||
* ``HttpBasicAuthenticator``: an abstract class to implement to create a request value representing a HTTP basic authenticated principal
|
||||
* in ``Parameters``: request values to access URI paramaters unmarshalled to various primitive Java types
|
||||
* in ``PathMatchers``: request values to match and access URI path segments
|
||||
* ``CustomRequestVal``: an abstract class to implement arbitrary custom request values
|
||||
90
akka-docs-dev/rst/java/http/routing-dsl/routes.rst
Normal file
90
akka-docs-dev/rst/java/http/routing-dsl/routes.rst
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
.. _routes-java:
|
||||
|
||||
Routes
|
||||
======
|
||||
|
||||
A ``Route`` itself is a function that operates on a ``RequestContext`` and returns a ``RouteResult``. The
|
||||
``RequestContext`` is a data structure that contains the current request and auxiliary data like the so far unmatched
|
||||
path of the request URI that gets passed through the route structure. It also contains the current ``ExecutionContext``
|
||||
and ``akka.stream.Materializer``, so that these don't have to be passed around manually.
|
||||
|
||||
|
||||
.. _request-context-java:
|
||||
|
||||
RequestContext
|
||||
--------------
|
||||
|
||||
The ``RequestContext`` achieves two goals: it allows access to request data and it is a factory for creating a
|
||||
``RouteResult``. A user-defined handler (see :ref:`handlers-java`) that is usually used at the leaf position of
|
||||
the route tree receives a ``RequestContext``, evaluates its content and then returns a result generated by one of
|
||||
the methods of the context.
|
||||
|
||||
.. _route-result-java:
|
||||
|
||||
RouteResult
|
||||
-----------
|
||||
|
||||
The ``RouteResult`` is an opaque structure that represents possible results of evaluating a route. A ``RouteResult``
|
||||
can only be created by using one of the methods of the ``RequestContext``. A result can either be a response, if
|
||||
it was generated by one of the ``completeX`` methods, it can be an eventual result, i.e. a ``Future<RouteResult`` if
|
||||
``completeWith`` was used or a rejection that contains information about why the route could not handle the request.
|
||||
|
||||
|
||||
Composing Routes
|
||||
----------------
|
||||
|
||||
Routes are composed to form the route tree in two principle ways.
|
||||
|
||||
A route can be wrapped by a "Directive" which adds some behavioral aspect to its wrapped "inner route". Such an aspect can
|
||||
be
|
||||
|
||||
* filtering requests to decide which requests will get to the inner route
|
||||
* transforming the request before passing it to the inner route
|
||||
* transforming the response (or more generally the route result) received from the inner route
|
||||
* applying side-effects around inner route processing, such as measuring the time taken to run the inner route
|
||||
|
||||
akka-http defines a library of predefined :ref:`directives-java` and routes for all the various aspects of dealing with
|
||||
HTTP requests and responses.
|
||||
|
||||
The other way of composition is defining a list of ``Route`` alternatives. Alternative routes are tried one after
|
||||
the other until one route "accepts" the request and provides a response. Otherwise, a route can also "reject" a request,
|
||||
in which case further alternatives are explored. Alternatives are specified by passing a list of routes either
|
||||
to ``Directive.route()`` as in ``path("xyz").route()`` or to directives that directly take a variable number
|
||||
of inner routes as argument like ``get()``.
|
||||
|
||||
.. _The Routing Tree-java:
|
||||
|
||||
The Routing Tree
|
||||
----------------
|
||||
|
||||
Essentially, when you combine routes via nesting and alternative, you build a routing
|
||||
structure that forms a tree. When a request comes in it is injected into this tree at the root and flows down through
|
||||
all the branches in a depth-first manner until either some node completes it or it is fully rejected.
|
||||
|
||||
Consider this schematic example::
|
||||
|
||||
val route =
|
||||
a.route(
|
||||
b.route(
|
||||
c.route(
|
||||
... // route 1
|
||||
),
|
||||
d.route(
|
||||
... // route 2
|
||||
),
|
||||
... // route 3
|
||||
),
|
||||
e.route(
|
||||
... // route 4
|
||||
)
|
||||
)
|
||||
|
||||
Here five directives form a routing tree.
|
||||
|
||||
- Route 1 will only be reached if directives ``a``, ``b`` and ``c`` all let the request pass through.
|
||||
- Route 2 will run if ``a`` and ``b`` pass, ``c`` rejects and ``d`` passes.
|
||||
- Route 3 will run if ``a`` and ``b`` pass, but ``c`` and ``d`` reject.
|
||||
|
||||
Route 3 can therefore be seen as a "catch-all" route that only kicks in, if routes chained into preceding positions
|
||||
reject. This mechanism can make complex filtering logic quite easy to implement: simply put the most
|
||||
specific cases up front and the most general cases in the back.
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
.. _http-low-level-server-side-api-java:
|
||||
|
||||
Low-Level Server-Side API
|
||||
=========================
|
||||
|
||||
Apart from the :ref:`HTTP Client <http-client-side-java>` Akka HTTP also provides an embedded,
|
||||
`Reactive-Streams`_-based, fully asynchronous HTTP/1.1 server implemented on top of :ref:`Akka Stream <streams-java>`.
|
||||
|
||||
It sports the following features:
|
||||
|
||||
- Full support for `HTTP persistent connections`_
|
||||
- Full support for `HTTP pipelining`_
|
||||
- Full support for asynchronous HTTP streaming including "chunked" transfer encoding accessible through an idiomatic API
|
||||
- Optional SSL/TLS encryption
|
||||
- Websocket support
|
||||
|
||||
.. _HTTP persistent connections: http://en.wikipedia.org/wiki/HTTP_persistent_connection
|
||||
.. _HTTP pipelining: http://en.wikipedia.org/wiki/HTTP_pipelining
|
||||
.. _Reactive-Streams: http://www.reactive-streams.org/
|
||||
|
||||
The server-side components of Akka HTTP are split into two layers:
|
||||
|
||||
1. The basic low-level server implementation in the ``akka-http-core`` module
|
||||
2. Higher-level functionality in the ``akka-http`` module
|
||||
|
||||
The low-level server (1) is scoped with a clear focus on the essential functionality of an HTTP/1.1 server:
|
||||
|
||||
- Connection management
|
||||
- Parsing and rendering of messages and headers
|
||||
- Timeout management (for requests and connections)
|
||||
- Response ordering (for transparent pipelining support)
|
||||
|
||||
All non-core features of typical HTTP servers (like request routing, file serving, compression, etc.) are left to
|
||||
the higher layers, they are not implemented by the ``akka-http-core``-level server itself.
|
||||
Apart from general focus this design keeps the server core small and light-weight as well as easy to understand and
|
||||
maintain.
|
||||
|
||||
Depending on your needs you can either use the low-level API directly or rely on the high-level
|
||||
:ref:`Routing DSL <http-high-level-server-side-api-java>` which can make the definition of more complex service logic much
|
||||
easier.
|
||||
|
||||
|
||||
Streams and HTTP
|
||||
----------------
|
||||
|
||||
The Akka HTTP server is implemented on top of :ref:`Akka Stream <streams-java>` and makes heavy use of it - in its
|
||||
implementation as well as on all levels of its API.
|
||||
|
||||
On the connection level Akka HTTP offers basically the same kind of interface as :ref:`Akka Stream IO <stream-io-java>`:
|
||||
A socket binding is represented as a stream of incoming connections. The application pulls connections from this stream
|
||||
source and, for each of them, provides a ``Flow<HttpRequest, HttpResponse, ?>`` to "translate" requests into responses.
|
||||
|
||||
Apart from regarding a socket bound on the server-side as a ``Source<IncomingConnection>`` and each connection as a
|
||||
``Source<HttpRequest>`` with a ``Sink<HttpResponse>`` the stream abstraction is also present inside a single HTTP
|
||||
message: The entities of HTTP requests and responses are generally modeled as a ``Source<ByteString>``. See also
|
||||
the :ref:`http-model-java` for more information on how HTTP messages are represented in Akka HTTP.
|
||||
|
||||
|
||||
Starting and Stopping
|
||||
---------------------
|
||||
|
||||
On the most basic level an Akka HTTP server is bound by invoking the ``bind`` method of the `akka.http.javadsl.Http`_
|
||||
extension:
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/HttpServerExampleSpec.java
|
||||
:include: binding-example
|
||||
|
||||
Arguments to the ``Http().bind`` method specify the interface and port to bind to and register interest in handling
|
||||
incoming HTTP connections. Additionally, the method also allows for the definition of socket options as well as a larger
|
||||
number of settings for configuring the server according to your needs.
|
||||
|
||||
The result of the ``bind`` method is a ``Source<Http.IncomingConnection>`` which must be drained by the application in
|
||||
order to accept incoming connections.
|
||||
The actual binding is not performed before this source is materialized as part of a processing pipeline. In
|
||||
case the bind fails (e.g. because the port is already busy) the materialized stream will immediately be terminated with
|
||||
a respective exception.
|
||||
The binding is released (i.e. the underlying socket unbound) when the subscriber of the incoming
|
||||
connection source has cancelled its subscription. Alternatively one can use the ``unbind()`` method of the
|
||||
``Http.ServerBinding`` instance that is created as part of the connection source's materialization process.
|
||||
The ``Http.ServerBinding`` also provides a way to get a hold of the actual local address of the bound socket, which is
|
||||
useful for example when binding to port zero (and thus letting the OS pick an available port).
|
||||
|
||||
.. _akka.http.javadsl.Http: @github@/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala
|
||||
|
||||
.. _http-low-level-server-side-example-java:
|
||||
|
||||
Request-Response Cycle
|
||||
----------------------
|
||||
|
||||
When a new connection has been accepted it will be published as an ``Http.IncomingConnection`` which consists
|
||||
of the remote address and methods to provide a ``Flow<HttpRequest, HttpResponse, ?>`` to handle requests coming in over
|
||||
this connection.
|
||||
|
||||
Requests are handled by calling one of the ``handleWithXXX`` methods with a handler, which can either be
|
||||
|
||||
- a ``Flow<HttpRequest, HttpResponse, ?>`` for ``handleWith``,
|
||||
- a function ``Function<HttpRequest, HttpResponse>`` for ``handleWithSyncHandler``,
|
||||
- a function ``Function<HttpRequest, Future<HttpResponse>>`` for ``handleWithAsyncHandler``.
|
||||
|
||||
Here is a complete example:
|
||||
|
||||
.. includecode:: ../../code/docs/http/javadsl/HttpServerExampleSpec.java
|
||||
:include: full-server-example
|
||||
|
||||
In this example, a request is handled by transforming the request stream with a function ``Function<HttpRequest, HttpResponse>``
|
||||
using ``handleWithSyncHandler`` (or equivalently, Akka Stream's ``map`` operator). Depending on the use case many
|
||||
other ways of providing a request handler are conceivable using Akka Stream's combinators.
|
||||
|
||||
If the application provides a ``Flow`` it is also the responsibility of the application to generate exactly one response
|
||||
for every request and that the ordering of responses matches the ordering of the associated requests (which is relevant
|
||||
if HTTP pipelining is enabled where processing of multiple incoming requests may overlap). When relying on
|
||||
``handleWithSyncHandler`` or ``handleWithAsyncHandler``, or the ``map`` or ``mapAsync`` stream operators, this
|
||||
requirement will be automatically fulfilled.
|
||||
|
||||
See :ref:`routing-java` for a more convenient high-level DSL to create request handlers.
|
||||
|
||||
Streaming Request/Response Entities
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Streaming of HTTP message entities is supported through subclasses of ``HttpEntity``. The application needs to be able
|
||||
to deal with streamed entities when receiving a request as well as, in many cases, when constructing responses.
|
||||
See :ref:`HttpEntity-java` for a description of the alternatives.
|
||||
|
||||
|
||||
Closing a connection
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The HTTP connection will be closed when the handling ``Flow`` cancels its upstream subscription or the peer closes the
|
||||
connection. An often times more convenient alternative is to explicitly add a ``Connection: close`` header to an
|
||||
``HttpResponse``. This response will then be the last one on the connection and the server will actively close the
|
||||
connection when it has been sent out.
|
||||
|
||||
|
||||
.. _serverSideHTTPS-java:
|
||||
|
||||
Server-Side HTTPS Support
|
||||
-------------------------
|
||||
|
||||
Akka HTTP supports TLS encryption on the server-side as well as on the :ref:`client-side <clientSideHTTPS-java>`.
|
||||
|
||||
The central vehicle for configuring encryption is the ``HttpsContext``, which can be created using ``HttpsContext.create``
|
||||
which is defined like this:
|
||||
|
||||
.. includecode:: /../../akka-http-core/src/main/java/akka/http/javadsl/HttpsContext.java
|
||||
:include: http-context-creation
|
||||
|
||||
On the server-side the ``bind``, and ``bindAndHandleXXX`` methods of the `akka.http.javadsl.Http`_ extension define an
|
||||
optional ``httpsContext`` parameter, which can receive the HTTPS configuration in the form of an ``HttpsContext``
|
||||
instance.
|
||||
If defined encryption is enabled on all accepted connections. Otherwise it is disabled (which is the default).
|
||||
|
||||
|
||||
Stand-Alone HTTP Layer Usage
|
||||
----------------------------
|
||||
|
||||
// TODO
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
.. _server-side-websocket-support-java:
|
||||
|
||||
Server-Side WebSocket Support
|
||||
=============================
|
||||
|
||||
TODO
|
||||
|
||||
For the time being, :ref:`see the Scala chapter on the same topic <server-side-websocket-support-scala>`.
|
||||
|
|
@ -334,7 +334,7 @@ In essence, the above guarantees are similar to what :class:`Actor`s provide, if
|
|||
stage as state of an actor, and the callbacks as the ``receive`` block of the actor.
|
||||
|
||||
.. warning::
|
||||
It is **not safe** to access the state of any custom stage outside of the callbacks that it provides, just like it
|
||||
It is **not safe** to access the state of any custom stage outside of the callbacks that it provides, just like it
|
||||
is unsafe to access the state of an actor from the outside. This means that Future callbacks should **not close over**
|
||||
internal state of custom stages because such access can be concurrent with the provided callbacks, leading to undefined
|
||||
behavior.
|
||||
|
|
@ -119,6 +119,7 @@ class HttpServerExampleSpec extends WordSpec with Matchers {
|
|||
}
|
||||
}
|
||||
|
||||
// `route` will be implicitly converted to `Flow` using `RouteResult.route2HandlerFlow`
|
||||
Http().bindAndHandle(route, "localhost", 8080)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,8 @@
|
|||
.. _client-side-websocket-support:
|
||||
|
||||
Client-Side WebSocket Support
|
||||
=============================
|
||||
|
||||
TODO
|
||||
Not yet implemented see 17275_.
|
||||
|
||||
.. _17275: https://github.com/akka/akka/issues/17275
|
||||
|
|
@ -72,8 +72,8 @@ Here are some examples how to construct an ``HttpResponse``:
|
|||
.. includecode:: ../../code/docs/http/scaladsl/ModelSpec.scala
|
||||
:include: construct-response
|
||||
|
||||
In addition to the simple ``HttpEntity`` constructors create an entity from a fixed ``String`` or ``ByteString`` shown
|
||||
as here the Akka HTTP model defines a number of subclasses of ``HttpEntity`` which allow body data to be specified as a
|
||||
In addition to the simple ``HttpEntity`` constructors which create an entity from a fixed ``String`` or ``ByteString``
|
||||
as shown here the Akka HTTP model defines a number of subclasses of ``HttpEntity`` which allow body data to be specified as a
|
||||
stream of bytes.
|
||||
|
||||
|
||||
|
|
@ -93,9 +93,9 @@ HttpEntity.Strict
|
|||
|
||||
HttpEntity.Default
|
||||
The general, unchunked HTTP/1.1 message entity.
|
||||
It has a known length and presents its data as a ``Source[ByteString]`` which can only materialized once.
|
||||
It has a known length and presents its data as a ``Source[ByteString]`` which can be only materialized once.
|
||||
It is an error if the provided source doesn't produce exactly as many bytes as specified.
|
||||
On the wire, a ``Strict`` entity and a ``Default`` entity cannot be distinguished.
|
||||
The distinction of ``Strict`` and ``Default`` is an API-only one. One the wire, both kinds of entities look the same.
|
||||
|
||||
|
||||
HttpEntity.Chunked
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ akka-http-core
|
|||
:language: none
|
||||
|
||||
|
||||
akka-http-core-scala
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
akka-http
|
||||
~~~~~~~~~
|
||||
|
||||
.. literalinclude:: ../../../../akka-http/src/main/resources/reference.conf
|
||||
:language: none
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ with a browser is of course also in scope it is not the primary focus of Akka HT
|
|||
Akka HTTP follows a rather open design and many times offers several different API levels for "doing the same thing".
|
||||
You get to pick the API level of abstraction that is most suitable for your application.
|
||||
This means that, if you have trouble achieving something using a high-level API, there's a good chance that you can get
|
||||
it done with a low-level API, which offers more flexibility but might require you do write more application code.
|
||||
it done with a low-level API, which offers more flexibility but might require you to write more application code.
|
||||
|
||||
Akka HTTP is structured into several modules:
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ It sports the following features:
|
|||
- Full support for `HTTP pipelining`_
|
||||
- Full support for asynchronous HTTP streaming including "chunked" transfer encoding accessible through an idiomatic API
|
||||
- Optional SSL/TLS encryption
|
||||
- Websocket support
|
||||
|
||||
.. _HTTP persistent connections: http://en.wikipedia.org/wiki/HTTP_persistent_connection
|
||||
.. _HTTP pipelining: http://en.wikipedia.org/wiki/HTTP_pipelining
|
||||
|
|
@ -26,7 +27,7 @@ The server-side components of Akka HTTP are split into two layers:
|
|||
The low-level server (1) is scoped with a clear focus on the essential functionality of an HTTP/1.1 server:
|
||||
|
||||
- Connection management
|
||||
- Parsing messages and headers
|
||||
- Parsing and rendering of messages and headers
|
||||
- Timeout management (for requests and connections)
|
||||
- Response ordering (for transparent pipelining support)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,9 @@ service definition as DRY_ as you might like.
|
|||
As an alternative Akka HTTP provides a flexible DSL for expressing your service behavior as a structure of
|
||||
composable elements (called :ref:`Directives`) in a concise and readable way. Directives are assembled into a so called
|
||||
*route structure* which, at its top-level, forms a handler ``Flow`` (or, alternatively, an async handler function) that
|
||||
can be directly supplied to a ``bind`` call.
|
||||
can be directly supplied to a ``bind`` call. The conversion from ``Route`` to flow can either be invoked explicitly
|
||||
using ``Route.handlerFlow`` or, otherwise, the conversion is also provided implicitly by
|
||||
``RouteResult.route2HandlerFlow`` [1]_.
|
||||
|
||||
For example, the service definition from above, written using the routing DSL, would look like this:
|
||||
|
||||
|
|
@ -37,5 +39,12 @@ regard.
|
|||
For learning how to work with the Routing DSL you should first understand the concept of :ref:`Routes`.
|
||||
|
||||
|
||||
.. [1] To be picked up automatically, the implicit conversion needs to be provided in the companion object of the source
|
||||
type. However, as ``Route`` is just a type alias for ``RequestContext => Future[RouteResult]``, there's no
|
||||
companion object for ``Route``. Fortunately, the `implicit scope`_ for finding an implicit conversion also
|
||||
includes all types that are "associated with any part" of the source type which in this case means that the
|
||||
implicit conversion will also be picked up from ``RouteResult.route2HandlerFlow`` automatically.
|
||||
|
||||
.. _Unfiltered: http://unfiltered.databinder.net/
|
||||
.. _DRY: http://en.wikipedia.org/wiki/Don%27t_repeat_yourself
|
||||
.. _DRY: http://en.wikipedia.org/wiki/Don%27t_repeat_yourself
|
||||
.. _implicit scope: http://www.scala-lang.org/files/archive/spec/2.11/07-implicits.html#implicit-parameters
|
||||
|
|
@ -1,3 +1,5 @@
|
|||
.. _server-side-websocket-support-scala:
|
||||
|
||||
Server-Side WebSocket Support
|
||||
=============================
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue