diff --git a/akka-docs-dev/rst/java/code/docs/http/javadsl/HandlerExampleSpec.java b/akka-docs-dev/rst/java/code/docs/http/javadsl/HandlerExampleSpec.java new file mode 100644 index 0000000000..79ce476add --- /dev/null +++ b/akka-docs-dev/rst/java/code/docs/http/javadsl/HandlerExampleSpec.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2009-2015 Typesafe Inc. + */ + +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 xParam = Parameters.intValue("x"); + RequestVal yParam = Parameters.intValue("y"); + + RequestVal xSegment = PathMatchers.integerNumber(); + RequestVal ySegment = PathMatchers.integerNumber(); + + //#handler2 + Handler2 multiply = + new Handler2() { + @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 xParam = Parameters.intValue("x"); + RequestVal yParam = Parameters.intValue("y"); + + RequestVal xSegment = PathMatchers.integerNumber(); + RequestVal 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 + } +} diff --git a/akka-docs-dev/rst/java/code/docs/http/javadsl/HighLevelServerExample.java b/akka-docs-dev/rst/java/code/docs/http/javadsl/HighLevelServerExample.java new file mode 100644 index 0000000000..4fd0e09052 --- /dev/null +++ b/akka-docs-dev/rst/java/code/docs/http/javadsl/HighLevelServerExample.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2009-2014 Typesafe Inc. + */ + +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 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() { + @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(), + "Hello world!") + ), + path("ping").route( + // return a simple `text/plain` response + complete("PONG!") + ), + path("hello").route( + // uses the route defined above + helloRoute + ) + ) + ); + } +} +//#high-level-server-example \ No newline at end of file diff --git a/akka-docs-dev/rst/java/code/docs/http/javadsl/HttpServerExampleSpec.java b/akka-docs-dev/rst/java/code/docs/http/javadsl/HttpServerExampleSpec.java new file mode 100644 index 0000000000..ff0731baaa --- /dev/null +++ b/akka-docs-dev/rst/java/code/docs/http/javadsl/HttpServerExampleSpec.java @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2009-2015 Typesafe Inc. + */ + +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> serverSource = + Http.get(system).bind("localhost", 8080, materializer); + + Future serverBindingFuture = + serverSource.to(Sink.foreach( + new Procedure() { + @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> serverSource = + Http.get(system).bind("localhost", 8080, materializer); + + //#request-handler + final Function requestHandler = + new Function() { + 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(), + "Hello world!"); + 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 serverBindingFuture = + serverSource.to(Sink.foreach( + new Procedure() { + @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(); + } +} diff --git a/akka-docs-dev/rst/java/code/docs/http/javadsl/ModelSpec.java b/akka-docs-dev/rst/java/code/docs/http/javadsl/ModelSpec.java new file mode 100644 index 0000000000..c7b4e28817 --- /dev/null +++ b/akka-docs-dev/rst/java/code/docs/http/javadsl/ModelSpec.java @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2009-2015 Typesafe Inc. + */ + +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 getCredentialsOfRequest(HttpRequest request) { + Option 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 +} diff --git a/akka-docs-dev/rst/java/http/client-side/connection-level.rst b/akka-docs-dev/rst/java/http/client-side/connection-level.rst new file mode 100644 index 0000000000..fc41a2d08f --- /dev/null +++ b/akka-docs-dev/rst/java/http/client-side/connection-level.rst @@ -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 `. \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/client-side/host-level.rst b/akka-docs-dev/rst/java/http/client-side/host-level.rst new file mode 100644 index 0000000000..3efcf7ace1 --- /dev/null +++ b/akka-docs-dev/rst/java/http/client-side/host-level.rst @@ -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 `. \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/client-side/https-support.rst b/akka-docs-dev/rst/java/http/client-side/https-support.rst new file mode 100644 index 0000000000..c9175c080d --- /dev/null +++ b/akka-docs-dev/rst/java/http/client-side/https-support.rst @@ -0,0 +1,8 @@ +.. _clientSideHTTPS-java: + +Client-Side HTTPS Support +========================= + +TODO + +For the time being, :ref:`see the Scala chapter on the same topic `. \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/client-side/index.rst b/akka-docs-dev/rst/java/http/client-side/index.rst new file mode 100644 index 0000000000..9e0a7261d5 --- /dev/null +++ b/akka-docs-dev/rst/java/http/client-side/index.rst @@ -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 \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/client-side/request-level.rst b/akka-docs-dev/rst/java/http/client-side/request-level.rst new file mode 100644 index 0000000000..7941d7dc40 --- /dev/null +++ b/akka-docs-dev/rst/java/http/client-side/request-level.rst @@ -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 `. \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/client-side/websocket-support.rst b/akka-docs-dev/rst/java/http/client-side/websocket-support.rst new file mode 100644 index 0000000000..91d0d0aa1c --- /dev/null +++ b/akka-docs-dev/rst/java/http/client-side/websocket-support.rst @@ -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 \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/configuration.rst b/akka-docs-dev/rst/java/http/configuration.rst new file mode 100644 index 0000000000..00c02e25a3 --- /dev/null +++ b/akka-docs-dev/rst/java/http/configuration.rst @@ -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 \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/http-model.rst b/akka-docs-dev/rst/java/http/http-model.rst new file mode 100644 index 0000000000..983831d24f --- /dev/null +++ b/akka-docs-dev/rst/java/http/http-model.rst @@ -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`` 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. diff --git a/akka-docs-dev/rst/java/http/index.rst b/akka-docs-dev/rst/java/http/index.rst index 99a1336ce9..a3547ea710 100644 --- a/akka-docs-dev/rst/java/http/index.rst +++ b/akka-docs-dev/rst/java/http/index.rst @@ -3,4 +3,13 @@ Akka HTTP ========= -... \ No newline at end of file +.. toctree:: + :maxdepth: 2 + + introduction + configuration + http-model + server-side/low-level-server-side-api + server-side/websocket-support + routing-dsl/index + client-side/index \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/introduction.rst b/akka-docs-dev/rst/java/http/introduction.rst new file mode 100644 index 0000000000..d7080f2a1c --- /dev/null +++ b/akka-docs-dev/rst/java/http/introduction.rst @@ -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 diff --git a/akka-docs-dev/rst/java/http/routing-dsl/directives/index.rst b/akka-docs-dev/rst/java/http/routing-dsl/directives/index.rst new file mode 100644 index 0000000000..c72ee6c145 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/directives/index.rst @@ -0,0 +1,6 @@ +.. _directives-java: + +Directives +========== + +TODO \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/routing-dsl/handlers.rst b/akka-docs-dev/rst/java/http/routing-dsl/handlers.rst new file mode 100644 index 0000000000..57b0710f3d --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/handlers.rst @@ -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`` arguments and a ``HandlerN`` 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 \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/routing-dsl/index.rst b/akka-docs-dev/rst/java/http/routing-dsl/index.rst new file mode 100644 index 0000000000..2ec80185ba --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/index.rst @@ -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 diff --git a/akka-docs-dev/rst/java/http/routing-dsl/marshalling.rst b/akka-docs-dev/rst/java/http/routing-dsl/marshalling.rst new file mode 100644 index 0000000000..fd1c2ec9b9 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/marshalling.rst @@ -0,0 +1,6 @@ +.. _marshalling-java: + +Marshalling & Unmarshalling +=========================== + +TODO \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/routing-dsl/overview.rst b/akka-docs-dev/rst/java/http/routing-dsl/overview.rst new file mode 100644 index 0000000000..3828ad8a42 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/overview.rst @@ -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 `): + +.. 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``. 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 diff --git a/akka-docs-dev/rst/java/http/routing-dsl/path-matchers.rst b/akka-docs-dev/rst/java/http/routing-dsl/path-matchers.rst new file mode 100644 index 0000000000..59a3834976 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/path-matchers.rst @@ -0,0 +1,6 @@ +.. _pathmatcher-dsl-java: + +The PathMatcher DSL +=================== + +TODO \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/routing-dsl/request-vals/index.rst b/akka-docs-dev/rst/java/http/routing-dsl/request-vals/index.rst new file mode 100644 index 0000000000..678db867e3 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/request-vals/index.rst @@ -0,0 +1,37 @@ +.. _request-vals-java: + +Request values +============== + +A request value of type ``RequestVal`` 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 diff --git a/akka-docs-dev/rst/java/http/routing-dsl/routes.rst b/akka-docs-dev/rst/java/http/routing-dsl/routes.rst new file mode 100644 index 0000000000..84dcbc86f6 --- /dev/null +++ b/akka-docs-dev/rst/java/http/routing-dsl/routes.rst @@ -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` Akka HTTP also provides an embedded, +`Reactive-Streams`_-based, fully asynchronous HTTP/1.1 server implemented on top of :ref:`Akka Stream `. + +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 ` 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 ` 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 `: +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`` to "translate" requests into responses. + +Apart from regarding a socket bound on the server-side as a ``Source`` and each connection as a +``Source`` with a ``Sink`` the stream abstraction is also present inside a single HTTP +message: The entities of HTTP requests and responses are generally modeled as a ``Source``. 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`` 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`` 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`` for ``handleWith``, + - a function ``Function`` for ``handleWithSyncHandler``, + - a function ``Function>`` 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`` +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 `. + +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 \ No newline at end of file diff --git a/akka-docs-dev/rst/java/http/server-side/websocket-support.rst b/akka-docs-dev/rst/java/http/server-side/websocket-support.rst new file mode 100644 index 0000000000..898bc9dd6e --- /dev/null +++ b/akka-docs-dev/rst/java/http/server-side/websocket-support.rst @@ -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 `. \ No newline at end of file diff --git a/akka-docs-dev/rst/java/stream-customize.rst b/akka-docs-dev/rst/java/stream-customize.rst index 536f7b1246..17993ad1e1 100644 --- a/akka-docs-dev/rst/java/stream-customize.rst +++ b/akka-docs-dev/rst/java/stream-customize.rst @@ -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. \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/code/docs/http/scaladsl/HttpServerExampleSpec.scala b/akka-docs-dev/rst/scala/code/docs/http/scaladsl/HttpServerExampleSpec.scala index 0129f68be5..b3278c2f6e 100644 --- a/akka-docs-dev/rst/scala/code/docs/http/scaladsl/HttpServerExampleSpec.scala +++ b/akka-docs-dev/rst/scala/code/docs/http/scaladsl/HttpServerExampleSpec.scala @@ -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) } diff --git a/akka-docs-dev/rst/scala/http/client-side/websocket-support.rst b/akka-docs-dev/rst/scala/http/client-side/websocket-support.rst index 69cdb560eb..522a8123c7 100644 --- a/akka-docs-dev/rst/scala/http/client-side/websocket-support.rst +++ b/akka-docs-dev/rst/scala/http/client-side/websocket-support.rst @@ -1,4 +1,8 @@ +.. _client-side-websocket-support: + Client-Side WebSocket Support ============================= -TODO \ No newline at end of file +Not yet implemented see 17275_. + +.. _17275: https://github.com/akka/akka/issues/17275 \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/common/http-model.rst b/akka-docs-dev/rst/scala/http/common/http-model.rst index 8fc9fca17f..578f57f39c 100644 --- a/akka-docs-dev/rst/scala/http/common/http-model.rst +++ b/akka-docs-dev/rst/scala/http/common/http-model.rst @@ -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 diff --git a/akka-docs-dev/rst/scala/http/configuration.rst b/akka-docs-dev/rst/scala/http/configuration.rst index 727b0d13cb..8760c0cc54 100644 --- a/akka-docs-dev/rst/scala/http/configuration.rst +++ b/akka-docs-dev/rst/scala/http/configuration.rst @@ -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 diff --git a/akka-docs-dev/rst/scala/http/introduction.rst b/akka-docs-dev/rst/scala/http/introduction.rst index 33e92e62e7..c21cffbbbf 100644 --- a/akka-docs-dev/rst/scala/http/introduction.rst +++ b/akka-docs-dev/rst/scala/http/introduction.rst @@ -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: diff --git a/akka-docs-dev/rst/scala/http/low-level-server-side-api.rst b/akka-docs-dev/rst/scala/http/low-level-server-side-api.rst index 380839ad14..0fcc13055f 100644 --- a/akka-docs-dev/rst/scala/http/low-level-server-side-api.rst +++ b/akka-docs-dev/rst/scala/http/low-level-server-side-api.rst @@ -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) diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/directives/basic-directives/extractFlowMaterializer.rst b/akka-docs-dev/rst/scala/http/routing-dsl/directives/basic-directives/extractMaterializer.rst similarity index 100% rename from akka-docs-dev/rst/scala/http/routing-dsl/directives/basic-directives/extractFlowMaterializer.rst rename to akka-docs-dev/rst/scala/http/routing-dsl/directives/basic-directives/extractMaterializer.rst diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/directives/basic-directives/withFlowMaterializer.rst b/akka-docs-dev/rst/scala/http/routing-dsl/directives/basic-directives/withMaterializer.rst similarity index 100% rename from akka-docs-dev/rst/scala/http/routing-dsl/directives/basic-directives/withFlowMaterializer.rst rename to akka-docs-dev/rst/scala/http/routing-dsl/directives/basic-directives/withMaterializer.rst diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/overview.rst b/akka-docs-dev/rst/scala/http/routing-dsl/overview.rst index 4ddc54d999..178f744061 100644 --- a/akka-docs-dev/rst/scala/http/routing-dsl/overview.rst +++ b/akka-docs-dev/rst/scala/http/routing-dsl/overview.rst @@ -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 \ No newline at end of file +.. _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 \ No newline at end of file diff --git a/akka-docs-dev/rst/scala/http/routing-dsl/websocket-support.rst b/akka-docs-dev/rst/scala/http/routing-dsl/websocket-support.rst index 3dced95dde..33cd90cb04 100644 --- a/akka-docs-dev/rst/scala/http/routing-dsl/websocket-support.rst +++ b/akka-docs-dev/rst/scala/http/routing-dsl/websocket-support.rst @@ -1,3 +1,5 @@ +.. _server-side-websocket-support-scala: + Server-Side WebSocket Support ============================= diff --git a/akka-http-core/src/main/java/akka/http/javadsl/HttpsContext.java b/akka-http-core/src/main/java/akka/http/javadsl/HttpsContext.java index b3820305a3..28c429d712 100644 --- a/akka-http-core/src/main/java/akka/http/javadsl/HttpsContext.java +++ b/akka-http-core/src/main/java/akka/http/javadsl/HttpsContext.java @@ -23,13 +23,16 @@ public abstract class HttpsContext { public abstract Option getClientAuth(); public abstract Option getSslParameters(); - + + //#http-context-creation public static HttpsContext create(SSLContext sslContext, Option> enabledCipherSuites, Option> enabledProtocols, Option clientAuth, - Option sslParameters) { + Option sslParameters) + //#http-context-creation + { return akka.http.scaladsl.HttpsContext.create(sslContext, enabledCipherSuites, enabledProtocols, clientAuth, sslParameters); } -} \ No newline at end of file +} diff --git a/akka-http-core/src/main/scala/akka/http/impl/model/JavaUri.scala b/akka-http-core/src/main/scala/akka/http/impl/model/JavaUri.scala index fd9e096849..6fe329a5d1 100644 --- a/akka-http-core/src/main/scala/akka/http/impl/model/JavaUri.scala +++ b/akka-http-core/src/main/scala/akka/http/impl/model/JavaUri.scala @@ -78,4 +78,6 @@ case class JavaUri(uri: sm.Uri) extends jm.Uri { def fragment(fragment: Option[String]): jm.Uri = t(_.copy(fragment = fragment)) def fragment(fragment: String): jm.Uri = t(_.withFragment(fragment)) + + override def toString: String = uri.toString } \ No newline at end of file diff --git a/akka-http-tests-java8/src/test/java/AllJavaTests.java b/akka-http-tests-java8/src/test/java/AllJavaTests.java index be754e78a5..bb81c717af 100644 --- a/akka-http-tests-java8/src/test/java/AllJavaTests.java +++ b/akka-http-tests-java8/src/test/java/AllJavaTests.java @@ -3,10 +3,14 @@ */ import akka.http.javadsl.server.HandlerBindingTest; +import docs.http.javadsl.server.HandlerExampleSpec; import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) -@Suite.SuiteClasses(HandlerBindingTest.class) +@Suite.SuiteClasses({ + HandlerBindingTest.class, + HandlerExampleSpec.class +}) public class AllJavaTests { } diff --git a/akka-http-tests-java8/src/test/java/docs/http/javadsl/server/HandlerExampleSpec.java b/akka-http-tests-java8/src/test/java/docs/http/javadsl/server/HandlerExampleSpec.java new file mode 100644 index 0000000000..2f5495d964 --- /dev/null +++ b/akka-http-tests-java8/src/test/java/docs/http/javadsl/server/HandlerExampleSpec.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2009-2015 Typesafe Inc. + */ + +package docs.http.javadsl.server; + +import akka.http.javadsl.model.HttpRequest; +import akka.http.javadsl.server.*; +import akka.http.javadsl.server.values.Parameters; +import akka.http.javadsl.testkit.JUnitRouteTest; +import akka.http.javadsl.testkit.TestRoute; +import org.junit.Test; + +public class HandlerExampleSpec extends JUnitRouteTest { + @Test + public void testCalculator() { + //#handler2-example-full + class TestHandler extends akka.http.javadsl.server.AllDirectives { + RequestVal xParam = Parameters.intValue("x"); + RequestVal yParam = Parameters.intValue("y"); + + //#handler2 + Handler2 multiply = + (ctx, x, y) -> ctx.complete("x * y = " + (x * y)); + + Route multiplyXAndYParam = handleWith(xParam, yParam, multiply); + //#handler2 + + Route createRoute() { + return route( + get( + pathPrefix("calculator").route( + path("multiply").route( + multiplyXAndYParam + ), + path("add").route( + handleWith(xParam, yParam, + (ctx, x, y) -> ctx.complete("x + y = " + (x + y))) + ) + ) + ) + ); + } + } + + // 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/add?x=12&y=42")) + .assertStatusCode(200) + .assertEntity("x + y = 54"); + //#handler2-example-full + } +} \ No newline at end of file diff --git a/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerApp.java b/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerApp.java index 5c5f5ebc99..983382b727 100644 --- a/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerApp.java +++ b/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/simple/SimpleServerApp.java @@ -23,6 +23,8 @@ public class SimpleServerApp extends HttpApp { static PathMatcher xSegment = PathMatchers.integerNumber(); static PathMatcher ySegment = PathMatchers.integerNumber(); + static RequestVal bodyAsName = RequestVals.entityAs(Unmarshallers.String()); + public static RouteResult multiply(RequestContext ctx, int x, int y) { int result = x * y; return ctx.complete(String.format("%d * %d = %d", x, y, result)); @@ -52,6 +54,13 @@ public class SimpleServerApp extends HttpApp { return ctx.complete(String.format("%d - %d = %d", xVal, yVal, result)); } }; + Handler1 helloPostHandler = + new Handler1() { + @Override + public RouteResult handle(RequestContext ctx, String s) { + return ctx.complete("Hello " + s + "!"); + } + }; return route( // matches the empty path @@ -73,6 +82,11 @@ public class SimpleServerApp extends HttpApp { path("multiplyAsync", xSegment, ySegment).route( // bind async handler by reflection handleWith(SimpleServerApp.class, "multiplyAsync", xSegment, ySegment) + ), + post( + path("hello").route( + handleWith(bodyAsName, helloPostHandler) + ) ) ); } diff --git a/akka-http-tests/src/test/java/akka/http/javadsl/server/examples/simple/SimpleServerTest.java b/akka-http-tests/src/test/java/akka/http/javadsl/server/examples/simple/SimpleServerTest.java index 5c65cbb947..b5b51c10dd 100644 --- a/akka-http-tests/src/test/java/akka/http/javadsl/server/examples/simple/SimpleServerTest.java +++ b/akka-http-tests/src/test/java/akka/http/javadsl/server/examples/simple/SimpleServerTest.java @@ -28,4 +28,13 @@ public class SimpleServerTest extends JUnitRouteTest { .assertStatusCode(200) .assertEntity("42 * 23 = 966"); } + + @Test + public void testPostWithBody() { + TestResponse response = route.run(HttpRequest.POST("/hello").withEntity("John")); + + response + .assertStatusCode(200) + .assertEntity("Hello John!"); + } } diff --git a/akka-http/src/main/scala/akka/http/impl/server/RequestContextImpl.scala b/akka-http/src/main/scala/akka/http/impl/server/RequestContextImpl.scala index f15a7aaf87..294b03cfa8 100644 --- a/akka-http/src/main/scala/akka/http/impl/server/RequestContextImpl.scala +++ b/akka-http/src/main/scala/akka/http/impl/server/RequestContextImpl.scala @@ -4,6 +4,10 @@ package akka.http.impl.server +import akka.http.javadsl.model.ContentType +import akka.http.scaladsl.model.HttpEntity +import akka.stream.Materializer + import scala.concurrent.{ ExecutionContext, Future } import akka.http.javadsl.{ model ⇒ jm } import akka.http.impl.util.JavaMapping.Implicits._ @@ -23,8 +27,11 @@ private[http] final case class RequestContextImpl(underlying: ScalaRequestContex def completeWith(futureResult: Future[RouteResult]): RouteResult = futureResult.flatMap { case r: RouteResultImpl ⇒ r.underlying - }(executionContext) + }(executionContext()) def complete(text: String): RouteResult = underlying.complete(text) + def complete(contentType: ContentType, text: String): RouteResult = + underlying.complete(HttpEntity(contentType.asScala, text)) + def completeWithStatus(statusCode: Int): RouteResult = completeWithStatus(jm.StatusCodes.get(statusCode)) def completeWithStatus(statusCode: jm.StatusCode): RouteResult = @@ -39,5 +46,6 @@ private[http] final case class RequestContextImpl(underlying: ScalaRequestContex def notFound(): RouteResult = underlying.reject() - def executionContext: ExecutionContext = underlying.executionContext + def executionContext(): ExecutionContext = underlying.executionContext + def materializer(): Materializer = underlying.materializer } diff --git a/akka-http/src/main/scala/akka/http/impl/server/Util.scala b/akka-http/src/main/scala/akka/http/impl/server/Util.scala new file mode 100644 index 0000000000..9c8ab9615f --- /dev/null +++ b/akka-http/src/main/scala/akka/http/impl/server/Util.scala @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2009-2015 Typesafe Inc. + */ + +package akka.http.impl.server + +import akka.http.scaladsl.unmarshalling.{ Unmarshaller, FromStringUnmarshaller } +import akka.http.scaladsl.util.FastFuture +import akka.japi.function.Function + +import scala.concurrent.{ Future, ExecutionContext } +import scala.util.Try + +object Util { + def fromStringUnmarshallerFromFunction[T](convert: Function[String, T]): FromStringUnmarshaller[T] = + scalaUnmarshallerFromFunction(convert) + def scalaUnmarshallerFromFunction[T, U](convert: Function[T, U]): Unmarshaller[T, U] = + new Unmarshaller[T, U] { + def apply(value: T)(implicit ec: ExecutionContext): Future[U] = FastFuture(Try(convert(value))) + } +} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/CustomRequestVal.scala b/akka-http/src/main/scala/akka/http/javadsl/server/CustomRequestVal.scala new file mode 100644 index 0000000000..1ced44426f --- /dev/null +++ b/akka-http/src/main/scala/akka/http/javadsl/server/CustomRequestVal.scala @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2009-2015 Typesafe Inc. + */ + +package akka.http.javadsl.server + +import akka.http.impl.server.{ RequestContextImpl, StandaloneExtractionImpl } +import akka.http.scaladsl.server.Directive1 +import akka.http.scaladsl.server.directives.BasicDirectives._ + +import scala.reflect.ClassTag + +/** + * Extend from this class and implement ``extractValue`` to create a custom request val. + */ +abstract class CustomRequestVal[T](clazz: Class[T]) extends StandaloneExtractionImpl[T]()(ClassTag(clazz)) { + final def directive: Directive1[T] = extract(ctx ⇒ extractValue(RequestContextImpl(ctx))) + + protected def extractValue(ctx: RequestContext): T +} diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/Handler.scala b/akka-http/src/main/scala/akka/http/javadsl/server/Handler.scala index e5d3823a90..ec878011af 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/Handler.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/Handler.scala @@ -11,9 +11,11 @@ package akka.http.javadsl.server * Use the methods in [[RequestContext]] to create a [[RouteResult]]. A handler mustn't * return [[null]] as the result. */ +//#handler trait Handler { def handle(ctx: RequestContext): RouteResult } +//#handler /** * A route handler with one additional argument. diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/RequestContext.scala b/akka-http/src/main/scala/akka/http/javadsl/server/RequestContext.scala index 8c3332d511..f2836b72ff 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/RequestContext.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/RequestContext.scala @@ -5,6 +5,7 @@ package akka.http.javadsl.server import akka.http.javadsl.model._ +import akka.stream.Materializer import akka.util.ByteString import scala.concurrent.{ ExecutionContext, Future } @@ -24,6 +25,12 @@ trait RequestContext { */ def unmatchedPath: String + /** Returns the ExecutionContext of this RequestContext */ + def executionContext(): ExecutionContext + + /** Returns the Materializer of this RequestContext */ + def materializer(): Materializer + /** * Completes the request with a value of type T and marshals it using the given * marshaller. @@ -40,6 +47,11 @@ trait RequestContext { */ def complete(text: String): RouteResult + /** + * Completes the request with the given string as an entity of the given type. + */ + def complete(contentType: ContentType, text: String): RouteResult + /** * Completes the request with the given status code and no entity. */ @@ -61,8 +73,5 @@ trait RequestContext { */ def notFound(): RouteResult - /** Returns the ExecutionContext of this RequestContext */ - def executionContext(): ExecutionContext - // FIXME: provide proper support for rejections, see #16438 } \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/Unmarshallers.scala b/akka-http/src/main/scala/akka/http/javadsl/server/Unmarshallers.scala index eadb955780..54aadf1fdb 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/Unmarshallers.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/Unmarshallers.scala @@ -4,13 +4,33 @@ package akka.http.javadsl.server -import akka.http.impl.server.UnmarshallerImpl -import akka.http.scaladsl.unmarshalling.{ FromMessageUnmarshaller, PredefinedFromEntityUnmarshallers } +import akka.http.impl.server.{ Util, UnmarshallerImpl } +import akka.http.javadsl.model.{ HttpEntity, HttpMessage } +import akka.http.scaladsl.unmarshalling.{ Unmarshaller ⇒ ScalaUnmarshaller, FromMessageUnmarshaller } +import akka.japi.function.Function +import akka.stream.Materializer +import akka.util.ByteString + +import scala.reflect.ClassTag object Unmarshallers { - def String: Unmarshaller[String] = - new UnmarshallerImpl[String]({ (ec, mat) ⇒ - implicit val _ = mat - implicitly[FromMessageUnmarshaller[String]] + def String: Unmarshaller[String] = withMat(implicit mat ⇒ implicitly) + def ByteString: Unmarshaller[ByteString] = withMat(implicit mat ⇒ implicitly) + def ByteArray: Unmarshaller[Array[Byte]] = withMat(implicit mat ⇒ implicitly) + def CharArray: Unmarshaller[Array[Char]] = withMat(implicit mat ⇒ implicitly) + + def fromMessage[T](convert: Function[HttpMessage, T], clazz: Class[T]): Unmarshaller[T] = + new UnmarshallerImpl[T]({ (ec, mat) ⇒ + Util.scalaUnmarshallerFromFunction(convert) + })(ClassTag(clazz)) + + def fromEntity[T](convert: Function[HttpEntity, T], clazz: Class[T]): Unmarshaller[T] = + new UnmarshallerImpl[T]({ (ec, mat) ⇒ + ScalaUnmarshaller.messageUnmarshallerFromEntityUnmarshaller(Util.scalaUnmarshallerFromFunction[HttpEntity, T](convert)) + })(ClassTag(clazz)) + + private def withMat[T: ClassTag](f: Materializer ⇒ FromMessageUnmarshaller[T]): Unmarshaller[T] = + new UnmarshallerImpl[T]({ (ec, mat) ⇒ + f(mat) }) } diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/directives/BasicDirectives.scala b/akka-http/src/main/scala/akka/http/javadsl/server/directives/BasicDirectives.scala index 04b66d51a5..d8589b2dc0 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/directives/BasicDirectives.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/directives/BasicDirectives.scala @@ -7,7 +7,7 @@ package akka.http.javadsl.server.directives import scala.annotation.varargs import java.lang.reflect.{ ParameterizedType, Method } -import akka.http.javadsl.model.{ StatusCode, HttpResponse } +import akka.http.javadsl.model.{ ContentType, StatusCode, HttpResponse } import akka.http.javadsl.server._ import akka.http.impl.server.RouteStructure._ import akka.http.impl.server._ @@ -30,6 +30,15 @@ abstract class BasicDirectives { def handle(ctx: RequestContext): RouteResult = ctx.complete(text) } + /** + * A route that completes the request with a static text + */ + def complete(contentType: ContentType, text: String): Route = + new OpaqueRoute() { + def handle(ctx: RequestContext): RouteResult = + ctx.complete(contentType, text) + } + /** * A route that completes the request with a static text */ diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/values/FormField.scala b/akka-http/src/main/scala/akka/http/javadsl/server/values/FormField.scala index 15ca4a9b2f..ffdbda4a6e 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/values/FormField.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/values/FormField.scala @@ -6,11 +6,14 @@ package akka.http.javadsl.server package values import java.{ lang ⇒ jl } +import akka.japi.function.Function import akka.japi.{ Option ⇒ JOption } -import akka.http.impl.server.FormFieldImpl +import akka.http.impl.server.{ Util, FormFieldImpl } import akka.http.scaladsl.unmarshalling._ +import scala.reflect.ClassTag + trait FormField[T] extends RequestVal[T] { def optional: RequestVal[JOption[T]] def withDefault(defaultValue: T): RequestVal[T] @@ -32,4 +35,9 @@ object FormFields { def hexShortValue(name: String): FormField[jl.Short] = FormFieldImpl(name.as(Unmarshaller.HexShort)) def hexIntValue(name: String): FormField[jl.Integer] = FormFieldImpl(name.as(Unmarshaller.HexInt)) def hexLongValue(name: String): FormField[jl.Long] = FormFieldImpl(name.as(Unmarshaller.HexLong)) + + def fromString[T](name: String, convert: Function[String, T], clazz: Class[T]): FormField[T] = { + implicit val tTag: ClassTag[T] = ClassTag(clazz) + FormFieldImpl(name.as(Util.fromStringUnmarshallerFromFunction(convert))) + } } \ No newline at end of file diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/values/Parameter.scala b/akka-http/src/main/scala/akka/http/javadsl/server/values/Parameter.scala index ddd6fed5ed..b1c4865517 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/values/Parameter.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/values/Parameter.scala @@ -11,12 +11,13 @@ import java.util.{ Map ⇒ JMap, Collection ⇒ JCollection } import akka.http.scaladsl.server.directives.ParameterDirectives import akka.http.scaladsl.unmarshalling.Unmarshaller +import akka.japi.function.Function import scala.reflect.ClassTag import akka.japi.{ Option ⇒ JOption } -import akka.http.impl.server.{ StandaloneExtractionImpl, ParameterImpl } +import akka.http.impl.server.{ Util, StandaloneExtractionImpl, ParameterImpl } import akka.http.javadsl.server.RequestVal import akka.http.scaladsl.server.Directive1 import akka.http.scaladsl.server.directives.ParameterDirectives.ParamMagnet @@ -63,5 +64,10 @@ object Parameters { StandaloneExtractionImpl(ParameterDirectives.parameterMultiMap.map(_.mapValues(_.asJavaCollection).asJava)) def asCollection: RequestVal[JCollection[JMap.Entry[String, String]]] = StandaloneExtractionImpl(ParameterDirectives.parameterSeq.map(_.map(e ⇒ new SimpleEntry(e._1, e._2): JMap.Entry[String, String]).asJavaCollection)) + + def fromString[T](name: String, convert: Function[String, T], clazz: Class[T]): Parameter[T] = { + implicit val tTag: ClassTag[T] = ClassTag(clazz) + ParameterImpl(name.as(Util.fromStringUnmarshallerFromFunction(convert))) + } } diff --git a/akka-http/src/main/scala/akka/http/javadsl/server/values/PathMatchers.scala b/akka-http/src/main/scala/akka/http/javadsl/server/values/PathMatchers.scala index 9871328141..5bcf35e56e 100644 --- a/akka-http/src/main/scala/akka/http/javadsl/server/values/PathMatchers.scala +++ b/akka-http/src/main/scala/akka/http/javadsl/server/values/PathMatchers.scala @@ -8,8 +8,9 @@ import java.{ lang ⇒ jl, util ⇒ ju } import akka.http.impl.server.PathMatcherImpl import akka.http.javadsl.server.RequestVal -import akka.http.scaladsl.server.{ PathMatcher0, PathMatcher1, PathMatchers ⇒ ScalaPathMatchers } +import akka.http.scaladsl.server.{ PathMatcher0, PathMatcher1, PathMatchers ⇒ ScalaPathMatchers, PathMatcher ⇒ ScalaPathMatcher } import akka.japi.Option +import akka.japi.function.Function import scala.collection.JavaConverters._ import scala.reflect.ClassTag @@ -50,6 +51,9 @@ object PathMatchers { def rest: PathMatcher[String] = matcher(_.Rest) + def segmentFromString[T](convert: Function[String, T], clazz: Class[T]): PathMatcher[T] = + matcher(_ ⇒ ScalaPathMatchers.Segment.map(convert(_)))(ClassTag(clazz)) + private def matcher[T: ClassTag](scalaMatcher: ScalaPathMatchers.type ⇒ PathMatcher1[T]): PathMatcher[T] = new PathMatcherImpl[T](scalaMatcher(ScalaPathMatchers)) private def matcher0(scalaMatcher: ScalaPathMatchers.type ⇒ PathMatcher0): PathMatcher[Void] = diff --git a/akka-http/src/main/scala/akka/http/scaladsl/server/Route.scala b/akka-http/src/main/scala/akka/http/scaladsl/server/Route.scala index a2642ec9f4..01e00769fb 100644 --- a/akka-http/src/main/scala/akka/http/scaladsl/server/Route.scala +++ b/akka-http/src/main/scala/akka/http/scaladsl/server/Route.scala @@ -31,6 +31,8 @@ object Route { /** * Turns a `Route` into a server flow. + * + * This conversion is also implicitly available through [[RouteResult.route2HandlerFlow]]. */ def handlerFlow(route: Route)(implicit setup: RoutingSetup): Flow[HttpRequest, HttpResponse, Unit] = Flow[HttpRequest].mapAsync(1)(asyncHandler(route))