* !htt #18919 #19519 Align Java HTTP server DSL with Scala This commits replaces the Java HTTP server DSL with a Java-8 centric one which exposes all scala DSL concepts to be usable from Java, including custom directives, (un)marshallers, rejections, headers, and type safety for path and query parameters. * Add RequestContext and RouteResult to Java DSL fix websockets WIP bring java docs up to date. This applies some updates to the root-level documentation * [htp] Fix java documentation to correctly mention timeouts Timeouts are configured the same in Java and Scala. Hence, linking to the scala docs for timeouts from Java. * =htc fix optionalHeaderValueByType in Java * =htt #20200 fix java testkit always using NoLogging instead logger * +htt actually run new javadsl tests, allow overriding config * =htt improve javadsl test infra with more details when fails * =htt fix bug in wrong path matcher exposed * +htp add missing remaining path matcher * =htp Java DSL cookie tests fixed * =htt Java DSL ParameterDirectivesTest fixed Protect the tweets from scalariform Incorrect response expectations in cache condition directives spec fixed * =htt Path directives for Java DSL * +!htt PathMatchers rewritten, made uniform and tests passing * Bugfix in java reject and a little test-boyscouting * Revert "Incorrect response expectations in cache condition directives spec fixed" This reverts commit cd50e89d45db010309f8249b090ea654ebb11c7a. * +htc HttpAPIsTest is compile time only, not for running Also, moved from the client package since not strictly a client test. SecurityDirectives passing Two faulty tests and two actual bugs. Fix for cache condition spec not working * Not sending in Unit instad of the implicit magnet in the test * HeaderMagnet now works as expected * Java API added for - and + on DateTime PetStore example and test fixed * Annotations to make marshalling work without default constructor * Made model class immutable Incorrect tests fixed Some scaladoc boyscouting as bonus * =htt RequestValTest sprinkled out across multiple directive tests Client ip extraction test with incorrect header name fixed. * =htt Incorrect CodingDirectivesTest fixed. * =htt Bugfix for Java Unmarshaller.firstOf and fixes to JavaRouteTest * =htt MarshallerTest fixed * Missing seal signature added to JavaDSL * More consistent (with Scala) test kit setup for Java * missing Javadocs added * Thread.sleep in default exception handler removed * =htt copy directive docs, prepare for finishing it up * +htt SecurityDirectives.authorize variants and test coverage added * +htt Custom headers in Java DSL * =htt WIP on java docs * +htp add missing parameterOrDefault directive Fixed a lot of doc warnings * =htc intense progress on javadsl docs * =htc #20470 Link to issue about docs and fix compile error compile, migration guide don't mima check http-experimental * =htt Java DSL doc warnings fixed. Only `Could not lex literal_block` ones left now * =htc fix mima settings * =doc fix MethodDirectives doc test with custom method * =htc fix coding directives spec after bad merge * =htc fix concat being corresponding to route() in javadsl * =htt Disable consistency check for route/concat as it fails only on ci server * !htt Minor fixes to PathMatchers
This commit is contained in:
parent
094c8974ed
commit
29029be31d
381 changed files with 12616 additions and 6630 deletions
|
|
@ -4,27 +4,28 @@
|
|||
|
||||
package docs.http.javadsl.server;
|
||||
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import akka.http.javadsl.model.FormData;
|
||||
import akka.http.javadsl.model.HttpRequest;
|
||||
import akka.http.javadsl.server.Route;
|
||||
import akka.http.javadsl.server.values.FormField;
|
||||
import akka.http.javadsl.server.values.FormFields;
|
||||
import akka.http.javadsl.server.StringUnmarshallers;
|
||||
import akka.http.javadsl.server.StringUnmarshaller;
|
||||
import akka.http.javadsl.server.Unmarshaller;
|
||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||
import akka.japi.Pair;
|
||||
import org.junit.Test;
|
||||
|
||||
public class FormFieldRequestValsExampleTest extends JUnitRouteTest {
|
||||
|
||||
@Test
|
||||
public void testFormFieldVals() {
|
||||
//#simple
|
||||
FormField<String> name = FormFields.stringValue("name");
|
||||
FormField<Integer> age = FormFields.intValue("age");
|
||||
|
||||
final Route route =
|
||||
route(
|
||||
handleWith2(name, age, (ctx, n, a) ->
|
||||
ctx.complete(String.format("Name: %s, age: %d", n, a))
|
||||
formField("name", n ->
|
||||
formField(StringUnmarshallers.INTEGER, "age", a ->
|
||||
complete(String.format("Name: %s, age: %d", n, a))
|
||||
)
|
||||
);
|
||||
|
||||
|
|
@ -44,13 +45,11 @@ public class FormFieldRequestValsExampleTest extends JUnitRouteTest {
|
|||
@Test
|
||||
public void testFormFieldValsUnmarshaling() {
|
||||
//#custom-unmarshal
|
||||
FormField<SampleId> sampleId = FormFields.fromString("id", SampleId.class, s -> new SampleId(Integer.valueOf(s)));
|
||||
Unmarshaller<String, SampleId> SAMPLE_ID = StringUnmarshaller.sync(s -> new SampleId(Integer.valueOf(s)));
|
||||
|
||||
final Route route =
|
||||
route(
|
||||
handleWith1(sampleId, (ctx, sid) ->
|
||||
ctx.complete(String.format("SampleId: %s", sid.id))
|
||||
)
|
||||
formField(SAMPLE_ID, "id", sid ->
|
||||
complete(String.format("SampleId: %s", sid.id))
|
||||
);
|
||||
|
||||
// tests:
|
||||
|
|
|
|||
|
|
@ -4,28 +4,23 @@
|
|||
|
||||
package docs.http.javadsl.server;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import akka.http.javadsl.model.HttpRequest;
|
||||
import akka.http.javadsl.model.headers.Host;
|
||||
import akka.http.javadsl.model.headers.RawHeader;
|
||||
import akka.http.javadsl.server.RequestVal;
|
||||
import akka.http.javadsl.server.Route;
|
||||
import akka.http.javadsl.server.values.Headers;
|
||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||
import org.junit.Test;
|
||||
|
||||
public class HeaderRequestValsExampleTest extends JUnitRouteTest {
|
||||
|
||||
@Test
|
||||
public void testHeaderVals() {
|
||||
//#by-class
|
||||
// extract the entire header instance:
|
||||
RequestVal<Host> host = Headers.byClass(Host.class).instance();
|
||||
|
||||
final Route route =
|
||||
route(
|
||||
handleWith1(host, (ctx, h) ->
|
||||
ctx.complete(String.format("Host header was: %s", h.host()))
|
||||
)
|
||||
extractHost(host ->
|
||||
complete(String.format("Host header was: %s", host))
|
||||
);
|
||||
|
||||
// tests:
|
||||
|
|
@ -41,14 +36,11 @@ public class HeaderRequestValsExampleTest extends JUnitRouteTest {
|
|||
@Test
|
||||
public void testHeaderByName() {
|
||||
//#by-name
|
||||
// extract the `value` of the header:
|
||||
final RequestVal<String> XFishName = Headers.byName("X-Fish-Name").value();
|
||||
|
||||
final Route route =
|
||||
route(
|
||||
handleWith1(XFishName, (ctx, xFishName) ->
|
||||
ctx.complete(String.format("The `X-Fish-Name` header's value was: %s", xFishName))
|
||||
)
|
||||
// extract the `value` of the header:
|
||||
headerValueByName("X-Fish-Name", xFishName ->
|
||||
complete(String.format("The `X-Fish-Name` header's value was: %s", xFishName))
|
||||
);
|
||||
|
||||
// tests:
|
||||
|
|
|
|||
|
|
@ -5,28 +5,41 @@
|
|||
package docs.http.javadsl.server;
|
||||
|
||||
//#binding-failure-high-level-example
|
||||
|
||||
import akka.NotUsed;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.http.scaladsl.Http;
|
||||
import akka.http.javadsl.ConnectHttp;
|
||||
import akka.http.javadsl.ServerBinding;
|
||||
import akka.http.javadsl.model.HttpRequest;
|
||||
import akka.http.javadsl.model.HttpResponse;
|
||||
import akka.http.javadsl.server.Route;
|
||||
import akka.http.javadsl.Http;
|
||||
import akka.stream.ActorMaterializer;
|
||||
import akka.stream.javadsl.Flow;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
|
||||
public class HighLevelServerBindFailureExample {
|
||||
public static void main(String[] args) throws IOException {
|
||||
// boot up server using the route as defined below
|
||||
final ActorSystem system = ActorSystem.create();
|
||||
public static void main(String[] args) throws IOException {
|
||||
// boot up server using the route as defined below
|
||||
final ActorSystem system = ActorSystem.create();
|
||||
final ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
// HttpApp.bindRoute expects a route being provided by HttpApp.createRoute
|
||||
CompletionStage<Http.ServerBinding> bindingFuture =
|
||||
new HighLevelServerExample().bindRoute("localhost", 8080, system);
|
||||
// HttpApp.bindRoute expects a route being provided by HttpApp.createRoute
|
||||
final HighLevelServerExample app = new HighLevelServerExample();
|
||||
final Route route = app.createRoute();
|
||||
|
||||
bindingFuture.exceptionally(failure -> {
|
||||
System.err.println("Something very bad happened! " + failure.getMessage());
|
||||
system.terminate();
|
||||
return null;
|
||||
});
|
||||
final Flow<HttpRequest, HttpResponse, NotUsed> handler = route.flow(system, materializer);
|
||||
final CompletionStage<ServerBinding> binding = Http.get(system).bindAndHandle(handler, ConnectHttp.toHost("127.0.0.1", 8080), materializer);
|
||||
|
||||
system.terminate();
|
||||
}
|
||||
binding.exceptionally(failure -> {
|
||||
System.err.println("Something very bad happened! " + failure.getMessage());
|
||||
system.terminate();
|
||||
return null;
|
||||
});
|
||||
|
||||
system.terminate();
|
||||
}
|
||||
}
|
||||
//#binding-failure-high-level-example
|
||||
|
|
|
|||
|
|
@ -5,65 +5,73 @@
|
|||
package docs.http.javadsl.server;
|
||||
|
||||
//#high-level-server-example
|
||||
|
||||
import akka.NotUsed;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.http.javadsl.ConnectHttp;
|
||||
import akka.http.javadsl.Http;
|
||||
import akka.http.javadsl.ServerBinding;
|
||||
import akka.http.javadsl.model.ContentTypes;
|
||||
import akka.http.javadsl.model.MediaTypes;
|
||||
import akka.http.javadsl.server.*;
|
||||
import akka.http.javadsl.server.values.Parameters;
|
||||
import akka.http.javadsl.model.HttpEntities;
|
||||
import akka.http.javadsl.model.HttpRequest;
|
||||
import akka.http.javadsl.model.HttpResponse;
|
||||
import akka.http.javadsl.server.AllDirectives;
|
||||
import akka.http.javadsl.server.Route;
|
||||
import akka.stream.ActorMaterializer;
|
||||
import akka.stream.javadsl.Flow;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
|
||||
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();
|
||||
public class HighLevelServerExample extends AllDirectives {
|
||||
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.terminate();
|
||||
}
|
||||
// HttpApp.bindRoute expects a route being provided by HttpApp.createRoute
|
||||
final HighLevelServerExample app = new HighLevelServerExample();
|
||||
|
||||
// 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");
|
||||
final Http http = Http.get(system);
|
||||
final ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
@Override
|
||||
public Route createRoute() {
|
||||
// This handler generates responses to `/hello?name=XXX` requests
|
||||
Route helloRoute =
|
||||
handleWith1(name,
|
||||
// in Java 8 the following becomes simply
|
||||
// (ctx, name) -> ctx.complete("Hello " + name + "!")
|
||||
new Handler1<String>() {
|
||||
@Override
|
||||
public RouteResult apply(RequestContext ctx, String name) {
|
||||
return ctx.complete("Hello " + name + "!");
|
||||
}
|
||||
});
|
||||
final Flow<HttpRequest, HttpResponse, NotUsed> routeFlow = app.createRoute().flow(system, materializer);
|
||||
final CompletionStage<ServerBinding> binding = http.bindAndHandle(routeFlow, ConnectHttp.toHost("localhost", 8080), materializer);
|
||||
|
||||
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(ContentTypes.TEXT_HTML_UTF8,
|
||||
"<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
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
System.out.println("Type RETURN to exit");
|
||||
System.in.read();
|
||||
|
||||
binding
|
||||
.thenCompose(ServerBinding::unbind)
|
||||
.thenAccept(unbound -> system.terminate());
|
||||
}
|
||||
|
||||
public Route createRoute() {
|
||||
// This handler generates responses to `/hello?name=XXX` requests
|
||||
Route helloRoute =
|
||||
parameterOptional("name", optName -> {
|
||||
String name = optName.orElse("Mister X");
|
||||
return complete("Hello " + name + "!");
|
||||
});
|
||||
|
||||
return
|
||||
// here the complete behavior for this server is defined
|
||||
|
||||
// only handle GET requests
|
||||
get(() -> route(
|
||||
// matches the empty path
|
||||
pathSingleSlash(() ->
|
||||
// return a constant string with a certain content type
|
||||
complete(HttpEntities.create(ContentTypes.TEXT_HTML_UTF8, "<html><body>Hello world!</body></html>"))
|
||||
),
|
||||
path("ping", () ->
|
||||
// return a simple `text/plain` response
|
||||
complete("PONG!")
|
||||
),
|
||||
path("hello", () ->
|
||||
// uses the route defined above
|
||||
helloRoute
|
||||
)
|
||||
));
|
||||
}
|
||||
}
|
||||
//#high-level-server-example
|
||||
//#high-level-server-example
|
||||
|
|
|
|||
|
|
@ -4,59 +4,35 @@
|
|||
package docs.http.javadsl.server;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import akka.http.javadsl.model.HttpRequest;
|
||||
import akka.http.javadsl.model.headers.Host;
|
||||
import akka.http.javadsl.server.Handler1;
|
||||
import akka.http.javadsl.server.RequestContext;
|
||||
import akka.http.javadsl.server.Route;
|
||||
import akka.http.javadsl.server.RouteResult;
|
||||
import akka.http.javadsl.server.values.BasicCredentials;
|
||||
import akka.http.javadsl.server.values.HttpBasicAuthenticator;
|
||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||
import akka.http.scaladsl.model.headers.Authorization;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
|
||||
import org.junit.Test;
|
||||
import scala.Option;
|
||||
import scala.concurrent.Future;
|
||||
|
||||
public class HttpBasicAuthenticatorExample extends JUnitRouteTest {
|
||||
|
||||
private final String hardcodedPassword = "correcthorsebatterystaple";
|
||||
|
||||
private Optional<String> authenticate(Optional<ProvidedCredentials> creds) {
|
||||
// this is where your actual authentication logic would go
|
||||
return creds
|
||||
.filter(c -> c.verify(hardcodedPassword)) // Only allow users that provide the right password
|
||||
.map(c -> c.identifier()); // Provide the username down to the inner route
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBasicAuthenticator() {
|
||||
//#basic-authenticator-java
|
||||
final HttpBasicAuthenticator<String> authentication = new HttpBasicAuthenticator<String>("My realm") {
|
||||
|
||||
private final String hardcodedPassword = "correcthorsebatterystaple";
|
||||
|
||||
public CompletionStage<Optional<String>> authenticate(BasicCredentials credentials) {
|
||||
// this is where your actual authentication logic would go
|
||||
if (credentials.available() && // no anonymous access
|
||||
credentials.verify(hardcodedPassword)) {
|
||||
return authenticateAs(credentials.identifier());
|
||||
} else {
|
||||
return refuseAccess();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
final Route route =
|
||||
authentication.route(
|
||||
handleWith1(
|
||||
authentication,
|
||||
new Handler1<String>() {
|
||||
public RouteResult apply(RequestContext ctx, String user) {
|
||||
return ctx.complete("Hello " + user + "!");
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
authenticateBasic("My realm", this::authenticate, user ->
|
||||
complete("Hello " + user + "!")
|
||||
);
|
||||
|
||||
|
||||
// tests:
|
||||
final HttpRequest okRequest =
|
||||
HttpRequest
|
||||
|
|
|
|||
|
|
@ -6,18 +6,11 @@ package docs.http.javadsl.server;
|
|||
|
||||
import akka.NotUsed;
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.dispatch.OnFailure;
|
||||
import akka.http.javadsl.ConnectHttp;
|
||||
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.ContentTypes;
|
||||
import akka.http.javadsl.model.HttpMethods;
|
||||
import akka.http.javadsl.model.HttpRequest;
|
||||
import akka.http.javadsl.model.HttpResponse;
|
||||
import akka.http.javadsl.model.Uri;
|
||||
import akka.http.scaladsl.model.HttpEntity;
|
||||
import akka.japi.function.Function;
|
||||
import akka.stream.ActorMaterializer;
|
||||
import akka.stream.Materializer;
|
||||
|
|
@ -34,180 +27,182 @@ import java.util.concurrent.TimeUnit;
|
|||
@SuppressWarnings("unused")
|
||||
public class HttpServerExampleDocTest {
|
||||
|
||||
public static void bindingExample() throws Exception {
|
||||
//#binding-example
|
||||
ActorSystem system = ActorSystem.create();
|
||||
Materializer materializer = ActorMaterializer.create(system);
|
||||
public static void bindingExample() throws Exception {
|
||||
//#binding-example
|
||||
ActorSystem system = ActorSystem.create();
|
||||
Materializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
Source<IncomingConnection, CompletionStage<ServerBinding>> serverSource =
|
||||
Http.get(system).bind(ConnectHttp.toHost("localhost", 8080), materializer);
|
||||
Source<IncomingConnection, CompletionStage<ServerBinding>> serverSource =
|
||||
Http.get(system).bind(ConnectHttp.toHost("localhost", 8080), materializer);
|
||||
|
||||
CompletionStage<ServerBinding> serverBindingFuture =
|
||||
serverSource.to(Sink.foreach(connection -> {
|
||||
System.out.println("Accepted new connection from " + connection.remoteAddress());
|
||||
// ... and then actually handle the connection
|
||||
}
|
||||
)).run(materializer);
|
||||
//#binding-example
|
||||
serverBindingFuture.toCompletableFuture().get(3, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public static void bindingFailureExample() throws Exception {
|
||||
//#binding-failure-handling
|
||||
ActorSystem system = ActorSystem.create();
|
||||
Materializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
Source<IncomingConnection, CompletionStage<ServerBinding>> serverSource =
|
||||
Http.get(system).bind(ConnectHttp.toHost("localhost", 80), materializer);
|
||||
|
||||
CompletionStage<ServerBinding> serverBindingFuture =
|
||||
serverSource.to(Sink.foreach(connection -> {
|
||||
System.out.println("Accepted new connection from " + connection.remoteAddress());
|
||||
// ... and then actually handle the connection
|
||||
}
|
||||
)).run(materializer);
|
||||
|
||||
serverBindingFuture.whenCompleteAsync((binding, failure) -> {
|
||||
// possibly report the failure somewhere...
|
||||
}, system.dispatcher());
|
||||
//#binding-failure-handling
|
||||
serverBindingFuture.toCompletableFuture().get(3, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public static void connectionSourceFailureExample() throws Exception {
|
||||
//#incoming-connections-source-failure-handling
|
||||
ActorSystem system = ActorSystem.create();
|
||||
Materializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
Source<IncomingConnection, CompletionStage<ServerBinding>> serverSource =
|
||||
Http.get(system).bind(ConnectHttp.toHost("localhost", 8080), materializer);
|
||||
|
||||
Flow<IncomingConnection, IncomingConnection, NotUsed> failureDetection =
|
||||
Flow.of(IncomingConnection.class).watchTermination((notUsed, termination) -> {
|
||||
termination.whenComplete((done, cause) -> {
|
||||
if (cause != null) {
|
||||
// signal the failure to external monitoring service!
|
||||
}
|
||||
});
|
||||
return NotUsed.getInstance();
|
||||
});
|
||||
|
||||
CompletionStage<ServerBinding> serverBindingFuture =
|
||||
serverSource
|
||||
.via(failureDetection) // feed signals through our custom stage
|
||||
.to(Sink.foreach(connection -> {
|
||||
System.out.println("Accepted new connection from " + connection.remoteAddress());
|
||||
// ... and then actually handle the connection
|
||||
}))
|
||||
.run(materializer);
|
||||
//#incoming-connections-source-failure-handling
|
||||
serverBindingFuture.toCompletableFuture().get(3, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public static void connectionStreamFailureExample() throws Exception {
|
||||
//#connection-stream-failure-handling
|
||||
ActorSystem system = ActorSystem.create();
|
||||
Materializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
Source<IncomingConnection, CompletionStage<ServerBinding>> serverSource =
|
||||
Http.get(system).bind(ConnectHttp.toHost("localhost", 8080), materializer);
|
||||
|
||||
Flow<HttpRequest, HttpRequest, NotUsed> failureDetection =
|
||||
Flow.of(HttpRequest.class)
|
||||
.watchTermination((notUsed, termination) -> {
|
||||
termination.whenComplete((done, cause) -> {
|
||||
if (cause != null) {
|
||||
// signal the failure to external monitoring service!
|
||||
}
|
||||
});
|
||||
return NotUsed.getInstance();
|
||||
});
|
||||
|
||||
Flow<HttpRequest, HttpResponse, NotUsed> httpEcho =
|
||||
Flow.of(HttpRequest.class)
|
||||
.via(failureDetection)
|
||||
.map(request -> {
|
||||
Source<ByteString, Object> bytes = request.entity().getDataBytes();
|
||||
HttpEntity.Chunked entity = HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8, bytes);
|
||||
|
||||
return HttpResponse.create()
|
||||
.withEntity(entity);
|
||||
});
|
||||
|
||||
CompletionStage<ServerBinding> serverBindingFuture =
|
||||
serverSource.to(Sink.foreach(conn -> {
|
||||
System.out.println("Accepted new connection from " + conn.remoteAddress());
|
||||
conn.handleWith(httpEcho, materializer);
|
||||
}
|
||||
)).run(materializer);
|
||||
//#connection-stream-failure-handling
|
||||
serverBindingFuture.toCompletableFuture().get(3, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
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, CompletionStage<ServerBinding>> serverSource =
|
||||
Http.get(system).bind(ConnectHttp.toHost("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(ContentTypes.TEXT_HTML_UTF8,
|
||||
"<html><body>Hello world!</body></html>");
|
||||
else if (uri.path().equals("/hello")) {
|
||||
String name = uri.query().get("name").orElse("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
|
||||
|
||||
CompletionStage<ServerBinding> serverBindingFuture =
|
||||
serverSource.to(Sink.foreach(connection -> {
|
||||
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
|
||||
|
||||
serverBindingFuture.toCompletableFuture().get(1, TimeUnit.SECONDS); // will throw if binding fails
|
||||
System.out.println("Press ENTER to stop.");
|
||||
new BufferedReader(new InputStreamReader(System.in)).readLine();
|
||||
} finally {
|
||||
system.terminate();
|
||||
CompletionStage<ServerBinding> serverBindingFuture =
|
||||
serverSource.to(Sink.foreach(connection -> {
|
||||
System.out.println("Accepted new connection from " + connection.remoteAddress());
|
||||
// ... and then actually handle the connection
|
||||
}
|
||||
)).run(materializer);
|
||||
//#binding-example
|
||||
serverBindingFuture.toCompletableFuture().get(3, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public static void bindingFailureExample() throws Exception {
|
||||
//#binding-failure-handling
|
||||
ActorSystem system = ActorSystem.create();
|
||||
Materializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
Source<IncomingConnection, CompletionStage<ServerBinding>> serverSource =
|
||||
Http.get(system).bind(ConnectHttp.toHost("localhost", 80), materializer);
|
||||
|
||||
CompletionStage<ServerBinding> serverBindingFuture =
|
||||
serverSource.to(Sink.foreach(connection -> {
|
||||
System.out.println("Accepted new connection from " + connection.remoteAddress());
|
||||
// ... and then actually handle the connection
|
||||
}
|
||||
)).run(materializer);
|
||||
|
||||
serverBindingFuture.whenCompleteAsync((binding, failure) -> {
|
||||
// possibly report the failure somewhere...
|
||||
}, system.dispatcher());
|
||||
//#binding-failure-handling
|
||||
serverBindingFuture.toCompletableFuture().get(3, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public static void connectionSourceFailureExample() throws Exception {
|
||||
//#incoming-connections-source-failure-handling
|
||||
ActorSystem system = ActorSystem.create();
|
||||
Materializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
Source<IncomingConnection, CompletionStage<ServerBinding>> serverSource =
|
||||
Http.get(system).bind(ConnectHttp.toHost("localhost", 8080), materializer);
|
||||
|
||||
Flow<IncomingConnection, IncomingConnection, NotUsed> failureDetection =
|
||||
Flow.of(IncomingConnection.class).watchTermination((notUsed, termination) -> {
|
||||
termination.whenComplete((done, cause) -> {
|
||||
if (cause != null) {
|
||||
// signal the failure to external monitoring service!
|
||||
}
|
||||
});
|
||||
return NotUsed.getInstance();
|
||||
});
|
||||
|
||||
CompletionStage<ServerBinding> serverBindingFuture =
|
||||
serverSource
|
||||
.via(failureDetection) // feed signals through our custom stage
|
||||
.to(Sink.foreach(connection -> {
|
||||
System.out.println("Accepted new connection from " + connection.remoteAddress());
|
||||
// ... and then actually handle the connection
|
||||
}))
|
||||
.run(materializer);
|
||||
//#incoming-connections-source-failure-handling
|
||||
serverBindingFuture.toCompletableFuture().get(3, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public static void connectionStreamFailureExample() throws Exception {
|
||||
//#connection-stream-failure-handling
|
||||
ActorSystem system = ActorSystem.create();
|
||||
Materializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
Source<IncomingConnection, CompletionStage<ServerBinding>> serverSource =
|
||||
Http.get(system).bind(ConnectHttp.toHost("localhost", 8080), materializer);
|
||||
|
||||
Flow<HttpRequest, HttpRequest, NotUsed> failureDetection =
|
||||
Flow.of(HttpRequest.class)
|
||||
.watchTermination((notUsed, termination) -> {
|
||||
termination.whenComplete((done, cause) -> {
|
||||
if (cause != null) {
|
||||
// signal the failure to external monitoring service!
|
||||
}
|
||||
});
|
||||
return NotUsed.getInstance();
|
||||
});
|
||||
|
||||
Flow<HttpRequest, HttpResponse, NotUsed> httpEcho =
|
||||
Flow.of(HttpRequest.class)
|
||||
.via(failureDetection)
|
||||
.map(request -> {
|
||||
Source<ByteString, Object> bytes = request.entity().getDataBytes();
|
||||
HttpEntity.Chunked entity = HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8, bytes);
|
||||
|
||||
return HttpResponse.create()
|
||||
.withEntity(entity);
|
||||
});
|
||||
|
||||
CompletionStage<ServerBinding> serverBindingFuture =
|
||||
serverSource.to(Sink.foreach(conn -> {
|
||||
System.out.println("Accepted new connection from " + conn.remoteAddress());
|
||||
conn.handleWith(httpEcho, materializer);
|
||||
}
|
||||
)).run(materializer);
|
||||
//#connection-stream-failure-handling
|
||||
serverBindingFuture.toCompletableFuture().get(3, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
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, CompletionStage<ServerBinding>> serverSource =
|
||||
Http.get(system).bind(ConnectHttp.toHost("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(ContentTypes.TEXT_HTML_UTF8,
|
||||
"<html><body>Hello world!</body></html>");
|
||||
} else if (uri.path().equals("/hello")) {
|
||||
String name = uri.query().get("name").orElse("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
|
||||
|
||||
CompletionStage<ServerBinding> serverBindingFuture =
|
||||
serverSource.to(Sink.foreach(connection -> {
|
||||
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
|
||||
|
||||
serverBindingFuture.toCompletableFuture().get(1, TimeUnit.SECONDS); // will throw if binding fails
|
||||
System.out.println("Press ENTER to stop.");
|
||||
new BufferedReader(new InputStreamReader(System.in)).readLine();
|
||||
} finally {
|
||||
system.terminate();
|
||||
}
|
||||
public static void main(String[] args) throws Exception {
|
||||
fullServerExample();
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
fullServerExample();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,62 +5,36 @@ package docs.http.javadsl.server;
|
|||
|
||||
import akka.http.javadsl.model.HttpRequest;
|
||||
import akka.http.javadsl.model.headers.Host;
|
||||
import akka.http.javadsl.model.headers.OAuth2BearerToken;
|
||||
import akka.http.javadsl.server.Handler1;
|
||||
import akka.http.javadsl.server.RequestContext;
|
||||
import akka.http.javadsl.server.Route;
|
||||
import akka.http.javadsl.server.RouteResult;
|
||||
import akka.http.javadsl.server.values.BasicCredentials;
|
||||
import akka.http.javadsl.server.values.HttpBasicAuthenticator;
|
||||
import akka.http.javadsl.server.values.OAuth2Authenticator;
|
||||
import akka.http.javadsl.server.values.OAuth2Credentials;
|
||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||
import akka.http.scaladsl.model.headers.Authorization;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
|
||||
import org.junit.Test;
|
||||
import scala.Option;
|
||||
import scala.concurrent.Future;
|
||||
|
||||
public class OAuth2AuthenticatorExample extends JUnitRouteTest {
|
||||
|
||||
private final String hardcodedToken = "token";
|
||||
|
||||
private Optional<String> authenticate(Optional<ProvidedCredentials> creds) {
|
||||
// this is where your actual authentication logic would go, looking up the user
|
||||
// based on the token or something in that direction
|
||||
|
||||
// We will not allow anonymous access.
|
||||
return creds
|
||||
.filter(c -> c.verify(hardcodedToken)) //
|
||||
.map(c -> c.identifier()); // Provide the "identifier" down to the inner route
|
||||
// (for OAuth2, that's actually just the token)
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOAuth2Authenticator() {
|
||||
//#oauth2-authenticator-java
|
||||
final OAuth2Authenticator<String> authentication = new OAuth2Authenticator<String>("My realm") {
|
||||
|
||||
private final String hardcodedToken = "token";
|
||||
|
||||
@Override
|
||||
public CompletionStage<Optional<String>> authenticate(OAuth2Credentials credentials) {
|
||||
// this is where your actual authentication logic would go, looking up the user
|
||||
// based on the token or something in that direction
|
||||
if (credentials.available() && // no anonymous access
|
||||
credentials.verify(hardcodedToken)) {
|
||||
// not a secret + identity pair, so this is actually the token
|
||||
return authenticateAs(credentials.identifier());
|
||||
} else {
|
||||
return refuseAccess();
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
final Route route =
|
||||
authentication.route(
|
||||
handleWith1(
|
||||
authentication,
|
||||
new Handler1<String>() {
|
||||
public RouteResult apply(RequestContext ctx, String token) {
|
||||
return ctx.complete("The secret token is: " + token);
|
||||
}
|
||||
|
||||
}
|
||||
)
|
||||
);
|
||||
authenticateOAuth2("My realm", this::authenticate, token ->
|
||||
complete("The secret token is: " + token)
|
||||
);
|
||||
|
||||
|
||||
// tests:
|
||||
|
|
|
|||
|
|
@ -5,73 +5,75 @@
|
|||
package docs.http.javadsl.server;
|
||||
|
||||
import akka.http.javadsl.model.StatusCodes;
|
||||
import akka.http.javadsl.server.Handler1;
|
||||
import akka.http.javadsl.server.values.PathMatcher;
|
||||
import akka.http.javadsl.server.values.PathMatchers;
|
||||
import akka.http.javadsl.server.PathMatchers;
|
||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class PathDirectiveExampleTest extends JUnitRouteTest {
|
||||
@Test
|
||||
public void testPathPrefix() {
|
||||
//#path-examples
|
||||
// matches "/test"
|
||||
path("test").route(
|
||||
completeWithStatus(StatusCodes.OK)
|
||||
);
|
||||
@Test
|
||||
public void testPathPrefix() {
|
||||
//#path-examples
|
||||
// matches "/test"
|
||||
path("test", () ->
|
||||
complete(StatusCodes.OK)
|
||||
);
|
||||
|
||||
// matches "/test", as well
|
||||
path(PathMatchers.segment("test")).route(
|
||||
completeWithStatus(StatusCodes.OK)
|
||||
);
|
||||
// matches "/test", as well
|
||||
path(PathMatchers.segment("test"), () ->
|
||||
complete(StatusCodes.OK)
|
||||
);
|
||||
|
||||
// matches "/admin/user"
|
||||
path("admin", "user").route(
|
||||
completeWithStatus(StatusCodes.OK)
|
||||
);
|
||||
// matches "/admin/user"
|
||||
path(PathMatchers.segment("admin")
|
||||
.slash("user"), () ->
|
||||
complete(StatusCodes.OK)
|
||||
);
|
||||
|
||||
// matches "/admin/user", as well
|
||||
pathPrefix("admin").route(
|
||||
path("user").route(
|
||||
completeWithStatus(StatusCodes.OK)
|
||||
)
|
||||
);
|
||||
// matches "/admin/user", as well
|
||||
pathPrefix("admin", () ->
|
||||
path("user", () ->
|
||||
complete(StatusCodes.OK)
|
||||
)
|
||||
);
|
||||
|
||||
// matches "/admin/user/<user-id>"
|
||||
Handler1<Integer> completeWithUserId =
|
||||
(ctx, userId) -> ctx.complete("Hello user " + userId);
|
||||
PathMatcher<Integer> userId = PathMatchers.intValue();
|
||||
pathPrefix("admin", "user").route(
|
||||
path(userId).route(
|
||||
handleWith1(userId, completeWithUserId)
|
||||
)
|
||||
);
|
||||
// matches "/admin/user/<user-id>"
|
||||
path(PathMatchers.segment("admin")
|
||||
.slash("user")
|
||||
.slash(PathMatchers.integerSegment()), userId -> {
|
||||
return complete("Hello user " + userId);
|
||||
}
|
||||
);
|
||||
|
||||
// matches "/admin/user/<user-id>", as well
|
||||
path("admin", "user", userId).route(
|
||||
handleWith1(userId, completeWithUserId)
|
||||
);
|
||||
// matches "/admin/user/<user-id>", as well
|
||||
pathPrefix("admin", () ->
|
||||
path("user", () ->
|
||||
path(PathMatchers.integerSegment(), userId ->
|
||||
complete("Hello user " + userId)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
// never matches
|
||||
path("admin").route( // oops this only matches "/admin"
|
||||
path("user").route(
|
||||
completeWithStatus(StatusCodes.OK)
|
||||
)
|
||||
);
|
||||
// never matches
|
||||
path("admin", () -> // oops this only matches "/admin", and no sub-paths
|
||||
path("user", () ->
|
||||
complete(StatusCodes.OK)
|
||||
)
|
||||
);
|
||||
|
||||
// matches "/user/" with the first subroute, "/user" (without a trailing slash)
|
||||
// with the second subroute, and "/user/<user-id>" with the last one.
|
||||
pathPrefix("user").route(
|
||||
pathSingleSlash().route(
|
||||
completeWithStatus(StatusCodes.OK)
|
||||
),
|
||||
pathEnd().route(
|
||||
completeWithStatus(StatusCodes.OK)
|
||||
),
|
||||
path(userId).route(
|
||||
handleWith1(userId, completeWithUserId)
|
||||
)
|
||||
);
|
||||
//#path-examples
|
||||
}
|
||||
}
|
||||
// matches "/user/" with the first subroute, "/user" (without a trailing slash)
|
||||
// with the second subroute, and "/user/<user-id>" with the last one.
|
||||
pathPrefix("user", () -> route(
|
||||
pathSingleSlash(() ->
|
||||
complete(StatusCodes.OK)
|
||||
),
|
||||
pathEnd(() ->
|
||||
complete(StatusCodes.OK)
|
||||
),
|
||||
path(PathMatchers.integerSegment(), userId ->
|
||||
complete("Hello user " + userId)
|
||||
)
|
||||
));
|
||||
//#path-examples
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
package docs.http.javadsl.server;
|
||||
|
||||
//#websocket-example-using-core
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
|
|
@ -12,11 +13,6 @@ import java.util.concurrent.TimeUnit;
|
|||
|
||||
import akka.NotUsed;
|
||||
import akka.http.javadsl.ConnectHttp;
|
||||
import scala.concurrent.Await;
|
||||
import scala.concurrent.Future;
|
||||
import scala.concurrent.duration.FiniteDuration;
|
||||
import scala.runtime.BoxedUnit;
|
||||
|
||||
import akka.japi.Function;
|
||||
import akka.japi.JavaPartialFunction;
|
||||
|
||||
|
|
@ -34,66 +30,76 @@ import akka.http.javadsl.model.ws.Message;
|
|||
import akka.http.javadsl.model.ws.TextMessage;
|
||||
import akka.http.javadsl.model.ws.WebSocket;
|
||||
|
||||
@SuppressWarnings("Convert2MethodRef")
|
||||
public class WebSocketCoreExample {
|
||||
//#websocket-handling
|
||||
public static HttpResponse handleRequest(HttpRequest request) {
|
||||
System.out.println("Handling request to " + request.getUri());
|
||||
|
||||
//#websocket-handling
|
||||
public static HttpResponse handleRequest(HttpRequest request) {
|
||||
System.out.println("Handling request to " + request.getUri());
|
||||
|
||||
if (request.getUri().path().equals("/greeter"))
|
||||
return WebSocket.handleWebSocketRequestWith(request, greeter());
|
||||
else
|
||||
return HttpResponse.create().withStatus(404);
|
||||
if (request.getUri().path().equals("/greeter")) {
|
||||
final Flow<Message, Message, NotUsed> greeterFlow = greeter();
|
||||
return WebSocket.handleWebSocketRequestWith(request, greeterFlow);
|
||||
} else {
|
||||
return HttpResponse.create().withStatus(404);
|
||||
}
|
||||
//#websocket-handling
|
||||
}
|
||||
//#websocket-handling
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
ActorSystem system = ActorSystem.create();
|
||||
public static void main(String[] args) throws Exception {
|
||||
ActorSystem system = ActorSystem.create();
|
||||
|
||||
try {
|
||||
final Materializer materializer = ActorMaterializer.create(system);
|
||||
try {
|
||||
final Materializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
CompletionStage<ServerBinding> serverBindingFuture =
|
||||
Http.get(system).bindAndHandleSync(
|
||||
new Function<HttpRequest, HttpResponse>() {
|
||||
public HttpResponse apply(HttpRequest request) throws Exception {
|
||||
return handleRequest(request);
|
||||
}
|
||||
}, ConnectHttp.toHost("localhost", 8080), materializer);
|
||||
final Function<HttpRequest, HttpResponse> handler = request -> handleRequest(request);
|
||||
CompletionStage<ServerBinding> serverBindingFuture =
|
||||
Http.get(system).bindAndHandleSync(
|
||||
handler, ConnectHttp.toHost("localhost", 8080), materializer);
|
||||
|
||||
// will throw if binding fails
|
||||
serverBindingFuture.toCompletableFuture().get(1, TimeUnit.SECONDS);
|
||||
System.out.println("Press ENTER to stop.");
|
||||
new BufferedReader(new InputStreamReader(System.in)).readLine();
|
||||
} finally {
|
||||
system.terminate();
|
||||
}
|
||||
// will throw if binding fails
|
||||
serverBindingFuture.toCompletableFuture().get(1, TimeUnit.SECONDS);
|
||||
System.out.println("Press ENTER to stop.");
|
||||
new BufferedReader(new InputStreamReader(System.in)).readLine();
|
||||
} finally {
|
||||
system.terminate();
|
||||
}
|
||||
}
|
||||
|
||||
//#websocket-handler
|
||||
/**
|
||||
* A handler that treats incoming messages as a name,
|
||||
* and responds with a greeting to that name
|
||||
*/
|
||||
public static Flow<Message, Message, NotUsed> greeter() {
|
||||
return
|
||||
Flow.<Message>create()
|
||||
.collect(new JavaPartialFunction<Message, Message>() {
|
||||
@Override
|
||||
public Message apply(Message msg, boolean isCheck) throws Exception {
|
||||
if (isCheck)
|
||||
if (msg.isText()) return null;
|
||||
else throw noMatch();
|
||||
else
|
||||
return handleTextMessage(msg.asTextMessage());
|
||||
}
|
||||
});
|
||||
//#websocket-handler
|
||||
|
||||
/**
|
||||
* A handler that treats incoming messages as a name,
|
||||
* and responds with a greeting to that name
|
||||
*/
|
||||
public static Flow<Message, Message, NotUsed> greeter() {
|
||||
return
|
||||
Flow.<Message>create()
|
||||
.collect(new JavaPartialFunction<Message, Message>() {
|
||||
@Override
|
||||
public Message apply(Message msg, boolean isCheck) throws Exception {
|
||||
if (isCheck) {
|
||||
if (msg.isText()) {
|
||||
return null;
|
||||
} else {
|
||||
throw noMatch();
|
||||
}
|
||||
} else {
|
||||
return handleTextMessage(msg.asTextMessage());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static TextMessage handleTextMessage(TextMessage msg) {
|
||||
if (msg.isStrict()) // optimization that directly creates a simple response...
|
||||
{
|
||||
return TextMessage.create("Hello " + msg.getStrictText());
|
||||
} else // ... this would suffice to handle all text messages in a streaming fashion
|
||||
{
|
||||
return TextMessage.create(Source.single("Hello ").concat(msg.getStreamedText()));
|
||||
}
|
||||
public static TextMessage handleTextMessage(TextMessage msg) {
|
||||
if (msg.isStrict()) // optimization that directly creates a simple response...
|
||||
return TextMessage.create("Hello "+msg.getStrictText());
|
||||
else // ... this would suffice to handle all text messages in a streaming fashion
|
||||
return TextMessage.create(Source.single("Hello ").concat(msg.getStreamedText()));
|
||||
}
|
||||
//#websocket-handler
|
||||
}
|
||||
//#websocket-handler
|
||||
}
|
||||
//#websocket-example-using-core
|
||||
//#websocket-example-using-core
|
||||
|
|
|
|||
|
|
@ -4,51 +4,51 @@
|
|||
|
||||
package docs.http.javadsl.server;
|
||||
|
||||
import akka.NotUsed;
|
||||
import akka.http.javadsl.server.AllDirectives;
|
||||
import akka.http.javadsl.server.Route;
|
||||
|
||||
import akka.japi.JavaPartialFunction;
|
||||
|
||||
import akka.stream.javadsl.Flow;
|
||||
import akka.stream.javadsl.Source;
|
||||
|
||||
import akka.http.javadsl.model.ws.Message;
|
||||
import akka.http.javadsl.model.ws.TextMessage;
|
||||
|
||||
import akka.http.javadsl.server.HttpApp;
|
||||
public class WebSocketRoutingExample extends AllDirectives {
|
||||
|
||||
public class WebSocketRoutingExample extends HttpApp {
|
||||
//#websocket-route
|
||||
@Override
|
||||
public Route createRoute() {
|
||||
return
|
||||
path("greeter").route(
|
||||
handleWebSocketMessages(greeter())
|
||||
);
|
||||
}
|
||||
//#websocket-route
|
||||
//#websocket-route
|
||||
public Route createRoute() {
|
||||
return
|
||||
path("greeter", () ->
|
||||
handleWebSocketMessages(greeter())
|
||||
);
|
||||
}
|
||||
//#websocket-route
|
||||
|
||||
/**
|
||||
* A handler that treats incoming messages as a name,
|
||||
* and responds with a greeting to that name
|
||||
*/
|
||||
public static Flow<Message, Message, ?> greeter() {
|
||||
return
|
||||
Flow.<Message>create()
|
||||
.collect(new JavaPartialFunction<Message, Message>() {
|
||||
@Override
|
||||
public Message apply(Message msg, boolean isCheck) throws Exception {
|
||||
if (isCheck)
|
||||
if (msg.isText()) return null;
|
||||
else throw noMatch();
|
||||
else
|
||||
return handleTextMessage(msg.asTextMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
public static TextMessage handleTextMessage(TextMessage msg) {
|
||||
if (msg.isStrict()) // optimization that directly creates a simple response...
|
||||
return TextMessage.create("Hello "+msg.getStrictText());
|
||||
else // ... this would suffice to handle all text messages in a streaming fashion
|
||||
return TextMessage.create(Source.single("Hello ").concat(msg.getStreamedText()));
|
||||
/**
|
||||
* A handler that treats incoming messages as a name,
|
||||
* and responds with a greeting to that name
|
||||
*/
|
||||
public static Flow<Message, Message, NotUsed> greeter() {
|
||||
return
|
||||
Flow.<Message>create()
|
||||
.collect(new JavaPartialFunction<Message, Message>() {
|
||||
@Override
|
||||
public Message apply(Message msg, boolean isCheck) throws Exception {
|
||||
if (isCheck) {
|
||||
if (msg.isText()) return null;
|
||||
else throw noMatch();
|
||||
} else return handleTextMessage(msg.asTextMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static TextMessage handleTextMessage(TextMessage msg) {
|
||||
if (msg.isStrict()) // optimization that directly creates a simple response...
|
||||
{
|
||||
return TextMessage.create("Hello " + msg.getStrictText());
|
||||
} else // ... this would suffice to handle all text messages in a streaming fashion
|
||||
{
|
||||
return TextMessage.create(Source.single("Hello ").concat(msg.getStreamedText()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,126 @@
|
|||
/*
|
||||
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
|
||||
*/
|
||||
package docs.http.javadsl.server.directives;
|
||||
|
||||
import akka.http.javadsl.model.HttpRequest;
|
||||
import akka.http.javadsl.model.StatusCodes;
|
||||
import akka.http.javadsl.model.headers.RawHeader;
|
||||
import akka.http.javadsl.server.Route;
|
||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class CustomDirectivesExamplesTest extends JUnitRouteTest {
|
||||
|
||||
//#labeling-1
|
||||
public Route getOrPut(Supplier<Route> inner) {
|
||||
return get(inner).orElse(put(inner));
|
||||
}
|
||||
//#
|
||||
|
||||
@Test
|
||||
public void testLabeling() {
|
||||
// tests:
|
||||
|
||||
//#labeling-2
|
||||
Route route = getOrPut(() -> complete("ok"));
|
||||
//#
|
||||
|
||||
testRoute(route).run(HttpRequest.GET("/"))
|
||||
.assertStatusCode(StatusCodes.OK);
|
||||
|
||||
testRoute(route).run(HttpRequest.PUT("/"))
|
||||
.assertStatusCode(StatusCodes.OK);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public static class MyCredentials {
|
||||
private final String userId;
|
||||
private final String secret;
|
||||
|
||||
public MyCredentials(String userId, String secret) {
|
||||
this.userId = userId;
|
||||
this.secret = secret;
|
||||
}
|
||||
|
||||
public String getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public boolean safeSecretVerification(String correct) {
|
||||
// of course this is not what you would do in a real app
|
||||
return correct.equals(secret);
|
||||
}
|
||||
|
||||
}
|
||||
public static enum MyRole {
|
||||
USER,
|
||||
ADMIN
|
||||
}
|
||||
|
||||
//#composition-1
|
||||
// the composed custom directive
|
||||
/**
|
||||
* @param authenticate A function returns a set of roles for the credentials of a user
|
||||
* @param inner Inner route to execute if the provided credentials has the given role
|
||||
* if not, the request is completed with a
|
||||
*/
|
||||
public Route headerBasedAuth(Function<MyCredentials, Set<MyRole>> authenticate, MyRole requiredRole, Supplier<Route> inner) {
|
||||
return headerValueByName("X-My-User-Id", (userId) -> {
|
||||
return headerValueByName("X-My-User-Secret", (secret) -> {
|
||||
Set<MyRole> userRoles = authenticate.apply(new MyCredentials(userId, secret));
|
||||
if (userRoles.contains(requiredRole)) {
|
||||
return inner.get();
|
||||
} else {
|
||||
return complete(StatusCodes.FORBIDDEN, "Role " + requiredRole + " required for access");
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
//#
|
||||
|
||||
@Test
|
||||
public void testComposition() {
|
||||
// tests:
|
||||
|
||||
//#composition-2
|
||||
// a function for authentication
|
||||
Function<MyCredentials, Set<MyRole>> authLogic =
|
||||
(credentials) -> {
|
||||
if (credentials.userId.equals("admin") && credentials.safeSecretVerification("secret"))
|
||||
return new HashSet<>(Arrays.asList(MyRole.USER, MyRole.ADMIN));
|
||||
else
|
||||
return Collections.emptySet();
|
||||
};
|
||||
|
||||
// and then using the custom route
|
||||
Route route = get(() ->
|
||||
path("admin", () ->
|
||||
headerBasedAuth(authLogic, MyRole.ADMIN, () -> complete(StatusCodes.OK, "admin stuff"))
|
||||
)
|
||||
);
|
||||
//#
|
||||
|
||||
|
||||
testRoute(route).run(HttpRequest.GET("/admin"))
|
||||
.assertStatusCode(StatusCodes.BAD_REQUEST);
|
||||
|
||||
testRoute(route).run(HttpRequest.GET("/admin").addHeaders(
|
||||
Arrays.asList(RawHeader.create("X-My-User-Id", "user"), RawHeader.create("X-My-User-Secret", "wrong"))))
|
||||
.assertStatusCode(StatusCodes.FORBIDDEN);
|
||||
|
||||
testRoute(route).run(HttpRequest.GET("/admin").addHeaders(
|
||||
Arrays.asList(RawHeader.create("X-My-User-Id", "admin"), RawHeader.create("X-My-User-Secret", "secret"))))
|
||||
.assertStatusCode(StatusCodes.OK);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -7,15 +7,14 @@ package docs.http.javadsl.server.directives;
|
|||
import java.util.Arrays;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import akka.http.javadsl.model.HttpMethod;
|
||||
import org.junit.Test;
|
||||
|
||||
import akka.http.javadsl.model.HttpRequest;
|
||||
import akka.http.javadsl.model.StatusCodes;
|
||||
import akka.http.javadsl.model.headers.Host;
|
||||
import akka.http.javadsl.server.*;
|
||||
import akka.http.javadsl.server.Route;
|
||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class HostDirectivesExamplesTest extends JUnitRouteTest {
|
||||
|
||||
@Test
|
||||
|
|
@ -23,7 +22,7 @@ public class HostDirectivesExamplesTest extends JUnitRouteTest {
|
|||
//#host1
|
||||
final Route matchListOfHosts = host(
|
||||
Arrays.asList("api.company.com", "rest.company.com"),
|
||||
completeWithStatus(StatusCodes.OK));
|
||||
() -> complete(StatusCodes.OK));
|
||||
|
||||
testRoute(matchListOfHosts).run(HttpRequest.GET("/").addHeader(Host.create("api.company.com")))
|
||||
.assertStatusCode(StatusCodes.OK);
|
||||
|
|
@ -34,7 +33,7 @@ public class HostDirectivesExamplesTest extends JUnitRouteTest {
|
|||
public void testHostPredicate() {
|
||||
//#host2
|
||||
final Route shortOnly = host(hostname -> hostname.length() < 10,
|
||||
completeWithStatus(StatusCodes.OK));
|
||||
() -> complete(StatusCodes.OK));
|
||||
|
||||
testRoute(shortOnly).run(HttpRequest.GET("/").addHeader(Host.create("short.com")))
|
||||
.assertStatusCode(StatusCodes.OK);
|
||||
|
|
@ -47,10 +46,9 @@ public class HostDirectivesExamplesTest extends JUnitRouteTest {
|
|||
@Test
|
||||
public void testExtractHost() {
|
||||
//#extractHostname
|
||||
final RequestVal<String> host = RequestVals.host();
|
||||
|
||||
final Route route = handleWith1(host,
|
||||
(ctx, hn) -> ctx.complete("Hostname: " + hn));
|
||||
final Route route = extractHost(hn ->
|
||||
complete("Hostname: " + hn));
|
||||
|
||||
testRoute(route).run(HttpRequest.GET("/").addHeader(Host.create("company.com", 9090)))
|
||||
.assertEntity("Hostname: company.com");
|
||||
|
|
@ -60,18 +58,12 @@ public class HostDirectivesExamplesTest extends JUnitRouteTest {
|
|||
@Test
|
||||
public void testMatchAndExtractHost() {
|
||||
//#matchAndExtractHost
|
||||
final RequestVal<String> hostPrefix = RequestVals
|
||||
.matchAndExtractHost(Pattern.compile("api|rest"));
|
||||
|
||||
final Route hostPrefixRoute = handleWith1(hostPrefix,
|
||||
(ctx, prefix) -> ctx.complete("Extracted prefix: " + prefix));
|
||||
final Route hostPrefixRoute = host(Pattern.compile("api|rest"), prefix ->
|
||||
complete("Extracted prefix: " + prefix));
|
||||
|
||||
final RequestVal<String> hostPart = RequestVals.matchAndExtractHost(Pattern
|
||||
.compile("public.(my|your)company.com"));
|
||||
|
||||
final Route hostPartRoute = handleWith1(
|
||||
hostPart,
|
||||
(ctx, captured) -> ctx.complete("You came through " + captured
|
||||
final Route hostPartRoute = host(Pattern.compile("public.(my|your)company.com"), captured ->
|
||||
complete("You came through " + captured
|
||||
+ " company"));
|
||||
|
||||
final Route route = route(hostPrefixRoute, hostPartRoute);
|
||||
|
|
@ -90,9 +82,10 @@ public class HostDirectivesExamplesTest extends JUnitRouteTest {
|
|||
public void testFailingMatchAndExtractHost() {
|
||||
//#failing-matchAndExtractHost
|
||||
// this will throw IllegalArgumentException
|
||||
final RequestVal<String> hostRegex = RequestVals
|
||||
.matchAndExtractHost(Pattern
|
||||
.compile("server-([0-9]).company.(com|net|org)"));
|
||||
final Route hostRegex = host(Pattern.compile("server-([0-9]).company.(com|net|org)"), s ->
|
||||
// will not reach here
|
||||
complete(s)
|
||||
);
|
||||
//#failing-matchAndExtractHost
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,20 +4,19 @@
|
|||
|
||||
package docs.http.javadsl.server.directives;
|
||||
|
||||
import akka.http.javadsl.model.HttpMethod;
|
||||
import org.junit.Test;
|
||||
|
||||
import akka.http.javadsl.model.HttpMethods;
|
||||
import akka.http.javadsl.model.HttpRequest;
|
||||
import akka.http.javadsl.model.StatusCodes;
|
||||
import akka.http.javadsl.server.*;
|
||||
import akka.http.javadsl.server.Route;
|
||||
import akka.http.javadsl.testkit.JUnitRouteTest;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class MethodDirectivesExamplesTest extends JUnitRouteTest {
|
||||
@Test
|
||||
public void testDelete() {
|
||||
//#delete
|
||||
final Route route = delete(complete("This is a DELETE request."));
|
||||
final Route route = delete(() -> complete("This is a DELETE request."));
|
||||
|
||||
testRoute(route).run(HttpRequest.DELETE("/")).assertEntity(
|
||||
"This is a DELETE request.");
|
||||
|
|
@ -27,7 +26,7 @@ public class MethodDirectivesExamplesTest extends JUnitRouteTest {
|
|||
@Test
|
||||
public void testGet() {
|
||||
//#get
|
||||
final Route route = get(complete("This is a GET request."));
|
||||
final Route route = get(() -> complete("This is a GET request."));
|
||||
|
||||
testRoute(route).run(HttpRequest.GET("/")).assertEntity(
|
||||
"This is a GET request.");
|
||||
|
|
@ -37,7 +36,7 @@ public class MethodDirectivesExamplesTest extends JUnitRouteTest {
|
|||
@Test
|
||||
public void testHead() {
|
||||
//#head
|
||||
final Route route = head(complete("This is a HEAD request."));
|
||||
final Route route = head(() -> complete("This is a HEAD request."));
|
||||
|
||||
testRoute(route).run(HttpRequest.HEAD("/")).assertEntity(
|
||||
"This is a HEAD request.");
|
||||
|
|
@ -47,7 +46,7 @@ public class MethodDirectivesExamplesTest extends JUnitRouteTest {
|
|||
@Test
|
||||
public void testOptions() {
|
||||
//#options
|
||||
final Route route = options(complete("This is a OPTIONS request."));
|
||||
final Route route = options(() -> complete("This is a OPTIONS request."));
|
||||
|
||||
testRoute(route).run(HttpRequest.OPTIONS("/")).assertEntity(
|
||||
"This is a OPTIONS request.");
|
||||
|
|
@ -57,7 +56,7 @@ public class MethodDirectivesExamplesTest extends JUnitRouteTest {
|
|||
@Test
|
||||
public void testPatch() {
|
||||
//#patch
|
||||
final Route route = patch(complete("This is a PATCH request."));
|
||||
final Route route = patch(() -> complete("This is a PATCH request."));
|
||||
|
||||
testRoute(route).run(HttpRequest.PATCH("/").withEntity("patch content"))
|
||||
.assertEntity("This is a PATCH request.");
|
||||
|
|
@ -67,7 +66,7 @@ public class MethodDirectivesExamplesTest extends JUnitRouteTest {
|
|||
@Test
|
||||
public void testPost() {
|
||||
//#post
|
||||
final Route route = post(complete("This is a POST request."));
|
||||
final Route route = post(() -> complete("This is a POST request."));
|
||||
|
||||
testRoute(route).run(HttpRequest.POST("/").withEntity("post content"))
|
||||
.assertEntity("This is a POST request.");
|
||||
|
|
@ -77,7 +76,7 @@ public class MethodDirectivesExamplesTest extends JUnitRouteTest {
|
|||
@Test
|
||||
public void testPut() {
|
||||
//#put
|
||||
final Route route = put(complete("This is a PUT request."));
|
||||
final Route route = put(() -> complete("This is a PUT request."));
|
||||
|
||||
testRoute(route).run(HttpRequest.PUT("/").withEntity("put content"))
|
||||
.assertEntity("This is a PUT request.");
|
||||
|
|
@ -88,7 +87,7 @@ public class MethodDirectivesExamplesTest extends JUnitRouteTest {
|
|||
public void testMethodExample() {
|
||||
//#method-example
|
||||
final Route route = method(HttpMethods.PUT,
|
||||
complete("This is a PUT request."));
|
||||
() -> complete("This is a PUT request."));
|
||||
|
||||
testRoute(route).run(HttpRequest.PUT("/").withEntity("put content"))
|
||||
.assertEntity("This is a PUT request.");
|
||||
|
|
@ -101,15 +100,15 @@ public class MethodDirectivesExamplesTest extends JUnitRouteTest {
|
|||
@Test
|
||||
public void testExtractMethodExample() {
|
||||
//#extractMethod
|
||||
final RequestVal<HttpMethod> requestMethod = RequestVals.requestMethod();
|
||||
|
||||
final Route otherMethod = handleWith1(
|
||||
requestMethod,
|
||||
(ctx, method) -> ctx.complete("This " + method.value()
|
||||
+ " request, clearly is not a GET!"));
|
||||
|
||||
final Route route = route(get(complete("This is a GET request.")),
|
||||
otherMethod);
|
||||
final Route route = route(
|
||||
get(() ->
|
||||
complete("This is a GET request.")
|
||||
),
|
||||
extractMethod(method ->
|
||||
complete("This " + method.value() + " request, clearly is not a GET!")
|
||||
)
|
||||
);
|
||||
|
||||
testRoute(route).run(HttpRequest.GET("/")).assertEntity(
|
||||
"This is a GET request.");
|
||||
|
|
@ -121,4 +120,31 @@ public class MethodDirectivesExamplesTest extends JUnitRouteTest {
|
|||
"This HEAD request, clearly is not a GET!");
|
||||
//#extractMethod
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOverrideMethodWithParameter() {
|
||||
//#overrideMethodWithParameter
|
||||
|
||||
final Route route = route(
|
||||
overrideMethodWithParameter("method", () ->
|
||||
route(
|
||||
get(() -> complete("This looks like a GET request.")),
|
||||
post(() -> complete("This looks like a POST request."))
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
// tests:
|
||||
testRoute(route).run(HttpRequest.GET("/?method=POST")).assertEntity(
|
||||
"This looks like a POST request.");
|
||||
|
||||
testRoute(route).run(HttpRequest.POST("/?method=get"))
|
||||
.assertEntity("This looks like a GET request.");
|
||||
|
||||
testRoute(route).run(HttpRequest.GET("/?method=hallo")).assertEntity(
|
||||
"The server either does not recognize the request method, or it lacks the ability to fulfill the request.");
|
||||
|
||||
//#overrideMethodWithParameter
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,29 +5,51 @@
|
|||
package docs.http.javadsl.server.testkit;
|
||||
|
||||
//#simple-app
|
||||
import akka.http.javadsl.server.*;
|
||||
import akka.http.javadsl.server.values.Parameters;
|
||||
|
||||
public class MyAppService extends HttpApp {
|
||||
RequestVal<Double> x = Parameters.doubleValue("x");
|
||||
RequestVal<Double> y = Parameters.doubleValue("y");
|
||||
import akka.actor.ActorSystem;
|
||||
import akka.http.javadsl.ConnectHttp;
|
||||
import akka.http.javadsl.Http;
|
||||
import akka.http.javadsl.server.AllDirectives;
|
||||
import akka.http.javadsl.server.Route;
|
||||
import akka.http.javadsl.server.StringUnmarshallers;
|
||||
import akka.http.javadsl.server.examples.simple.SimpleServerApp;
|
||||
import akka.stream.ActorMaterializer;
|
||||
|
||||
public RouteResult add(RequestContext ctx, double x, double y) {
|
||||
return ctx.complete("x + y = " + (x + y));
|
||||
}
|
||||
import java.io.IOException;
|
||||
|
||||
@Override
|
||||
public Route createRoute() {
|
||||
return
|
||||
route(
|
||||
get(
|
||||
pathPrefix("calculator").route(
|
||||
path("add").route(
|
||||
handleReflectively(this, "add", x, y)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
public class MyAppService extends AllDirectives {
|
||||
|
||||
public String add(double x, double y) {
|
||||
return "x + y = " + (x + y);
|
||||
}
|
||||
|
||||
public Route createRoute() {
|
||||
return
|
||||
get(() ->
|
||||
pathPrefix("calculator", () ->
|
||||
path("add", () ->
|
||||
parameter(StringUnmarshallers.DOUBLE, "x", x ->
|
||||
parameter(StringUnmarshallers.DOUBLE, "y", y ->
|
||||
complete(add(x, y))
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
final ActorSystem system = ActorSystem.create();
|
||||
final ActorMaterializer materializer = ActorMaterializer.create(system);
|
||||
|
||||
final SimpleServerApp app = new SimpleServerApp();
|
||||
|
||||
final ConnectHttp host = ConnectHttp.toHost("127.0.0.1");
|
||||
|
||||
Http.get(system).bindAndHandle(app.createRoute().flow(system, materializer), host, materializer);
|
||||
|
||||
System.console().readLine("Type RETURN to exit...");
|
||||
system.terminate();
|
||||
}
|
||||
}
|
||||
//#simple-app
|
||||
//#simple-app
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue