Remove Akka-HTTP sources from akka/akka, moving to akka/akka-http! (#21690)

This commit is contained in:
Konrad Malawski 2016-10-18 15:17:17 +02:00 committed by GitHub
parent 09a6d2ede1
commit a6a5556a8f
1155 changed files with 20 additions and 96517 deletions

View file

@ -1,76 +0,0 @@
/**
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package akka.http
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl._
import akka.http.scaladsl.Http
import akka.http.scaladsl.Http.ServerBinding
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.unmarshalling._
import java.util.concurrent.TimeUnit
import org.openjdk.jmh.annotations._
import scala.concurrent.Await
import scala.concurrent.duration._
import scala.util.Try
import com.typesafe.config.ConfigFactory
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Array(Mode.Throughput))
class HttpBenchmark {
val config = ConfigFactory.parseString(
"""
akka {
loglevel = "ERROR"
}""".stripMargin
).withFallback(ConfigFactory.load())
implicit val system = ActorSystem("HttpBenchmark", config)
implicit val materializer = ActorMaterializer()
var binding: ServerBinding = _
var request: HttpRequest = _
var pool: Flow[(HttpRequest, Int), (Try[HttpResponse], Int), _] = _
@Setup
def setup(): Unit = {
val route = {
path("test") {
get {
complete("ok")
}
}
}
binding = Await.result(Http().bindAndHandle(route, "127.0.0.1", 0), 1.second)
request = HttpRequest(uri = s"http://${binding.localAddress.getHostString}:${binding.localAddress.getPort}/test")
pool = Http().cachedHostConnectionPool[Int](binding.localAddress.getHostString, binding.localAddress.getPort)
}
@TearDown
def shutdown(): Unit = {
Await.ready(Http().shutdownAllConnectionPools(), 1.second)
binding.unbind()
Await.result(system.terminate(), 5.seconds)
}
@Benchmark
def single_request(): Unit = {
import system.dispatcher
val response = Await.result(Http().singleRequest(request), 1.second)
Await.result(Unmarshal(response.entity).to[String], 1.second)
}
@Benchmark
def single_request_pool(): Unit = {
import system.dispatcher
val (response, id) = Await.result(Source.single(HttpRequest(uri = "/test") -> 42).via(pool).runWith(Sink.head), 1.second)
Await.result(Unmarshal(response.get.entity).to[String], 1.second)
}
}

View file

@ -1,128 +0,0 @@
/**
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package akka.http
import java.util.concurrent.{ CountDownLatch, TimeUnit }
import akka.NotUsed
import akka.actor.ActorSystem
import akka.http.impl.util.ByteStringRendering
import akka.http.scaladsl.{ Http, HttpExt }
import akka.http.scaladsl.Http.ServerBinding
import akka.http.scaladsl.model._
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.unmarshalling._
import akka.stream._
import akka.stream.TLSProtocol.{ SslTlsInbound, SslTlsOutbound }
import akka.stream.scaladsl._
import akka.stream.stage.{ GraphStage, GraphStageLogic }
import akka.util.ByteString
import com.typesafe.config.ConfigFactory
import org.openjdk.jmh.annotations._
import org.openjdk.jmh.infra.Blackhole
import scala.concurrent.{ Await, Future }
import scala.concurrent.duration._
import scala.util.Try
/*
Baseline:
[info] Benchmark Mode Cnt Score Error Units
[info] HttpBlueprintBenchmark.run_10000_reqs thrpt 20 197972.659 ± 14512.694 ops/s
*/
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Array(Mode.Throughput))
class HttpBlueprintBenchmark {
val config = ConfigFactory.parseString(
"""
akka {
loglevel = "WARNING"
stream.materializer {
# default: sync-processing-limit = 1000
sync-processing-limit = 1000
# default: output-burst-limit = 10000
output-burst-limit = 1000
# default: initial-input-buffer-size = 4
initial-input-buffer-size = 4
# default: max-input-buffer-size = 16
max-input-buffer-size = 16
}
http {
# default: request-timeout = 20s
request-timeout = infinite # disabled
# request-timeout = 20s
}
}""".stripMargin
).withFallback(ConfigFactory.load())
implicit val system: ActorSystem = ActorSystem("HttpBenchmark", config)
val materializer: ActorMaterializer = ActorMaterializer()
val notFusingMaterializer = ActorMaterializer(materializer.settings.withAutoFusing(false))
val request: HttpRequest = HttpRequest()
val requestRendered = ByteString(
"GET / HTTP/1.1\r\n" +
"Accept: */*\r\n" +
"Accept-Encoding: gzip, deflate\r\n" +
"Connection: keep-alive\r\n" +
"Host: example.com\r\n" +
"User-Agent: HTTPie/0.9.3\r\n" +
"\r\n"
)
val response: HttpResponse = HttpResponse()
val responseRendered: ByteString = ByteString(
s"HTTP/1.1 200 OK\r\n" +
s"Content-Length: 0\r\n" +
s"\r\n"
)
def TCPPlacebo(requests: Int): Flow[ByteString, ByteString, NotUsed] =
Flow.fromSinkAndSource(
Flow[ByteString].takeWhile(it => !(it.utf8String contains "Connection: close")) to Sink.ignore,
Source.repeat(requestRendered).take(requests)
)
def layer: BidiFlow[HttpResponse, SslTlsOutbound, SslTlsInbound, HttpRequest, NotUsed] = Http().serverLayer()(materializer)
def server(requests: Int): Flow[HttpResponse, HttpRequest, _] = layer atop TLSPlacebo() join TCPPlacebo(requests)
val reply = Flow[HttpRequest].map { _ => response }
@TearDown
def shutdown(): Unit = {
Await.result(system.terminate(), 5.seconds)
}
val nothingHere: Flow[HttpRequest, HttpResponse, NotUsed] =
Flow.fromSinkAndSource(Sink.cancelled, Source.empty)
@Benchmark
@OperationsPerInvocation(100000)
def run_10000_reqs() = {
val n = 100000
val latch = new CountDownLatch(n)
val replyCountdown = reply map { x =>
latch.countDown()
x
}
server(n).joinMat(replyCountdown)(Keep.right).run()(materializer)
latch.await()
}
}

View file

@ -1,37 +0,0 @@
/**
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package akka.http
import akka.http.scaladsl.model._
import java.util.concurrent.TimeUnit
import org.openjdk.jmh.annotations._
/**
* Benchmark used to verify move to name-based extraction does not hurt preformance.
* It does not allocate an Option thus it should be more optimal actually.
*/
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@BenchmarkMode(Array(Mode.Throughput))
class HttpMessageMatchingBenchmark {
val req = HttpRequest()
val res = HttpResponse()
@Benchmark
def res_matching: HttpResponse = {
res match {
case r @ HttpResponse(status, headers, entity, protocol) => r
}
}
@Benchmark
def req_matching: HttpRequest = {
req match {
case r @ HttpRequest(method, uri, headers, entity, protocol) => r
}
}
}

View file

@ -1,102 +0,0 @@
/**
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package akka.http
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext
import akka.Done
import akka.actor.ActorSystem
import akka.event.NoLogging
import akka.http.impl.engine.parsing.{ HttpHeaderParser, HttpRequestParser }
import akka.http.scaladsl.settings.ParserSettings
import akka.event.NoLogging
import akka.stream.ActorMaterializer
import akka.stream.TLSProtocol.SessionBytes
import akka.stream.scaladsl._
import akka.util.ByteString
import org.openjdk.jmh.annotations.{ OperationsPerInvocation, _ }
import org.openjdk.jmh.infra.Blackhole
import scala.concurrent.duration._
import scala.concurrent.{ Await, Future }
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Array(Mode.Throughput))
class HttpRequestParsingBenchmark {
implicit val system: ActorSystem = ActorSystem("HttpRequestParsingBenchmark")
implicit val materializer = ActorMaterializer()(system)
val parserSettings = ParserSettings(system)
val parser = new HttpRequestParser(parserSettings, false, HttpHeaderParser(parserSettings, NoLogging)())
val dummySession = SSLContext.getDefault.createSSLEngine.getSession
@Param(Array("small", "large"))
var req: String = ""
def request = req match {
case "small" => requestBytesSmall
case "large" => requestBytesLarge
}
val requestBytesSmall: SessionBytes = SessionBytes(
dummySession,
ByteString(
"""|GET / HTTP/1.1
|Accept: */*
|Accept-Encoding: gzip, deflate
|Connection: keep-alive
|Host: example.com
|User-Agent: HTTPie/0.9.3
|
|""".stripMargin.replaceAll("\n", "\r\n")
)
)
val requestBytesLarge: SessionBytes = SessionBytes(
dummySession,
ByteString(
"""|GET /json HTTP/1.1
|Host: server
|User-Agent: Mozilla/5.0 (X11; Linux x86_64) Gecko/20130501 Firefox/30.0 AppleWebKit/600.00 Chrome/30.0.0000.0 Trident/10.0 Safari/600.00
|Cookie: uid=12345678901234567890; __utma=1.1234567890.1234567890.1234567890.1234567890.12; wd=2560x1600
|Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|Accept-Language: en-US,en;q=0.5
|Connection: keep-alive
|
|""".stripMargin.replaceAll("\n", "\r\n")
)
)
/*
// before:
[info] Benchmark (req) Mode Cnt Score Error Units
[info] HttpRequestParsingBenchmark.parse_10000_requests small thrpt 20 358 982.157 ± 93745.863 ops/s
[info] HttpRequestParsingBenchmark.parse_10000_requests large thrpt 20 388 335.666 ± 16990.715 ops/s
// after:
[info] HttpRequestParsingBenchmark.parse_10000_requests_val small thrpt 20 623 975.879 ± 6191.897 ops/s
[info] HttpRequestParsingBenchmark.parse_10000_requests_val large thrpt 20 507 460.283 ± 4735.843 ops/s
*/
val httpMessageParser = Flow.fromGraph(parser)
def flow(bytes: SessionBytes, n: Int): RunnableGraph[Future[Done]] =
Source.repeat(request).take(n)
.via(httpMessageParser)
.toMat(Sink.ignore)(Keep.right)
@Benchmark
@OperationsPerInvocation(10000)
def parse_10000_requests_val(blackhole: Blackhole): Unit = {
val done = flow(requestBytesSmall, 10000).run()
Await.ready(done, 32.days)
}
@TearDown
def shutdown(): Unit = {
Await.result(system.terminate(), 5.seconds)
}
}

View file

@ -1,250 +0,0 @@
/**
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package akka.http
import java.util.concurrent.{ CountDownLatch, TimeUnit }
import akka.NotUsed
import akka.actor.ActorSystem
import akka.event.NoLogging
import akka.http.impl.engine.rendering.ResponseRenderingOutput.HttpData
import akka.http.impl.engine.rendering.{ HttpResponseRendererFactory, ResponseRenderingContext, ResponseRenderingOutput }
import akka.http.scaladsl.Http
import akka.http.scaladsl.model._
import akka.http.scaladsl.model.headers.Server
import akka.http.scaladsl.unmarshalling.Unmarshal
import akka.stream._
import akka.stream.scaladsl._
import akka.stream.stage.{ GraphStageLogic, GraphStageWithMaterializedValue, InHandler }
import akka.util.ByteString
import com.typesafe.config.ConfigFactory
import org.openjdk.jmh.annotations._
import org.openjdk.jmh.infra.Blackhole
import scala.concurrent.duration._
import scala.concurrent.{ Await, Future }
import scala.util.Try
@State(Scope.Benchmark)
@OutputTimeUnit(TimeUnit.SECONDS)
@BenchmarkMode(Array(Mode.Throughput))
class HttpResponseRenderingBenchmark extends HttpResponseRendererFactory(
serverHeader = Some(Server("Akka HTTP 2.4.x")),
responseHeaderSizeHint = 64,
log = NoLogging
) {
val config = ConfigFactory.parseString(
"""
akka {
loglevel = "ERROR"
}""".stripMargin
).withFallback(ConfigFactory.load())
implicit val system = ActorSystem("HttpResponseRenderingBenchmark", config)
implicit val materializer = ActorMaterializer()
import system.dispatcher
val requestRendered = ByteString(
"GET / HTTP/1.1\r\n" +
"Accept: */*\r\n" +
"Accept-Encoding: gzip, deflate\r\n" +
"Connection: keep-alive\r\n" +
"Host: example.com\r\n" +
"User-Agent: HTTPie/0.9.3\r\n" +
"\r\n"
)
def TCPPlacebo(requests: Int): Flow[ByteString, ByteString, NotUsed] =
Flow.fromSinkAndSource(
Flow[ByteString].takeWhile(it => !(it.utf8String contains "Connection: close")) to Sink.ignore,
Source.repeat(requestRendered).take(requests)
)
def TlsPlacebo = TLSPlacebo()
val requestRendering: Flow[HttpRequest, String, NotUsed] =
Http()
.clientLayer(headers.Host("blah.com"))
.atop(TlsPlacebo)
.join {
Flow[ByteString].map { x
val response = s"HTTP/1.1 200 OK\r\nContent-Length: ${x.size}\r\n\r\n"
ByteString(response) ++ x
}
}
.mapAsync(1)(response => Unmarshal(response).to[String])
def renderResponse: Future[String] = Source.single(HttpRequest(uri = "/foo"))
.via(requestRendering)
.runWith(Sink.head)
var request: HttpRequest = _
var pool: Flow[(HttpRequest, Int), (Try[HttpResponse], Int), _] = _
@TearDown
def shutdown(): Unit = {
Await.ready(Http().shutdownAllConnectionPools(), 1.second)
Await.result(system.terminate(), 5.seconds)
}
/*
[info] Benchmark Mode Cnt Score Error Units
[info] HttpResponseRenderingBenchmark.header_date_val thrpt 20 2 704 169 260 029.906 ± 234456086114.237 ops/s
// def, normal time
[info] HttpResponseRenderingBenchmark.header_date_def thrpt 20 178 297 625 609.638 ± 7429280865.659 ops/s
[info] HttpResponseRenderingBenchmark.response_ok_simple_val thrpt 20 1 258 119.673 ± 58399.454 ops/s
[info] HttpResponseRenderingBenchmark.response_ok_simple_def thrpt 20 687 576.928 ± 94813.618 ops/s
// clock nanos
[info] HttpResponseRenderingBenchmark.response_ok_simple_clock thrpt 20 1 676 438.649 ± 33976.590 ops/s
[info] HttpResponseRenderingBenchmark.response_ok_simple_clock thrpt 40 1 199 462.263 ± 222226.304 ops/s
// ------
// before optimisig collectFirst
[info] HttpResponseRenderingBenchmark.json_response thrpt 20 1 782 572.845 ± 16572.625 ops/s
[info] HttpResponseRenderingBenchmark.simple_response thrpt 20 1 611 802.216 ± 19557.151 ops/s
// after removing collectFirst and Option from renderHeaders
// not much of a difference, but hey, less Option allocs
[info] HttpResponseRenderingBenchmark.json_response thrpt 20 1 785 152.896 ± 15210.299 ops/s
[info] HttpResponseRenderingBenchmark.simple_response thrpt 20 1 783 800.184 ± 14938.415 ops/s
// -----
// baseline for this optimisation is the above results (after collectFirst).
// after introducing pre-rendered ContentType headers:
normal clock
[info] HttpResponseRenderingBenchmark.json_long_raw_response thrpt 20 1738558.895 ± 159612.661 ops/s
[info] HttpResponseRenderingBenchmark.json_response thrpt 20 1714176.824 ± 100011.642 ops/s
"fast clock"
[info] HttpResponseRenderingBenchmark.json_long_raw_response thrpt 20 1 528 632.480 ± 44934.827 ops/s
[info] HttpResponseRenderingBenchmark.json_response thrpt 20 1 517 383.792 ± 28256.716 ops/s
*/
/**
* HTTP/1.1 200 OK
* Server: Akka HTTP 2.4.x
* Date: Tue, 26 Jul 2016 15:26:53 GMT
* Content-Type: text/plain; charset=UTF-8
* Content-Length: 6
*
* ENTITY
*/
val simpleResponse =
ResponseRenderingContext(
response = HttpResponse(
200,
headers = Nil,
entity = HttpEntity("ENTITY")
),
requestMethod = HttpMethods.GET
)
/**
* HTTP/1.1 200 OK
* Server: Akka HTTP 2.4.x
* Date: Tue, 26 Jul 2016 15:26:53 GMT
* Content-Type: application/json
* Content-Length: 27
*
* {"message":"Hello, World!"}
*/
val jsonResponse =
ResponseRenderingContext(
response = HttpResponse(
200,
headers = Nil,
entity = HttpEntity(ContentTypes.`application/json`, """{"message":"Hello, World!"}""")
),
requestMethod = HttpMethods.GET
)
/**
* HTTP/1.1 200 OK
* Server: Akka HTTP 2.4.x
* Date: Tue, 26 Jul 2016 15:26:53 GMT
* Content-Type: application/json
* Content-Length: 315
*
* [{"id":4174,"randomNumber":331},{"id":51,"randomNumber":6544},{"id":4462,"randomNumber":952},{"id":2221,"randomNumber":532},{"id":9276,"randomNumber":3097},{"id":3056,"randomNumber":7293},{"id":6964,"randomNumber":620},{"id":675,"randomNumber":6601},{"id":8414,"randomNumber":6569},{"id":2753,"randomNumber":4065}]
*/
val jsonLongRawResponse =
ResponseRenderingContext(
response = HttpResponse(
200,
headers = Nil,
entity = HttpEntity(ContentTypes.`application/json`, """[{"id":4174,"randomNumber":331},{"id":51,"randomNumber":6544},{"id":4462,"randomNumber":952},{"id":2221,"randomNumber":532},{"id":9276,"randomNumber":3097},{"id":3056,"randomNumber":7293},{"id":6964,"randomNumber":620},{"id":675,"randomNumber":6601},{"id":8414,"randomNumber":6569},{"id":2753,"randomNumber":4065}]""")
),
requestMethod = HttpMethods.GET
)
@Benchmark
@Threads(8)
@OperationsPerInvocation(100 * 1000)
def simple_response(blackhole: Blackhole): Unit =
renderToImpl(simpleResponse, blackhole, n = 100 * 1000).await()
@Benchmark
@OperationsPerInvocation(100 * 1000)
def json_response(blackhole: Blackhole): Unit =
renderToImpl(jsonResponse, blackhole, n = 100 * 1000).await()
/*
Difference between 27 and 315 bytes long JSON is:
[info] Benchmark Mode Cnt Score Error Units
[info] HttpResponseRenderingBenchmark.json_long_raw_response thrpt 20 1 932 331.049 ± 64125.621 ops/s
[info] HttpResponseRenderingBenchmark.json_response thrpt 20 1 973 232.941 ± 18568.314 ops/s
*/
@Benchmark
@OperationsPerInvocation(100 * 1000)
def json_long_raw_response(blackhole: Blackhole): Unit =
renderToImpl(jsonLongRawResponse, blackhole, n = 100 * 1000).await()
class JitSafeLatch[A](blackhole: Blackhole, n: Int) extends GraphStageWithMaterializedValue[SinkShape[A], CountDownLatch] {
val in = Inlet[A]("JitSafeLatch.in")
override val shape = SinkShape(in)
override def createLogicAndMaterializedValue(inheritedAttributes: Attributes): (GraphStageLogic, CountDownLatch) = {
val latch = new CountDownLatch(n)
val logic = new GraphStageLogic(shape) with InHandler {
override def preStart(): Unit = pull(in)
override def onPush(): Unit = {
if (blackhole ne null) blackhole.consume(grab(in))
latch.countDown()
pull(in)
}
setHandler(in, this)
}
(logic, latch)
}
}
def renderToImpl(ctx: ResponseRenderingContext, blackhole: Blackhole, n: Int)(implicit mat: Materializer): CountDownLatch = {
val latch =
(Source.repeat(ctx).take(n) ++ Source.maybe[ResponseRenderingContext]) // never send upstream completion
.via(renderer.named("renderer"))
.runWith(new JitSafeLatch[ResponseRenderingOutput](blackhole, n))
latch
}
// TODO benchmark with stable override
override def currentTimeMillis(): Long = System.currentTimeMillis()
// override def currentTimeMillis(): Long = System.currentTimeMillis() // DateTime(2011, 8, 25, 9, 10, 29).clicks // provide a stable date for testing
}

View file

@ -1,209 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl;
import akka.Done;
import akka.actor.AbstractActor;
import akka.http.javadsl.ConnectHttp;
import akka.http.javadsl.HostConnectionPool;
import akka.japi.Pair;
import akka.japi.pf.ReceiveBuilder;
import akka.stream.Materializer;
import akka.util.ByteString;
import scala.compat.java8.FutureConverters;
import scala.concurrent.ExecutionContextExecutor;
import scala.concurrent.Future;
import akka.stream.javadsl.*;
import akka.http.javadsl.OutgoingConnection;
import akka.http.javadsl.Http;
import static akka.http.javadsl.ConnectHttp.toHost;
import static akka.pattern.PatternsCS.*;
import java.util.concurrent.CompletionStage;
//#manual-entity-consume-example-1
import java.io.File;
import akka.actor.ActorSystem;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import akka.stream.ActorMaterializer;
import akka.stream.javadsl.Framing;
import akka.http.javadsl.model.*;
import scala.concurrent.duration.FiniteDuration;
import scala.util.Try;
//#manual-entity-consume-example-1
@SuppressWarnings("unused")
public class HttpClientExampleDocTest {
HttpResponse responseFromSomewhere() {
return null;
}
void manualEntityComsumeExample() {
//#manual-entity-consume-example-1
final ActorSystem system = ActorSystem.create();
final ExecutionContextExecutor dispatcher = system.dispatcher();
final ActorMaterializer materializer = ActorMaterializer.create(system);
final HttpResponse response = responseFromSomewhere();
final Function<ByteString, ByteString> transformEachLine = line -> line /* some transformation here */;
final int maximumFrameLength = 256;
response.entity().getDataBytes()
.via(Framing.delimiter(ByteString.fromString("\n"), maximumFrameLength, FramingTruncation.ALLOW))
.map(transformEachLine::apply)
.runWith(FileIO.toPath(new File("/tmp/example.out").toPath()), materializer);
//#manual-entity-consume-example-1
}
private
//#manual-entity-consume-example-2
final class ExamplePerson {
final String name;
public ExamplePerson(String name) { this.name = name; }
}
public ExamplePerson parse(ByteString line) {
return new ExamplePerson(line.utf8String());
}
//#manual-entity-consume-example-2
void manualEntityConsumeExample2() {
//#manual-entity-consume-example-2
final ActorSystem system = ActorSystem.create();
final ExecutionContextExecutor dispatcher = system.dispatcher();
final ActorMaterializer materializer = ActorMaterializer.create(system);
final HttpResponse response = responseFromSomewhere();
// toStrict to enforce all data be loaded into memory from the connection
final CompletionStage<HttpEntity.Strict> strictEntity = response.entity()
.toStrict(FiniteDuration.create(3, TimeUnit.SECONDS).toMillis(), materializer);
// while API remains the same to consume dataBytes, now they're in memory already:
final CompletionStage<ExamplePerson> person =
strictEntity
.thenCompose(strict ->
strict.getDataBytes()
.runFold(ByteString.empty(), (acc, b) -> acc.concat(b), materializer)
.thenApply(this::parse)
);
//#manual-entity-consume-example-2
}
void manualEntityDiscardExample1() {
//#manual-entity-discard-example-1
final ActorSystem system = ActorSystem.create();
final ExecutionContextExecutor dispatcher = system.dispatcher();
final ActorMaterializer materializer = ActorMaterializer.create(system);
final HttpResponse response = responseFromSomewhere();
final HttpMessage.DiscardedEntity discarded = response.discardEntityBytes(materializer);
discarded.completionStage().whenComplete((done, ex) -> {
System.out.println("Entity discarded completely!");
});
//#manual-entity-discard-example-1
}
void manualEntityDiscardExample2() {
//#manual-entity-discard-example-2
final ActorSystem system = ActorSystem.create();
final ExecutionContextExecutor dispatcher = system.dispatcher();
final ActorMaterializer materializer = ActorMaterializer.create(system);
final HttpResponse response = responseFromSomewhere();
final CompletionStage<Done> discardingComplete = response.entity().getDataBytes().runWith(Sink.ignore(), materializer);
discardingComplete.whenComplete((done, ex) -> {
System.out.println("Entity discarded completely!");
});
//#manual-entity-discard-example-2
}
// compile only test
public void testConstructRequest() {
//#outgoing-connection-example
final ActorSystem system = ActorSystem.create();
final ActorMaterializer materializer = ActorMaterializer.create(system);
final Flow<HttpRequest, HttpResponse, CompletionStage<OutgoingConnection>> connectionFlow =
Http.get(system).outgoingConnection(toHost("akka.io", 80));
final CompletionStage<HttpResponse> responseFuture =
Source.single(HttpRequest.create("/"))
.via(connectionFlow)
.runWith(Sink.<HttpResponse>head(), materializer);
//#outgoing-connection-example
}
// compile only test
public void testHostLevelExample() {
//#host-level-example
final ActorSystem system = ActorSystem.create();
final ActorMaterializer materializer = ActorMaterializer.create(system);
// construct a pool client flow with context type `Integer`
final Flow<
Pair<HttpRequest, Integer>,
Pair<Try<HttpResponse>, Integer>,
HostConnectionPool> poolClientFlow =
Http.get(system).<Integer>cachedHostConnectionPool(toHost("akka.io", 80), materializer);
// construct a pool client flow with context type `Integer`
final CompletionStage<Pair<Try<HttpResponse>, Integer>> responseFuture =
Source
.single(Pair.create(HttpRequest.create("/"), 42))
.via(poolClientFlow)
.runWith(Sink.<Pair<Try<HttpResponse>, Integer>>head(), materializer);
//#host-level-example
}
// compile only test
public void testSingleRequestExample() {
//#single-request-example
final ActorSystem system = ActorSystem.create();
final Materializer materializer = ActorMaterializer.create(system);
final CompletionStage<HttpResponse> responseFuture =
Http.get(system)
.singleRequest(HttpRequest.create("http://akka.io"), materializer);
//#single-request-example
}
static
//#single-request-in-actor-example
class Myself extends AbstractActor {
final Http http = Http.get(context().system());
final ExecutionContextExecutor dispatcher = context().dispatcher();
final Materializer materializer = ActorMaterializer.create(context());
public Myself() {
receive(ReceiveBuilder
.match(String.class, url -> {
pipe(fetch (url), dispatcher).to(self());
}).build());
}
CompletionStage<HttpResponse> fetch(String url) {
return http.singleRequest(HttpRequest.create(url), materializer);
}
}
//#single-request-in-actor-example
}

View file

@ -1,49 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl;
import akka.actor.AbstractActor;
import akka.actor.ActorSystem;
import akka.http.javadsl.*;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.HttpResponse;
import akka.japi.Pair;
import akka.japi.pf.ReceiveBuilder;
import akka.stream.ActorMaterializer;
import akka.stream.Materializer;
import akka.stream.javadsl.Flow;
import akka.stream.javadsl.Sink;
import akka.stream.javadsl.Source;
import com.typesafe.sslconfig.akka.AkkaSSLConfig;
import scala.concurrent.ExecutionContextExecutor;
import scala.util.Try;
import java.util.concurrent.CompletionStage;
import static akka.http.javadsl.ConnectHttp.toHost;
import static akka.pattern.PatternsCS.pipe;
@SuppressWarnings("unused")
public class HttpsExamplesDocTest {
// compile only test
public void testConstructRequest() {
String unsafeHost = "example.com";
//#disable-sni-connection
final ActorSystem system = ActorSystem.create();
final ActorMaterializer mat = ActorMaterializer.create(system);
final Http http = Http.get(system);
// WARNING: disabling SNI is a very bad idea, please don't unless you have a very good reason to.
final AkkaSSLConfig defaultSSLConfig = AkkaSSLConfig.get(system);
final AkkaSSLConfig badSslConfig = defaultSSLConfig
.convertSettings(s -> s.withLoose(s.loose().withDisableSNI(true)));
final HttpsConnectionContext badCtx = http.createClientHttpsContext(badSslConfig);
http.outgoingConnection(ConnectHttp.toHostHttps(unsafeHost).withCustomHttpsContext(badCtx));
//#disable-sni-connection
}
}

View file

@ -1,91 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl;
import akka.util.ByteString;
import org.junit.Test;
//#import-model
import akka.http.javadsl.model.*;
import akka.http.javadsl.model.headers.*;
import java.util.Optional;
//#import-model
@SuppressWarnings("unused")
public class ModelDocTest {
@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(ContentTypes.TEXT_PLAIN_UTF8, "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 Optional<BasicHttpCredentials> getCredentialsOfRequest(HttpRequest request) {
Optional<Authorization> auth = request.getHeader(Authorization.class);
if (auth.isPresent() && auth.get().credentials() instanceof BasicHttpCredentials)
return Optional.of((BasicHttpCredentials) auth.get().credentials());
else
return Optional.empty();
}
//#headers
}

View file

@ -1,241 +0,0 @@
/*
* Copyright (C) 2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl;
import akka.Done;
import akka.NotUsed;
import akka.actor.ActorSystem;
import akka.http.javadsl.Http;
import akka.http.javadsl.model.StatusCodes;
import akka.http.javadsl.model.headers.Authorization;
import akka.http.javadsl.model.ws.Message;
import akka.http.javadsl.model.ws.TextMessage;
import akka.http.javadsl.model.ws.WebSocketRequest;
import akka.http.javadsl.model.ws.WebSocketUpgradeResponse;
import akka.japi.Pair;
import akka.stream.ActorMaterializer;
import akka.stream.Materializer;
import akka.stream.javadsl.Flow;
import akka.stream.javadsl.Keep;
import akka.stream.javadsl.Sink;
import akka.stream.javadsl.Source;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
@SuppressWarnings("unused")
public class WebSocketClientExampleTest {
// compile only test
public void testSingleWebSocketRequest() {
//#single-WebSocket-request
ActorSystem system = ActorSystem.create();
Materializer materializer = ActorMaterializer.create(system);
Http http = Http.get(system);
// print each incoming text message
// would throw exception on non strict or binary message
final Sink<Message, CompletionStage<Done>> printSink =
Sink.foreach((message) ->
System.out.println("Got message: " + message.asTextMessage().getStrictText())
);
// send this as a message over the WebSocket
final Source<Message, NotUsed> helloSource =
Source.single(TextMessage.create("hello world"));
// the CompletionStage<Done> is the materialized value of Sink.foreach
// and it is completed when the stream completes
final Flow<Message, Message, CompletionStage<Done>> flow =
Flow.fromSinkAndSourceMat(printSink, helloSource, Keep.left());
final Pair<CompletionStage<WebSocketUpgradeResponse>, CompletionStage<Done>> pair =
http.singleWebSocketRequest(
WebSocketRequest.create("ws://echo.websocket.org"),
flow,
materializer
);
// The first value in the pair is a CompletionStage<WebSocketUpgradeResponse> that
// completes when the WebSocket request has connected successfully (or failed)
final CompletionStage<Done> connected = pair.first().thenApply(upgrade -> {
// just like a regular http request we can access response status which is available via upgrade.response.status
// status code 101 (Switching Protocols) indicates that server support WebSockets
if (upgrade.response().status().equals(StatusCodes.SWITCHING_PROTOCOLS)) {
return Done.getInstance();
} else {
throw new RuntimeException("Connection failed: " + upgrade.response().status());
}
});
// the second value is the completion of the sink from above
// in other words, it completes when the WebSocket disconnects
final CompletionStage<Done> closed = pair.second();
// in a real application you would not side effect here
// and handle errors more carefully
connected.thenAccept(done -> System.out.println("Connected"));
closed.thenAccept(done -> System.out.println("Connection closed"));
//#single-WebSocket-request
}
// compile only test
public void halfClosedWebSocketClosingExample() {
final ActorSystem system = ActorSystem.create();
final Materializer materializer = ActorMaterializer.create(system);
final Http http = Http.get(system);
//#half-closed-WebSocket-closing
// we may expect to be able to to just tail
// the server websocket output like this
final Flow<Message, Message, NotUsed> flow =
Flow.fromSinkAndSource(
Sink.foreach(System.out::println),
Source.empty());
http.singleWebSocketRequest(
WebSocketRequest.create("ws://example.com:8080/some/path"),
flow,
materializer);
//#half-closed-WebSocket-closing
}
public void halfClosedWebSocketWorkingExample() {
final ActorSystem system = ActorSystem.create();
final Materializer materializer = ActorMaterializer.create(system);
final Http http = Http.get(system);
//#half-closed-WebSocket-working
// using Source.maybe materializes into a completable future
// which will allow us to complete the source later
final Flow<Message, Message, CompletableFuture<Optional<Message>>> flow =
Flow.fromSinkAndSourceMat(
Sink.foreach(System.out::println),
Source.maybe(),
Keep.right());
final Pair<CompletionStage<WebSocketUpgradeResponse>, CompletableFuture<Optional<Message>>> pair =
http.singleWebSocketRequest(
WebSocketRequest.create("ws://example.com:8080/some/path"),
flow,
materializer);
// at some later time we want to disconnect
pair.second().complete(Optional.empty());
//#half-closed-WebSocket-working
}
public void halfClosedWebSocketFiniteWorkingExample() {
final ActorSystem system = ActorSystem.create();
final Materializer materializer = ActorMaterializer.create(system);
final Http http = Http.get(system);
//#half-closed-WebSocket-finite
// emit "one" and then "two" and then keep the source from completing
final Source<Message, CompletableFuture<Optional<Message>>> source =
Source.from(Arrays.<Message>asList(TextMessage.create("one"), TextMessage.create("two")))
.concatMat(Source.maybe(), Keep.right());
final Flow<Message, Message, CompletableFuture<Optional<Message>>> flow =
Flow.fromSinkAndSourceMat(
Sink.foreach(System.out::println),
source,
Keep.right());
final Pair<CompletionStage<WebSocketUpgradeResponse>, CompletableFuture<Optional<Message>>> pair =
http.singleWebSocketRequest(
WebSocketRequest.create("ws://example.com:8080/some/path"),
flow,
materializer);
// at some later time we want to disconnect
pair.second().complete(Optional.empty());
//#half-closed-WebSocket-finite
}
// compile time only test
public void testAuthorizedSingleWebSocketRequest() {
Materializer materializer = null;
Http http = null;
Flow<Message, Message, NotUsed> flow = null;
//#authorized-single-WebSocket-request
http.singleWebSocketRequest(
WebSocketRequest.create("ws://example.com:8080/some/path")
.addHeader(Authorization.basic("johan", "correcthorsebatterystaple")),
flow,
materializer);
//#authorized-single-WebSocket-request
}
// compile time only test
public void testWebSocketClientFlow() {
//#WebSocket-client-flow
ActorSystem system = ActorSystem.create();
Materializer materializer = ActorMaterializer.create(system);
Http http = Http.get(system);
// print each incoming text message
// would throw exception on non strict or binary message
Sink<Message, CompletionStage<Done>> printSink =
Sink.foreach((message) ->
System.out.println("Got message: " + message.asTextMessage().getStrictText())
);
// send this as a message over the WebSocket
Source<Message, NotUsed> helloSource =
Source.single(TextMessage.create("hello world"));
Flow<Message, Message, CompletionStage<WebSocketUpgradeResponse>> webSocketFlow =
http.webSocketClientFlow(WebSocketRequest.create("ws://echo.websocket.org"));
Pair<CompletionStage<WebSocketUpgradeResponse>, CompletionStage<Done>> pair =
helloSource.viaMat(webSocketFlow, Keep.right())
.toMat(printSink, Keep.both())
.run(materializer);
// The first value in the pair is a CompletionStage<WebSocketUpgradeResponse> that
// completes when the WebSocket request has connected successfully (or failed)
CompletionStage<WebSocketUpgradeResponse> upgradeCompletion = pair.first();
// the second value is the completion of the sink from above
// in other words, it completes when the WebSocket disconnects
CompletionStage<Done> closed = pair.second();
CompletionStage<Done> connected = upgradeCompletion.thenApply(upgrade->
{
// just like a regular http request we can access response status which is available via upgrade.response.status
// status code 101 (Switching Protocols) indicates that server support WebSockets
if (upgrade.response().status().equals(StatusCodes.SWITCHING_PROTOCOLS)) {
return Done.getInstance();
} else {
throw new RuntimeException(("Connection failed: " + upgrade.response().status()));
}
});
// in a real application you would not side effect here
// and handle errors more carefully
connected.thenAccept(done -> System.out.println("Connected"));
closed.thenAccept(done -> System.out.println("Connection closed"));
//#WebSocket-client-flow
}
}

View file

@ -1,75 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
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.unmarshalling.StringUnmarshallers;
import akka.http.javadsl.unmarshalling.StringUnmarshaller;
import akka.http.javadsl.unmarshalling.Unmarshaller;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.japi.Pair;
public class FormFieldRequestValsExampleTest extends JUnitRouteTest {
@Test
public void testFormFieldVals() {
//#simple
final Route route =
formField("name", n ->
formField(StringUnmarshallers.INTEGER, "age", a ->
complete(String.format("Name: %s, age: %d", n, a))
)
);
// tests:
final FormData formData = FormData.create(
Pair.create("name", "Blippy"),
Pair.create("age", "42"));
final HttpRequest request =
HttpRequest
.POST("/")
.withEntity(formData.toEntity());
testRoute(route).run(request).assertEntity("Name: Blippy, age: 42");
//#simple
}
@Test
public void testFormFieldValsUnmarshaling() {
//#custom-unmarshal
Unmarshaller<String, SampleId> SAMPLE_ID = StringUnmarshaller.sync(s -> new SampleId(Integer.valueOf(s)));
final Route route =
formField(SAMPLE_ID, "id", sid ->
complete(String.format("SampleId: %s", sid.id))
);
// tests:
final FormData formData = FormData.create(Pair.create("id", "1337"));
final HttpRequest request =
HttpRequest
.POST("/")
.withEntity(formData.toEntity());
testRoute(route).run(request).assertEntity("SampleId: 1337");
//#custom-unmarshal
}
static class SampleId {
public final int id;
SampleId(int id) {
this.id = id;
}
}
}

View file

@ -1,55 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
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.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
public class HeaderRequestValsExampleTest extends JUnitRouteTest {
@Test
public void testHeaderVals() {
//#by-class
final Route route =
extractHost(host ->
complete(String.format("Host header was: %s", host))
);
// tests:
final HttpRequest request =
HttpRequest
.GET("http://akka.io/")
.addHeader(Host.create("akka.io"));
testRoute(route).run(request).assertEntity("Host header was: akka.io");
//#by-class
}
@Test
public void testHeaderByName() {
//#by-name
final Route route =
// 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:
final HttpRequest request =
HttpRequest
.GET("/")
.addHeader(RawHeader.create("X-Fish-Name", "Blippy"));
testRoute(route).run(request).assertEntity("The `X-Fish-Name` header's value was: Blippy");
//#by-name
}
}

View file

@ -1,45 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server;
//#binding-failure-high-level-example
import akka.NotUsed;
import akka.actor.ActorSystem;
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();
final ActorMaterializer materializer = ActorMaterializer.create(system);
// HttpApp.bindRoute expects a route being provided by HttpApp.createRoute
final HighLevelServerExample app = new HighLevelServerExample();
final Route route = app.createRoute();
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);
binding.exceptionally(failure -> {
System.err.println("Something very bad happened! " + failure.getMessage());
system.terminate();
return null;
});
system.terminate();
}
}
//#binding-failure-high-level-example

View file

@ -1,77 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
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.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 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
final HighLevelServerExample app = new HighLevelServerExample();
final Http http = Http.get(system);
final ActorMaterializer materializer = ActorMaterializer.create(system);
final Flow<HttpRequest, HttpResponse, NotUsed> routeFlow = app.createRoute().flow(system, materializer);
final CompletionStage<ServerBinding> binding = http.bindAndHandle(routeFlow, ConnectHttp.toHost("localhost", 8080), materializer);
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

View file

@ -1,55 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
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.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.scaladsl.model.headers.Authorization;
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 Route route =
authenticateBasic("My realm", this::authenticate, user ->
complete("Hello " + user + "!")
);
// tests:
final HttpRequest okRequest =
HttpRequest
.GET("http://akka.io/")
.addHeader(Host.create("akka.io"))
.addHeader(Authorization.basic("randal", "correcthorsebatterystaple"));
testRoute(route).run(okRequest).assertEntity("Hello randal!");
final HttpRequest badRequest =
HttpRequest
.GET("http://akka.io/")
.addHeader(Host.create("akka.io"))
.addHeader(Authorization.basic("randal", "123abc"));
testRoute(route).run(badRequest).assertStatusCode(401);
//#basic-authenticator-java
}
}

View file

@ -1,328 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server;
import akka.Done;
import akka.NotUsed;
import akka.actor.ActorSystem;
import akka.http.javadsl.ConnectHttp;
import akka.http.javadsl.Http;
import akka.http.javadsl.IncomingConnection;
import akka.http.javadsl.ServerBinding;
import akka.http.javadsl.marshallers.jackson.Jackson;
import akka.http.javadsl.model.*;
import akka.http.javadsl.model.headers.Connection;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.unmarshalling.Unmarshaller;
import akka.japi.function.Function;
import akka.stream.ActorMaterializer;
import akka.stream.IOResult;
import akka.stream.Materializer;
import akka.stream.javadsl.FileIO;
import akka.stream.javadsl.Flow;
import akka.stream.javadsl.Sink;
import akka.stream.javadsl.Source;
import akka.util.ByteString;
import scala.concurrent.ExecutionContextExecutor;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import static akka.http.javadsl.server.Directives.*;
@SuppressWarnings("unused")
public class HttpServerExampleDocTest {
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);
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();
}
//#consume-entity-directive
class Bid {
final String userId;
final int bid;
Bid(String userId, int bid) {
this.userId = userId;
this.bid = bid;
}
}
//#consume-entity-directive
void consumeEntityUsingEntityDirective() {
//#consume-entity-directive
final ActorSystem system = ActorSystem.create();
final ExecutionContextExecutor dispatcher = system.dispatcher();
final ActorMaterializer materializer = ActorMaterializer.create(system);
final Unmarshaller<HttpEntity, Bid> asBid = Jackson.unmarshaller(Bid.class);
final Route s = path("bid", () ->
put(() ->
entity(asBid, bid ->
// incoming entity is fully consumed and converted into a Bid
complete("The bid was: " + bid)
)
)
);
//#consume-entity-directive
}
void consumeEntityUsingRawDataBytes() {
//#consume-raw-dataBytes
final ActorSystem system = ActorSystem.create();
final ExecutionContextExecutor dispatcher = system.dispatcher();
final ActorMaterializer materializer = ActorMaterializer.create(system);
final Route s =
put(() ->
path("lines", () ->
withoutSizeLimit(() ->
extractDataBytes(bytes -> {
final CompletionStage<IOResult> res = bytes.runWith(FileIO.toPath(new File("/tmp/example.out").toPath()), materializer);
return onComplete(() -> res, ioResult ->
// we only want to respond once the incoming data has been handled:
complete("Finished writing data :" + ioResult));
})
)
)
);
//#consume-raw-dataBytes
}
void discardEntityUsingRawBytes() {
//#discard-discardEntityBytes
final ActorSystem system = ActorSystem.create();
final ExecutionContextExecutor dispatcher = system.dispatcher();
final ActorMaterializer materializer = ActorMaterializer.create(system);
final Route s =
put(() ->
path("lines", () ->
withoutSizeLimit(() ->
extractRequest(r -> {
final CompletionStage<Done> res = r.discardEntityBytes(materializer).completionStage();
return onComplete(() -> res, done ->
// we only want to respond once the incoming data has been handled:
complete("Finished writing data :" + done));
})
)
)
);
//#discard-discardEntityBytes
}
void discardEntityManuallyCloseConnections() {
//#discard-close-connections
final ActorSystem system = ActorSystem.create();
final ExecutionContextExecutor dispatcher = system.dispatcher();
final ActorMaterializer materializer = ActorMaterializer.create(system);
final Route s =
put(() ->
path("lines", () ->
withoutSizeLimit(() ->
extractDataBytes(bytes -> {
// Closing connections, method 1 (eager):
// we deem this request as illegal, and close the connection right away:
bytes.runWith(Sink.cancelled(), materializer); // "brutally" closes the connection
// Closing connections, method 2 (graceful):
// consider draining connection and replying with `Connection: Close` header
// if you want the client to close after this request/reply cycle instead:
return respondWithHeader(Connection.create("close"), () ->
complete(StatusCodes.FORBIDDEN, "Not allowed!")
);
})
)
)
);
//#discard-close-connections
}
}

View file

@ -1,27 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server;
import akka.actor.ActorSystem;
import com.typesafe.sslconfig.akka.AkkaSSLConfig;
import org.junit.Test;
import org.scalatest.junit.JUnitSuite;
/* COMPILE ONLY TEST */
public class HttpsServerExampleTest extends JUnitSuite {
@Test
public void compileOnlySpec() throws Exception {
// just making sure for it to be really compiled / run even if empty
}
void sslConfigGet() {
//#akka-ssl-config
final ActorSystem system = ActorSystem.create();
final AkkaSSLConfig sslConfig = AkkaSSLConfig.get(system);
//#
}
}

View file

@ -1,179 +0,0 @@
/*
* Copyright (C) 2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server;
import akka.NotUsed;
import akka.http.javadsl.common.CsvEntityStreamingSupport;
import akka.http.javadsl.common.JsonEntityStreamingSupport;
import akka.http.javadsl.marshallers.jackson.Jackson;
import akka.http.javadsl.marshalling.Marshaller;
import akka.http.javadsl.model.*;
import akka.http.javadsl.model.headers.Accept;
import akka.http.javadsl.server.*;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.testkit.TestRoute;
import akka.http.javadsl.unmarshalling.StringUnmarshallers;
import akka.http.javadsl.common.EntityStreamingSupport;
import akka.http.javadsl.unmarshalling.Unmarshaller;
import akka.stream.javadsl.Flow;
import akka.stream.javadsl.Source;
import akka.util.ByteString;
import org.junit.Test;
import java.util.concurrent.CompletionStage;
public class JsonStreamingExamplesTest extends JUnitRouteTest {
//#routes
final Route tweets() {
//#formats
final Unmarshaller<ByteString, JavaTweet> JavaTweets = Jackson.byteStringUnmarshaller(JavaTweet.class);
//#formats
//#response-streaming
// Step 1: Enable JSON streaming
// we're not using this in the example, but it's the simplest way to start:
// The default rendering is a JSON array: `[el, el, el , ...]`
final JsonEntityStreamingSupport jsonStreaming = EntityStreamingSupport.json();
// Step 1.1: Enable and customise how we'll render the JSON, as a compact array:
final ByteString start = ByteString.fromString("[");
final ByteString between = ByteString.fromString(",");
final ByteString end = ByteString.fromString("]");
final Flow<ByteString, ByteString, NotUsed> compactArrayRendering =
Flow.of(ByteString.class).intersperse(start, between, end);
final JsonEntityStreamingSupport compactJsonSupport = EntityStreamingSupport.json()
.withFramingRendererFlow(compactArrayRendering);
// Step 2: implement the route
final Route responseStreaming = path("tweets", () ->
get(() ->
parameter(StringUnmarshallers.INTEGER, "n", n -> {
final Source<JavaTweet, NotUsed> tws =
Source.repeat(new JavaTweet(12, "Hello World!")).take(n);
// Step 3: call complete* with your source, marshaller, and stream rendering mode
return completeOKWithSource(tws, Jackson.marshaller(), compactJsonSupport);
})
)
);
//#response-streaming
//#incoming-request-streaming
final Route incomingStreaming = path("tweets", () ->
post(() ->
extractMaterializer(mat -> {
final JsonEntityStreamingSupport jsonSupport = EntityStreamingSupport.json();
return entityAsSourceOf(JavaTweets, jsonSupport, sourceOfTweets -> {
final CompletionStage<Integer> tweetsCount = sourceOfTweets.runFold(0, (acc, tweet) -> acc + 1, mat);
return onComplete(tweetsCount, c -> complete("Total number of tweets: " + c));
});
}
)
)
);
//#incoming-request-streaming
return responseStreaming.orElse(incomingStreaming);
}
final Route csvTweets() {
//#csv-example
final Marshaller<JavaTweet, ByteString> renderAsCsv =
Marshaller.withFixedContentType(ContentTypes.TEXT_CSV_UTF8, t ->
ByteString.fromString(t.getId() + "," + t.getMessage())
);
final CsvEntityStreamingSupport compactJsonSupport = EntityStreamingSupport.csv();
final Route responseStreaming = path("tweets", () ->
get(() ->
parameter(StringUnmarshallers.INTEGER, "n", n -> {
final Source<JavaTweet, NotUsed> tws =
Source.repeat(new JavaTweet(12, "Hello World!")).take(n);
return completeWithSource(tws, renderAsCsv, compactJsonSupport);
})
)
);
//#csv-example
return responseStreaming;
}
//#routes
@Test
public void getTweetsTest() {
//#response-streaming
// tests:
final TestRoute routes = testRoute(tweets());
// test happy path
final Accept acceptApplication = Accept.create(MediaRanges.create(MediaTypes.APPLICATION_JSON));
routes.run(HttpRequest.GET("/tweets?n=2").addHeader(acceptApplication))
.assertStatusCode(200)
.assertEntity("[{\"id\":12,\"message\":\"Hello World!\"},{\"id\":12,\"message\":\"Hello World!\"}]");
// test responses to potential errors
final Accept acceptText = Accept.create(MediaRanges.ALL_TEXT);
routes.run(HttpRequest.GET("/tweets?n=3").addHeader(acceptText))
.assertStatusCode(StatusCodes.NOT_ACCEPTABLE) // 406
.assertEntity("Resource representation is only available with these types:\napplication/json");
//#response-streaming
}
@Test
public void csvExampleTweetsTest() {
//#response-streaming
// tests --------------------------------------------
final TestRoute routes = testRoute(csvTweets());
// test happy path
final Accept acceptCsv = Accept.create(MediaRanges.create(MediaTypes.TEXT_CSV));
routes.run(HttpRequest.GET("/tweets?n=2").addHeader(acceptCsv))
.assertStatusCode(200)
.assertEntity("12,Hello World!\n" +
"12,Hello World!");
// test responses to potential errors
final Accept acceptText = Accept.create(MediaRanges.ALL_APPLICATION);
routes.run(HttpRequest.GET("/tweets?n=3").addHeader(acceptText))
.assertStatusCode(StatusCodes.NOT_ACCEPTABLE) // 406
.assertEntity("Resource representation is only available with these types:\ntext/csv; charset=UTF-8");
//#response-streaming
}
//#models
private static final class JavaTweet {
private int id;
private String message;
public JavaTweet(int id, String message) {
this.id = id;
this.message = message;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public void setMessage(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
//#models
}

View file

@ -1,59 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.headers.Host;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.scaladsl.model.headers.Authorization;
import java.util.Optional;
import org.junit.Test;
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 Route route =
authenticateOAuth2("My realm", this::authenticate, token ->
complete("The secret token is: " + token)
);
// tests:
final HttpRequest okRequest =
HttpRequest
.GET("http://akka.io/")
.addHeader(Host.create("akka.io"))
.addHeader(Authorization.oauth2("token"));
testRoute(route).run(okRequest).assertEntity("The secret token is: token");
final HttpRequest badRequest =
HttpRequest
.GET("http://akka.io/")
.addHeader(Host.create("akka.io"))
.addHeader(Authorization.oauth2("wrong"));
testRoute(route).run(badRequest).assertStatusCode(401);
//#oauth2-authenticator-java
}
}

View file

@ -1,79 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server;
import akka.http.javadsl.model.StatusCodes;
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", () ->
complete(StatusCodes.OK)
);
// matches "/test", as well
path(PathMatchers.segment("test"), () ->
complete(StatusCodes.OK)
);
// matches "/admin/user"
path(PathMatchers.segment("admin")
.slash("user"), () ->
complete(StatusCodes.OK)
);
// matches "/admin/user", as well
pathPrefix("admin", () ->
path("user", () ->
complete(StatusCodes.OK)
)
);
// 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
pathPrefix("admin", () ->
path("user", () ->
path(PathMatchers.integerSegment(), userId ->
complete("Hello user " + userId)
)
)
);
// 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(() ->
complete(StatusCodes.OK)
),
pathEnd(() ->
complete(StatusCodes.OK)
),
path(PathMatchers.integerSegment(), userId ->
complete("Hello user " + userId)
)
));
//#path-examples
}
}

View file

@ -1,105 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server;
//#websocket-example-using-core
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
import akka.NotUsed;
import akka.http.javadsl.ConnectHttp;
import akka.japi.Function;
import akka.japi.JavaPartialFunction;
import akka.stream.ActorMaterializer;
import akka.stream.Materializer;
import akka.stream.javadsl.Flow;
import akka.stream.javadsl.Source;
import akka.actor.ActorSystem;
import akka.http.javadsl.Http;
import akka.http.javadsl.ServerBinding;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.HttpResponse;
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());
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
public static void main(String[] args) throws Exception {
ActorSystem system = ActorSystem.create();
try {
final Materializer materializer = ActorMaterializer.create(system);
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();
}
}
//#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()));
}
}
//#websocket-handler
}
//#websocket-example-using-core

View file

@ -1,54 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
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;
public class WebSocketRoutingExample extends AllDirectives {
//#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, 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()));
}
}
}

View file

@ -1,893 +0,0 @@
/*
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import akka.NotUsed;
import akka.actor.ActorSystem;
import akka.dispatch.ExecutionContexts;
import akka.event.Logging;
import akka.event.LoggingAdapter;
import akka.http.javadsl.model.ContentTypes;
import akka.http.javadsl.model.HttpEntities;
import akka.http.javadsl.model.HttpEntity;
import akka.http.javadsl.model.HttpMethods;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.HttpResponse;
import akka.http.javadsl.model.ResponseEntity;
import akka.http.javadsl.model.StatusCodes;
import akka.http.javadsl.model.headers.RawHeader;
import akka.http.javadsl.model.headers.Server;
import akka.http.javadsl.model.headers.ProductVersion;
import akka.http.javadsl.settings.RoutingSettings;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.server.*;
import akka.japi.pf.PFBuilder;
import akka.stream.ActorMaterializer;
import akka.stream.ActorMaterializerSettings;
import akka.stream.javadsl.FileIO;
import akka.stream.javadsl.Sink;
import akka.stream.javadsl.Source;
import akka.util.ByteString;
import org.junit.Ignore;
import org.junit.Test;
import scala.concurrent.ExecutionContextExecutor;
import scala.concurrent.duration.FiniteDuration;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.StreamSupport;
public class BasicDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testExtract() {
//#extract
final Route route = extract(
ctx -> ctx.getRequest().getUri().toString().length(),
len -> complete("The length of the request URI is " + len)
);
// tests:
testRoute(route).run(HttpRequest.GET("/abcdef"))
.assertEntity("The length of the request URI is 25");
//#extract
}
@Test
public void testExtractLog() {
//#extractLog
final Route route = extractLog(log -> {
log.debug("I'm logging things in much detail..!");
return complete("It's amazing!");
});
// tests:
testRoute(route).run(HttpRequest.GET("/abcdef"))
.assertEntity("It's amazing!");
//#extractLog
}
@Test
public void testWithMaterializer() {
//#withMaterializer
final ActorMaterializerSettings settings = ActorMaterializerSettings.create(system());
final ActorMaterializer special = ActorMaterializer.create(settings, system(), "special");
final Route sample = path("sample", () ->
extractMaterializer(mat ->
onSuccess(() ->
// explicitly use the materializer:
Source.single("Materialized by " + mat.hashCode() + "!")
.runWith(Sink.head(), mat), this::complete
)
)
);
final Route route = route(
pathPrefix("special", () ->
withMaterializer(special, () -> sample) // `special` materializer will be used
),
sample // default materializer will be used
);
// tests:
testRoute(route).run(HttpRequest.GET("/sample"))
.assertEntity("Materialized by " + materializer().hashCode()+ "!");
testRoute(route).run(HttpRequest.GET("/special/sample"))
.assertEntity("Materialized by " + special.hashCode()+ "!");
//#withMaterializer
}
@Test
public void testExtractMaterializer() {
//#extractMaterializer
final Route route = path("sample", () ->
extractMaterializer(mat ->
onSuccess(() ->
// explicitly use the materializer:
Source.single("Materialized by " + mat.hashCode() + "!")
.runWith(Sink.head(), mat), this::complete
)
)
); // default materializer will be used
testRoute(route).run(HttpRequest.GET("/sample"))
.assertEntity("Materialized by " + materializer().hashCode()+ "!");
//#extractMaterializer
}
@Test
public void testWithExecutionContext() {
//#withExecutionContext
final ExecutionContextExecutor special =
ExecutionContexts.fromExecutor(Executors.newFixedThreadPool(1));
final Route sample = path("sample", () ->
extractExecutionContext(executor ->
onSuccess(() ->
CompletableFuture.supplyAsync(() ->
"Run on " + executor.hashCode() + "!", executor
), this::complete
)
)
);
final Route route = route(
pathPrefix("special", () ->
// `special` execution context will be used
withExecutionContext(special, () -> sample)
),
sample // default execution context will be used
);
// tests:
testRoute(route).run(HttpRequest.GET("/sample"))
.assertEntity("Run on " + system().dispatcher().hashCode() + "!");
testRoute(route).run(HttpRequest.GET("/special/sample"))
.assertEntity("Run on " + special.hashCode() + "!");
//#withExecutionContext
}
@Test
public void testExtractExecutionContext() {
//#extractExecutionContext
final Route route = path("sample", () ->
extractExecutionContext(executor ->
onSuccess(() ->
CompletableFuture.supplyAsync(
// uses the `executor` ExecutionContext
() -> "Run on " + executor.hashCode() + "!", executor
), str -> complete(str)
)
)
);
//tests:
testRoute(route).run(HttpRequest.GET("/sample"))
.assertEntity("Run on " + system().dispatcher().hashCode() + "!");
//#extractExecutionContext
}
@Test
public void testWithLog() {
//#withLog
final LoggingAdapter special = Logging.getLogger(system(), "SpecialRoutes");
final Route sample = path("sample", () ->
extractLog(log -> {
final String msg = "Logging using " + log + "!";
log.debug(msg);
return complete(msg);
}
)
);
final Route route = route(
pathPrefix("special", () ->
withLog(special, () -> sample)
),
sample
);
// tests:
testRoute(route).run(HttpRequest.GET("/sample"))
.assertEntity("Logging using " + system().log() + "!");
testRoute(route).run(HttpRequest.GET("/special/sample"))
.assertEntity("Logging using " + special + "!");
//#withLog
}
@Ignore("Ignore compile-only test")
@Test
public void testWithSettings() {
//#withSettings
final RoutingSettings special =
RoutingSettings
.create(system().settings().config())
.withFileIODispatcher("special-io-dispatcher");
final Route sample = path("sample", () -> {
// internally uses the configured fileIODispatcher:
// ContentTypes.APPLICATION_JSON, source
final Source<ByteString, Object> source =
FileIO.fromPath(Paths.get("example.json"))
.mapMaterializedValue(completionStage -> (Object) completionStage);
return complete(
HttpResponse.create()
.withEntity(HttpEntities.create(ContentTypes.APPLICATION_JSON, source))
);
});
final Route route = get(() ->
route(
pathPrefix("special", () ->
// `special` file-io-dispatcher will be used to read the file
withSettings(special, () -> sample)
),
sample // default file-io-dispatcher will be used to read the file
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/special/sample"))
.assertEntity("{}");
testRoute(route).run(HttpRequest.GET("/sample"))
.assertEntity("{}");
//#withSettings
}
@Test
public void testMapResponse() {
//#mapResponse
final Route route = mapResponse(
response -> response.withStatus(StatusCodes.BAD_GATEWAY),
() -> complete("abc")
);
// tests:
testRoute(route).run(HttpRequest.GET("/abcdef?ghi=12"))
.assertStatusCode(StatusCodes.BAD_GATEWAY);
//#mapResponse
}
@Test
public void testMapResponseAdvanced() {
//#mapResponse-advanced
class ApiRoute {
private final ActorSystem system;
private final LoggingAdapter log;
private final HttpEntity nullJsonEntity =
HttpEntities.create(ContentTypes.APPLICATION_JSON, "{}");
public ApiRoute(ActorSystem system) {
this.system = system;
this.log = Logging.getLogger(system, "ApiRoutes");
}
private HttpResponse nonSuccessToEmptyJsonEntity(HttpResponse response) {
if (response.status().isSuccess()) {
return response;
} else {
log.warning(
"Dropping response entity since response status code was: " + response.status());
return response.withEntity((ResponseEntity) nullJsonEntity);
}
}
/** Wrapper for all of our JSON API routes */
private Route apiRoute(Supplier<Route> innerRoutes) {
return mapResponse(this::nonSuccessToEmptyJsonEntity, innerRoutes);
}
}
final ApiRoute api = new ApiRoute(system());
final Route route = api.apiRoute(() ->
get(() -> complete(StatusCodes.INTERNAL_SERVER_ERROR))
);
// tests:
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("{}");
//#mapResponse-advanced
}
@Test
public void testMapRouteResult() {
//#mapRouteResult
// this directive is a joke, don't do that :-)
final Route route = mapRouteResult(r -> {
if (r instanceof Complete) {
final HttpResponse response = ((Complete) r).getResponse();
return RouteResults.complete(response.withStatus(200));
} else {
return r;
}
}, () -> complete(StatusCodes.ACCEPTED));
// tests:
testRoute(route).run(HttpRequest.GET("/"))
.assertStatusCode(StatusCodes.OK);
//#mapRouteResult
}
@Test
public void testMapRouteResultFuture() {
//#mapRouteResultFuture
final Route route = mapRouteResultFuture(cr ->
cr.exceptionally(t -> {
if (t instanceof IllegalArgumentException) {
return RouteResults.complete(
HttpResponse.create().withStatus(StatusCodes.INTERNAL_SERVER_ERROR));
} else {
return null;
}
}).thenApply(rr -> {
if (rr instanceof Complete) {
final HttpResponse res = ((Complete) rr).getResponse();
return RouteResults.complete(
res.addHeader(Server.create(ProductVersion.create("MyServer", "1.0"))));
} else {
return rr;
}
}), () -> complete("Hello world!"));
// tests:
testRoute(route).run(HttpRequest.GET("/"))
.assertStatusCode(StatusCodes.OK)
.assertHeaderExists(Server.create(ProductVersion.create("MyServer", "1.0")));
//#mapRouteResultFuture
}
@Test
public void testMapResponseEntity() {
//#mapResponseEntity
final Function<ResponseEntity, ResponseEntity> prefixEntity = entity -> {
if (entity instanceof HttpEntity.Strict) {
final HttpEntity.Strict strict = (HttpEntity.Strict) entity;
return HttpEntities.create(
strict.getContentType(),
ByteString.fromString("test").concat(strict.getData()));
} else {
throw new IllegalStateException("Unexpected entity type");
}
};
final Route route = mapResponseEntity(prefixEntity, () -> complete("abc"));
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("testabc");
//#mapResponseEntity
}
@Test
public void testMapResponseHeaders() {
//#mapResponseHeaders
// adds all request headers to the response
final Route echoRequestHeaders = extract(
ctx -> ctx.getRequest().getHeaders(),
headers -> respondWithHeaders(headers, () -> complete("test"))
);
final Route route = mapResponseHeaders(headers -> {
headers.removeIf(header -> header.lowercaseName().equals("id"));
return headers;
}, () -> echoRequestHeaders);
// tests:
testRoute(route).run(HttpRequest.GET("/").addHeaders(
Arrays.asList(RawHeader.create("id", "12345"),RawHeader.create("id2", "67890"))))
.assertHeaderKindNotExists("id")
.assertHeaderExists("id2", "67890");
//#mapResponseHeaders
}
@Ignore("Not implemented yet")
@Test
public void testMapInnerRoute() {
//#mapInnerRoute
// TODO: implement mapInnerRoute
//#mapInnerRoute
}
@Test
public void testMapRejections() {
//#mapRejections
// ignore any rejections and replace them by AuthorizationFailedRejection
final Route route = mapRejections(
rejections -> Collections.singletonList((Rejection) Rejections.authorizationFailed()),
() -> path("abc", () -> complete("abc"))
);
// tests:
runRouteUnSealed(route, HttpRequest.GET("/"))
.assertRejections(Rejections.authorizationFailed());
testRoute(route).run(HttpRequest.GET("/abc"))
.assertStatusCode(StatusCodes.OK);
//#mapRejections
}
@Test
public void testRecoverRejections() {
//#recoverRejections
final Function<Optional<ProvidedCredentials>, Optional<Object>> neverAuth =
creds -> Optional.empty();
final Function<Optional<ProvidedCredentials>, Optional<Object>> alwaysAuth =
creds -> Optional.of("id");
final Route originalRoute = pathPrefix("auth", () ->
route(
path("never", () ->
authenticateBasic("my-realm", neverAuth, obj -> complete("Welcome to the bat-cave!"))
),
path("always", () ->
authenticateBasic("my-realm", alwaysAuth, obj -> complete("Welcome to the secret place!"))
)
)
);
final Function<Iterable<Rejection>, Boolean> existsAuthenticationFailedRejection =
rejections ->
StreamSupport.stream(rejections.spliterator(), false)
.anyMatch(r -> r instanceof AuthenticationFailedRejection);
final Route route = recoverRejections(rejections -> {
if (existsAuthenticationFailedRejection.apply(rejections)) {
return RouteResults.complete(
HttpResponse.create().withEntity("Nothing to see here, move along."));
} else if (!rejections.iterator().hasNext()) { // see "Empty Rejections" for more details
return RouteResults.complete(
HttpResponse.create().withStatus(StatusCodes.NOT_FOUND)
.withEntity("Literally nothing to see here."));
} else {
return RouteResults.rejected(rejections);
}
}, () -> originalRoute);
// tests:
testRoute(route).run(HttpRequest.GET("/auth/never"))
.assertStatusCode(StatusCodes.OK)
.assertEntity("Nothing to see here, move along.");
testRoute(route).run(HttpRequest.GET("/auth/always"))
.assertStatusCode(StatusCodes.OK)
.assertEntity("Welcome to the secret place!");
testRoute(route).run(HttpRequest.GET("/auth/does_not_exist"))
.assertStatusCode(StatusCodes.NOT_FOUND)
.assertEntity("Literally nothing to see here.");
//#recoverRejections
}
@Test
public void testRecoverRejectionsWith() {
//#recoverRejectionsWith
final Function<Optional<ProvidedCredentials>, Optional<Object>> neverAuth =
creds -> Optional.empty();
final Route originalRoute = pathPrefix("auth", () ->
path("never", () ->
authenticateBasic("my-realm", neverAuth, obj -> complete("Welcome to the bat-cave!"))
)
);
final Function<Iterable<Rejection>, Boolean> existsAuthenticationFailedRejection =
rejections ->
StreamSupport.stream(rejections.spliterator(), false)
.anyMatch(r -> r instanceof AuthenticationFailedRejection);
final Route route = recoverRejectionsWith(
rejections -> CompletableFuture.supplyAsync(() -> {
if (existsAuthenticationFailedRejection.apply(rejections)) {
return RouteResults.complete(
HttpResponse.create().withEntity("Nothing to see here, move along."));
} else {
return RouteResults.rejected(rejections);
}
}), () -> originalRoute);
// tests:
testRoute(route).run(HttpRequest.GET("/auth/never"))
.assertStatusCode(StatusCodes.OK)
.assertEntity("Nothing to see here, move along.");
//#recoverRejectionsWith
}
@Test
public void testMapRequest() {
//#mapRequest
final Route route = mapRequest(req ->
req.withMethod(HttpMethods.POST), () ->
extractRequest(req -> complete("The request method was " + req.method().name()))
);
// tests:
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("The request method was POST");
//#mapRequest
}
@Test
public void testMapRequestContext() {
//#mapRequestContext
final Route route = mapRequestContext(ctx ->
ctx.withRequest(HttpRequest.create().withMethod(HttpMethods.POST)), () ->
extractRequest(req -> complete(req.method().value()))
);
// tests:
testRoute(route).run(HttpRequest.GET("/abc/def/ghi"))
.assertEntity("POST");
//#mapRequestContext
}
@Test
public void testMapRouteResult0() {
//#mapRouteResult
final Route route = mapRouteResult(rr -> {
final Iterable<Rejection> rejections = Collections.singletonList(Rejections.authorizationFailed());
return RouteResults.rejected(rejections);
}, () -> complete("abc"));
// tests:
runRouteUnSealed(route, HttpRequest.GET("/"))
.assertRejections(Rejections.authorizationFailed());
//#mapRouteResult
}
public static final class MyCustomRejection implements CustomRejection {}
@Test
public void testMapRouteResultPF() {
//#mapRouteResultPF
final Route route = mapRouteResultPF(
new PFBuilder<RouteResult, RouteResult>()
.match(Rejected.class, rejected -> {
final Iterable<Rejection> rejections =
Collections.singletonList(Rejections.authorizationFailed());
return RouteResults.rejected(rejections);
}).build(), () -> reject(new MyCustomRejection()));
// tests:
runRouteUnSealed(route, HttpRequest.GET("/"))
.assertRejections(Rejections.authorizationFailed());
//#mapRouteResultPF
}
@Test
public void testMapRouteResultWithPF() {
//#mapRouteResultWithPF
final Route route = mapRouteResultWithPF(
new PFBuilder<RouteResult, CompletionStage<RouteResult>>()
.match(Rejected.class, rejected -> CompletableFuture.supplyAsync(() -> {
final Iterable<Rejection> rejections =
Collections.singletonList(Rejections.authorizationFailed());
return RouteResults.rejected(rejections);
})
).build(), () -> reject(new MyCustomRejection()));
// tests:
runRouteUnSealed(route, HttpRequest.GET("/"))
.assertRejections(Rejections.authorizationFailed());
//#mapRouteResultWithPF
}
@Test
public void testMapRouteResultWith() {
//#mapRouteResultWith
final Route route = mapRouteResultWith(rr -> CompletableFuture.supplyAsync(() -> {
if (rr instanceof Rejected) {
final Iterable<Rejection> rejections =
Collections.singletonList(Rejections.authorizationFailed());
return RouteResults.rejected(rejections);
} else {
return rr;
}
}), () -> reject(new MyCustomRejection()));
// tests:
runRouteUnSealed(route, HttpRequest.GET("/"))
.assertRejections(Rejections.authorizationFailed());
//#mapRouteResultWith
}
@Test
public void testPass() {
//#pass
final Route route = pass(() -> complete("abc"));
// tests:
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("abc");
//#pass
}
private Route providePrefixedStringRoute(String value) {
return provide("prefix:" + value, this::complete);
}
@Test
public void testProvide() {
//#provide
final Route route = providePrefixedStringRoute("test");
// tests:
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("prefix:test");
//#provide
}
@Ignore("Test failed")
@Test
public void testCancelRejections() {
//#cancelRejections
final Predicate<Rejection> isMethodRejection = p -> p instanceof MethodRejection;
final Route route = cancelRejections(
isMethodRejection, () -> post(() -> complete("Result"))
);
// tests:
runRouteUnSealed(route, HttpRequest.GET("/"))
.assertRejections();
//#cancelRejections
}
@Ignore("Test failed")
@Test
public void testCancelRejection() {
//#cancelRejection
final Route route = cancelRejection(Rejections.method(HttpMethods.POST), () ->
post(() -> complete("Result"))
);
// tests:
runRouteUnSealed(route, HttpRequest.GET("/"))
.assertRejections();
//#cancelRejection
}
@Test
public void testExtractRequest() {
//#extractRequest
final Route route = extractRequest(request ->
complete("Request method is " + request.method().name() +
" and content-type is " + request.entity().getContentType())
);
// tests:
testRoute(route).run(HttpRequest.POST("/").withEntity("text"))
.assertEntity("Request method is POST and content-type is text/plain; charset=UTF-8");
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("Request method is GET and content-type is none/none");
//#extractRequest
}
@Test
public void testExtractSettings() {
//#extractSettings
final Route route = extractSettings(settings ->
complete("RoutingSettings.renderVanityFooter = " + settings.getRenderVanityFooter())
);
// tests:
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("RoutingSettings.renderVanityFooter = true");
//#extractSettings
}
@Test
public void testMapSettings() {
//#mapSettings
final Route route = mapSettings(settings ->
settings.withFileGetConditional(false), () ->
extractSettings(settings ->
complete("RoutingSettings.fileGetConditional = " + settings.getFileGetConditional())
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("RoutingSettings.fileGetConditional = false");
//#mapSettings
}
@Test
public void testExtractRequestContext() {
//#extractRequestContext
final Route route = extractRequestContext(ctx -> {
ctx.getLog().debug("Using access to additional context availablethings, like the logger.");
final HttpRequest request = ctx.getRequest();
return complete("Request method is " + request.method().name() +
" and content-type is " + request.entity().getContentType());
});
// tests:
testRoute(route).run(HttpRequest.POST("/").withEntity("text"))
.assertEntity("Request method is POST and content-type is text/plain; charset=UTF-8");
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("Request method is GET and content-type is none/none");
//#extractRequestContext
}
@Test
public void testExtractUri() {
//#extractUri
final Route route = extractUri(uri ->
complete("Full URI: " + uri)
);
// tests:
// tests are executed with the host assumed to be "example.com"
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("Full URI: http://example.com/");
testRoute(route).run(HttpRequest.GET("/test"))
.assertEntity("Full URI: http://example.com/test");
//#extractUri
}
@Test
public void testMapUnmatchedPath() {
//#mapUnmatchedPath
final Function<String, String> ignore456 = path -> {
int slashPos = path.indexOf("/");
if (slashPos != -1) {
String head = path.substring(0, slashPos);
String tail = path.substring(slashPos);
if (head.length() <= 3) {
return tail;
} else {
return path.substring(3);
}
} else {
return path;
}
};
final Route route = pathPrefix("123", () ->
mapUnmatchedPath(ignore456, () ->
path("abc", () ->
complete("Content")
)
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/123/abc"))
.assertEntity("Content");
testRoute(route).run(HttpRequest.GET("/123456/abc"))
.assertEntity("Content");
//#mapUnmatchedPath
}
@Test
public void testExtractUnmatchedPath() {
//#extractUnmatchedPath
final Route route = pathPrefix("abc", () ->
extractUnmatchedPath(remaining ->
complete("Unmatched: '" + remaining + "'")
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/abc"))
.assertEntity("Unmatched: ''");
testRoute(route).run(HttpRequest.GET("/abc/456"))
.assertEntity("Unmatched: '/456'");
//#extractUnmatchedPath
}
@Test
public void testExtractRequestEntity() {
//#extractRequestEntity
final Route route = extractRequestEntity(entity ->
complete("Request entity content-type is " + entity.getContentType())
);
// tests:
testRoute(route).run(
HttpRequest.POST("/abc")
.withEntity(HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8, "req"))
).assertEntity("Request entity content-type is text/plain; charset=UTF-8");
//#extractRequestEntity
}
@Test
public void testExtractDataBytes() {
//#extractDataBytes
final Route route = extractDataBytes(data -> {
final CompletionStage<Integer> sum = data.runFold(0, (acc, i) ->
acc + Integer.valueOf(i.utf8String()), materializer());
return onSuccess(() -> sum, s ->
complete(HttpResponse.create().withEntity(HttpEntities.create(s.toString()))));
});
// tests:
final Iterator iterator = Arrays.asList(
ByteString.fromString("1"),
ByteString.fromString("2"),
ByteString.fromString("3")).iterator();
final Source<ByteString, NotUsed> dataBytes = Source.fromIterator(() -> iterator);
testRoute(route).run(
HttpRequest.POST("abc")
.withEntity(HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8, dataBytes))
).assertEntity("6");
//#extractDataBytes
}
@Test
public void testExtractStrictEntity() {
//#extractStrictEntity
final FiniteDuration timeout = FiniteDuration.create(3, TimeUnit.SECONDS);
final Route route = extractStrictEntity(timeout, strict ->
complete(strict.getData().utf8String())
);
// tests:
final Iterator iterator = Arrays.asList(
ByteString.fromString("1"),
ByteString.fromString("2"),
ByteString.fromString("3")).iterator();
final Source<ByteString, NotUsed> dataBytes = Source.fromIterator(() -> iterator);
testRoute(route).run(
HttpRequest.POST("/")
.withEntity(HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8, dataBytes))
).assertEntity("123");
//#extractStrictEntity
}
@Test
public void testToStrictEntity() {
//#toStrictEntity
final FiniteDuration timeout = FiniteDuration.create(3, TimeUnit.SECONDS);
final Route route = toStrictEntity(timeout, () ->
extractRequest(req -> {
if (req.entity() instanceof HttpEntity.Strict) {
final HttpEntity.Strict strict = (HttpEntity.Strict)req.entity();
return complete("Request entity is strict, data=" + strict.getData().utf8String());
} else {
return complete("Ooops, request entity is not strict!");
}
})
);
// tests:
final Iterator iterator = Arrays.asList(
ByteString.fromString("1"),
ByteString.fromString("2"),
ByteString.fromString("3")).iterator();
final Source<ByteString, NotUsed> dataBytes = Source.fromIterator(() -> iterator);
testRoute(route).run(
HttpRequest.POST("/")
.withEntity(HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8, dataBytes))
).assertEntity("Request entity is strict, data=123");
//#toStrictEntity
}
@Test
public void testExtractActorSystem() {
//#extractActorSystem
final Route route = extractActorSystem(actorSystem ->
complete("Actor System extracted, hash=" + actorSystem.hashCode())
);
// tests:
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("Actor System extracted, hash=" + system().hashCode());
//#extractActorSystem
}
}

View file

@ -1,156 +0,0 @@
/*
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.headers.AcceptEncoding;
import akka.http.javadsl.model.headers.ContentEncoding;
import akka.http.javadsl.model.headers.HttpEncodings;
import akka.http.javadsl.coding.Coder;
import akka.http.javadsl.server.Rejections;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.util.ByteString;
import org.junit.Test;
import java.util.Collections;
import static akka.http.javadsl.unmarshalling.Unmarshaller.entityToString;
public class CodingDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testResponseEncodingAccepted() {
//#responseEncodingAccepted
final Route route = responseEncodingAccepted(HttpEncodings.GZIP, () ->
complete("content")
);
// tests:
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("content");
runRouteUnSealed(route,
HttpRequest.GET("/")
.addHeader(AcceptEncoding.create(HttpEncodings.DEFLATE)))
.assertRejections(Rejections.unacceptedResponseEncoding(HttpEncodings.GZIP));
//#responseEncodingAccepted
}
@Test
public void testEncodeResponse() {
//#encodeResponse
final Route route = encodeResponse(() -> complete("content"));
// tests:
testRoute(route).run(
HttpRequest.GET("/")
.addHeader(AcceptEncoding.create(HttpEncodings.GZIP))
.addHeader(AcceptEncoding.create(HttpEncodings.DEFLATE))
).assertHeaderExists(ContentEncoding.create(HttpEncodings.GZIP));
testRoute(route).run(
HttpRequest.GET("/")
.addHeader(AcceptEncoding.create(HttpEncodings.DEFLATE))
).assertHeaderExists(ContentEncoding.create(HttpEncodings.DEFLATE));
// This case failed!
// testRoute(route).run(
// HttpRequest.GET("/")
// .addHeader(AcceptEncoding.create(HttpEncodings.IDENTITY))
// ).assertHeaderExists(ContentEncoding.create(HttpEncodings.IDENTITY));
//#encodeResponse
}
@Test
public void testEncodeResponseWith() {
//#encodeResponseWith
final Route route = encodeResponseWith(
Collections.singletonList(Coder.Gzip),
() -> complete("content")
);
// tests:
testRoute(route).run(HttpRequest.GET("/"))
.assertHeaderExists(ContentEncoding.create(HttpEncodings.GZIP));
testRoute(route).run(
HttpRequest.GET("/")
.addHeader(AcceptEncoding.create(HttpEncodings.GZIP))
.addHeader(AcceptEncoding.create(HttpEncodings.DEFLATE))
).assertHeaderExists(ContentEncoding.create(HttpEncodings.GZIP));
runRouteUnSealed(route,
HttpRequest.GET("/")
.addHeader(AcceptEncoding.create(HttpEncodings.DEFLATE))
).assertRejections(Rejections.unacceptedResponseEncoding(HttpEncodings.GZIP));
runRouteUnSealed(route,
HttpRequest.GET("/")
.addHeader(AcceptEncoding.create(HttpEncodings.IDENTITY))
).assertRejections(Rejections.unacceptedResponseEncoding(HttpEncodings.GZIP));
//#encodeResponseWith
}
@Test
public void testDecodeRequest() {
//#decodeRequest
final ByteString helloGzipped = Coder.Gzip.encode(ByteString.fromString("Hello"));
final ByteString helloDeflated = Coder.Deflate.encode(ByteString.fromString("Hello"));
final Route route = decodeRequest(() ->
entity(entityToString(), content ->
complete("Request content: '" + content + "'")
)
);
// tests:
testRoute(route).run(
HttpRequest.POST("/").withEntity(helloGzipped)
.addHeader(ContentEncoding.create(HttpEncodings.GZIP)))
.assertEntity("Request content: 'Hello'");
testRoute(route).run(
HttpRequest.POST("/").withEntity(helloDeflated)
.addHeader(ContentEncoding.create(HttpEncodings.DEFLATE)))
.assertEntity("Request content: 'Hello'");
testRoute(route).run(
HttpRequest.POST("/").withEntity("hello uncompressed")
.addHeader(ContentEncoding.create(HttpEncodings.IDENTITY)))
.assertEntity( "Request content: 'hello uncompressed'");
//#decodeRequest
}
@Test
public void testDecodeRequestWith() {
//#decodeRequestWith
final ByteString helloGzipped = Coder.Gzip.encode(ByteString.fromString("Hello"));
final ByteString helloDeflated = Coder.Deflate.encode(ByteString.fromString("Hello"));
final Route route = decodeRequestWith(Coder.Gzip, () ->
entity(entityToString(), content ->
complete("Request content: '" + content + "'")
)
);
// tests:
testRoute(route).run(
HttpRequest.POST("/").withEntity(helloGzipped)
.addHeader(ContentEncoding.create(HttpEncodings.GZIP)))
.assertEntity("Request content: 'Hello'");
runRouteUnSealed(route,
HttpRequest.POST("/").withEntity(helloDeflated)
.addHeader(ContentEncoding.create(HttpEncodings.DEFLATE)))
.assertRejections(Rejections.unsupportedRequestEncoding(HttpEncodings.GZIP));
runRouteUnSealed(route,
HttpRequest.POST("/").withEntity("hello")
.addHeader(ContentEncoding.create(HttpEncodings.IDENTITY)))
.assertRejections(Rejections.unsupportedRequestEncoding(HttpEncodings.GZIP));
//#decodeRequestWith
}
}

View file

@ -1,102 +0,0 @@
/*
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import akka.http.javadsl.model.HttpHeader;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.headers.Cookie;
import akka.http.javadsl.model.headers.HttpCookie;
import akka.http.javadsl.model.headers.SetCookie;
import akka.http.javadsl.server.Rejections;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.scaladsl.model.DateTime;
import org.junit.Test;
import java.util.Optional;
import java.util.OptionalLong;
public class CookieDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testCookie() {
//#cookie
final Route route = cookie("userName", nameCookie ->
complete("The logged in user is '" + nameCookie.value() + "'")
);
// tests:
testRoute(route).run(HttpRequest.GET("/").addHeader(Cookie.create("userName", "paul")))
.assertEntity("The logged in user is 'paul'");
// missing cookie
runRouteUnSealed(route, HttpRequest.GET("/"))
.assertRejections(Rejections.missingCookie("userName"));
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("Request is missing required cookie 'userName'");
//#cookie
}
@Test
public void testOptionalCookie() {
//#optionalCookie
final Route route = optionalCookie("userName", optNameCookie -> {
if (optNameCookie.isPresent()) {
return complete("The logged in user is '" + optNameCookie.get().value() + "'");
} else {
return complete("No user logged in");
}
}
);
// tests:
testRoute(route).run(HttpRequest.GET("/").addHeader(Cookie.create("userName", "paul")))
.assertEntity("The logged in user is 'paul'");
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("No user logged in");
//#optionalCookie
}
@Test
public void testDeleteCookie() {
//#deleteCookie
final Route route = deleteCookie("userName", () ->
complete("The user was logged out")
);
// tests:
final HttpHeader expected = SetCookie.create(
HttpCookie.create(
"userName",
"deleted",
Optional.of(DateTime.MinValue()),
OptionalLong.empty(),
Optional.empty(),
Optional.empty(),
false,
false,
Optional.empty()));
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("The user was logged out")
.assertHeaderExists(expected);
//#deleteCookie
}
@Test
public void testSetCookie() {
//#setCookie
final Route route = setCookie(HttpCookie.create("userName", "paul"), () ->
complete("The user was logged in")
);
// tests:
final HttpHeader expected = SetCookie.create(HttpCookie.create("userName", "paul"));
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("The user was logged in")
.assertHeaderExists(expected);
//#setCookie
}
}

View file

@ -1,126 +0,0 @@
/*
* 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);
}
}

View file

@ -1,77 +0,0 @@
/*
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import akka.NotUsed;
import akka.actor.ActorSystem;
import akka.event.LoggingAdapter;
import akka.event.NoLogging;
import akka.http.javadsl.ConnectHttp;
import akka.http.javadsl.Http;
import akka.http.javadsl.ServerBinding;
import akka.http.javadsl.model.*;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.settings.ParserSettings;
import akka.http.javadsl.settings.ServerSettings;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.stream.Materializer;
import akka.stream.javadsl.Flow;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import static akka.http.javadsl.model.HttpProtocols.HTTP_1_0;
import static akka.http.javadsl.model.RequestEntityAcceptances.Expected;
public class CustomHttpMethodExamplesTest extends JUnitRouteTest {
@Test
public void testComposition() throws InterruptedException, ExecutionException {
ActorSystem system = system();
Materializer materializer = materializer();
LoggingAdapter loggingAdapter = NoLogging.getInstance();
int port = 9090;
String host = "127.0.0.1";
//#customHttpMethod
HttpMethod BOLT =
HttpMethods.createCustom("BOLT", false, true, Expected);
final ParserSettings parserSettings =
ParserSettings.create(system).withCustomMethods(BOLT);
final ServerSettings serverSettings =
ServerSettings.create(system).withParserSettings(parserSettings);
final Route routes = route(
extractMethod( method ->
complete( "This is a " + method.name() + " request.")
)
);
final Flow<HttpRequest, HttpResponse, NotUsed> handler = routes.flow(system, materializer);
final Http http = Http.get(system);
final CompletionStage<ServerBinding> binding =
http.bindAndHandle(
handler,
ConnectHttp.toHost(host, port),
serverSettings,
loggingAdapter,
materializer);
HttpRequest request = HttpRequest.create()
.withUri("http://" + host + ":" + Integer.toString(port))
.withMethod(BOLT)
.withProtocol(HTTP_1_0);
CompletionStage<HttpResponse> response = http.singleRequest(request, materializer);
//#customHttpMethod
assertEquals(StatusCodes.OK, response.toCompletableFuture().get().status());
assertEquals(
"This is a BOLT request.",
response.toCompletableFuture().get().entity().toStrict(3000, materializer).toCompletableFuture().get().getData().utf8String()
);
}
}

View file

@ -1,181 +0,0 @@
/*
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import java.util.Arrays;
import java.util.regex.Pattern;
import org.junit.Test;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.HttpResponse;
import akka.http.javadsl.model.StatusCodes;
import akka.http.javadsl.model.headers.Host;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.server.RequestContext;
import akka.http.javadsl.testkit.JUnitRouteTest;
import java.util.function.Function;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import akka.http.javadsl.model.Uri;
import akka.http.javadsl.model.headers.Location;
import akka.http.javadsl.server.directives.DebuggingDirectives;
import akka.http.javadsl.server.directives.RouteDirectives;
import akka.event.Logging;
import akka.event.Logging.LogLevel;
import akka.http.javadsl.server.directives.LogEntry;
import java.util.List;
import akka.http.javadsl.server.Rejection;
import static akka.event.Logging.InfoLevel;
import java.util.stream.Collectors;
import java.util.Optional;
public class DebuggingDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testLogRequest() {
//#logRequest
// logs request with "get-user"
final Route routeBasicLogRequest = get(() ->
logRequest("get-user", () -> complete("logged")));
// logs request with "get-user" as Info
final Route routeBasicLogRequestAsInfo = get(() ->
logRequest("get-user", InfoLevel(), () -> complete("logged")));
// logs just the request method at info level
Function<HttpRequest, LogEntry> requestMethodAsInfo = (request) ->
LogEntry.create(request.method().name(), InfoLevel());
final Route routeUsingFunction = get(() ->
logRequest(requestMethodAsInfo, () -> complete("logged")));
// tests:
testRoute(routeBasicLogRequest).run(HttpRequest.GET("/"))
.assertEntity("logged");
//#logRequest
}
@Test
public void testLogRequestResult() {
//#logRequestResult
// using logRequestResult
// handle request to optionally generate a log entry
BiFunction<HttpRequest, HttpResponse, Optional<LogEntry>> requestMethodAsInfo =
(request, response) ->
(response.status().isSuccess()) ?
Optional.of(
LogEntry.create(
request.method().name() + ":" + response.status().intValue(),
InfoLevel()))
: Optional.empty(); // not a successful response
// handle rejections to optionally generate a log entry
BiFunction<HttpRequest, List<Rejection>, Optional<LogEntry>> rejectionsAsInfo =
(request, rejections) ->
(!rejections.isEmpty()) ?
Optional.of(
LogEntry.create(
rejections
.stream()
.map(Rejection::toString)
.collect(Collectors.joining(", ")),
InfoLevel()))
: Optional.empty(); // no rejections
final Route route = get(() -> logRequestResultOptional(
requestMethodAsInfo,
rejectionsAsInfo,
() -> complete("logged")));
// tests:
testRoute(route).run(HttpRequest.GET("/")).assertEntity("logged");
//#logRequestResult
}
@Test
public void testLogResult() {
//#logResult
// logs result with "get-user"
final Route routeBasicLogResult = get(() ->
logResult("get-user", () -> complete("logged")));
// logs result with "get-user" as Info
final Route routeBasicLogResultAsInfo = get(() ->
logResult("get-user", InfoLevel(), () -> complete("logged")));
// logs the result and the rejections as LogEntry
Function<HttpResponse, LogEntry> showSuccessAsInfo = (response) ->
LogEntry.create(String.format("Response code '%d'", response.status().intValue()),
InfoLevel());
Function<List<Rejection>, LogEntry> showRejectionAsInfo = (rejections) ->
LogEntry.create(
rejections
.stream()
.map(rejection -> rejection.toString())
.collect(Collectors.joining(", ")),
InfoLevel());
final Route routeUsingFunction = get(() ->
logResult(showSuccessAsInfo, showRejectionAsInfo, () -> complete("logged")));
// tests:
testRoute(routeBasicLogResult).run(HttpRequest.GET("/"))
.assertEntity("logged");
//#logResult
}
@Test
public void testLogRequestResultWithResponseTime() {
//#logRequestResultWithResponseTime
// using logRequestResultOptional for generating Response Time
// handle request to optionally generate a log entry
BiFunction<HttpRequest, HttpResponse, Optional<LogEntry>> requestMethodAsInfo =
(request, response) -> {
Long requestTime = System.nanoTime();
return printResponseTime(request, response, requestTime);
};
// handle rejections to optionally generate a log entry
BiFunction<HttpRequest, List<Rejection>, Optional<LogEntry>> rejectionsAsInfo =
(request, rejections) ->
(!rejections.isEmpty()) ?
Optional.of(
LogEntry.create(
rejections
.stream()
.map(Rejection::toString)
.collect(Collectors.joining(", ")),
InfoLevel()))
: Optional.empty(); // no rejections
final Route route = get(() -> logRequestResultOptional(
requestMethodAsInfo,
rejectionsAsInfo,
() -> complete("logged")));
// tests:
testRoute(route).run(HttpRequest.GET("/")).assertEntity("logged");
//#logRequestResult
}
// A function for the logging of Time
public static Optional<LogEntry> printResponseTime(HttpRequest request, HttpResponse response, Long requestTime) {
if (response.status().isSuccess()) {
Long elapsedTime = (requestTime - System.nanoTime()) / 1000000;
return Optional.of(
LogEntry.create(
"Logged Request:" + request.method().name() + ":" + request.getUri() + ":" + response.status() + ":" + elapsedTime,
InfoLevel()));
} else {
return Optional.empty(); //not a successfull response
}
}
}

View file

@ -1,75 +0,0 @@
/*
* Copyright (C) 2016-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.server.ExceptionHandler;
import akka.http.javadsl.server.PathMatchers;
import akka.http.javadsl.server.RejectionHandler;
import akka.http.javadsl.server.Rejections;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.server.ValidationRejection;
import akka.http.javadsl.testkit.JUnitRouteTest;
import org.junit.Test;
import static akka.http.javadsl.server.PathMatchers.integerSegment;
public class ExecutionDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testHandleExceptions() {
//#handleExceptions
final ExceptionHandler divByZeroHandler = ExceptionHandler.newBuilder()
.match(ArithmeticException.class, x ->
complete(StatusCodes.BAD_REQUEST, "You've got your arithmetic wrong, fool!"))
.build();
final Route route =
path(PathMatchers.segment("divide").slash(integerSegment()).slash(integerSegment()), (a, b) ->
handleExceptions(divByZeroHandler, () -> complete("The result is " + (a / b)))
);
// tests:
testRoute(route).run(HttpRequest.GET("/divide/10/5"))
.assertEntity("The result is 2");
testRoute(route).run(HttpRequest.GET("/divide/10/0"))
.assertStatusCode(StatusCodes.BAD_REQUEST)
.assertEntity("You've got your arithmetic wrong, fool!");
//#handleExceptions
}
@Test
public void testHandleRejections() {
//#handleRejections
final RejectionHandler totallyMissingHandler = RejectionHandler.newBuilder()
.handleNotFound(complete(StatusCodes.NOT_FOUND, "Oh man, what you are looking for is long gone."))
.handle(ValidationRejection.class, r -> complete(StatusCodes.INTERNAL_SERVER_ERROR, r.message()))
.build();
final Route route = pathPrefix("handled", () ->
handleRejections(totallyMissingHandler, () ->
route(
path("existing", () -> complete("This path exists")),
path("boom", () -> reject(Rejections.validationRejection("This didn't work.")))
)
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/handled/existing"))
.assertEntity("This path exists");
// applies default handler
testRoute(route).run(HttpRequest.GET("/missing"))
.assertStatusCode(StatusCodes.NOT_FOUND)
.assertEntity("The requested resource could not be found.");
testRoute(route).run(HttpRequest.GET("/handled/missing"))
.assertStatusCode(StatusCodes.NOT_FOUND)
.assertEntity("Oh man, what you are looking for is long gone.");
testRoute(route).run(HttpRequest.GET("/handled/boom"))
.assertStatusCode(StatusCodes.INTERNAL_SERVER_ERROR)
.assertEntity("This didn't work.");
//#handleRejections
}
}

View file

@ -1,124 +0,0 @@
/*
* Copyright (C) 2016-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.server.PathMatchers;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.server.directives.DirectoryRenderer;
import akka.http.javadsl.testkit.JUnitRouteTest;
import org.junit.Ignore;
import org.junit.Test;
import scala.NotImplementedError;
import static akka.http.javadsl.server.PathMatchers.segment;
public class FileAndResourceDirectivesExamplesTest extends JUnitRouteTest {
@Ignore("Compile only test")
@Test
public void testGetFromFile() {
//#getFromFile
final Route route = path(PathMatchers.segment("logs").slash(segment()), name ->
getFromFile(name + ".log")
);
// tests:
testRoute(route).run(HttpRequest.GET("/logs/example"))
.assertEntity("example file contents");
//#getFromFile
}
@Ignore("Compile only test")
@Test
public void testGetFromResource() {
//#getFromResource
final Route route = path(PathMatchers.segment("logs").slash(segment()), name ->
getFromResource(name + ".log")
);
// tests:
testRoute(route).run(HttpRequest.GET("/logs/example"))
.assertEntity("example file contents");
//#getFromResource
}
@Ignore("Compile only test")
@Test
public void testListDirectoryContents() {
//#listDirectoryContents
final Route route = route(
path("tmp", () -> listDirectoryContents("/tmp")),
path("custom", () -> {
// implement your custom renderer here
final DirectoryRenderer renderer = renderVanityFooter -> {
throw new NotImplementedError();
};
return listDirectoryContents(renderer, "/tmp");
})
);
// tests:
testRoute(route).run(HttpRequest.GET("/logs/example"))
.assertEntity("example file contents");
//#listDirectoryContents
}
@Ignore("Compile only test")
@Test
public void testGetFromBrowseableDirectory() {
//#getFromBrowseableDirectory
final Route route = path("tmp", () ->
getFromBrowseableDirectory("/tmp")
);
// tests:
testRoute(route).run(HttpRequest.GET("/tmp"))
.assertStatusCode(StatusCodes.OK);
//#getFromBrowseableDirectory
}
@Ignore("Compile only test")
@Test
public void testGetFromBrowseableDirectories() {
//#getFromBrowseableDirectories
final Route route = path("tmp", () ->
getFromBrowseableDirectories("/main", "/backups")
);
// tests:
testRoute(route).run(HttpRequest.GET("/tmp"))
.assertStatusCode(StatusCodes.OK);
//#getFromBrowseableDirectories
}
@Ignore("Compile only test")
@Test
public void testGetFromDirectory() {
//#getFromDirectory
final Route route = pathPrefix("tmp", () ->
getFromDirectory("/tmp")
);
// tests:
testRoute(route).run(HttpRequest.GET("/tmp/example"))
.assertEntity("example file contents");
//#getFromDirectory
}
@Ignore("Compile only test")
@Test
public void testGetFromResourceDirectory() {
//#getFromResourceDirectory
final Route route = pathPrefix("examples", () ->
getFromResourceDirectory("/examples")
);
// tests:
testRoute(route).run(HttpRequest.GET("/examples/example-1"))
.assertEntity("example file contents");
//#getFromResourceDirectory
}
}

View file

@ -1,137 +0,0 @@
/**
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import akka.http.impl.engine.rendering.BodyPartRenderer;
import akka.http.javadsl.model.*;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.unmarshalling.Unmarshaller;
import akka.http.javadsl.server.directives.FileInfo;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.stream.javadsl.Framing;
import akka.stream.javadsl.Source;
import akka.util.ByteString;
import org.junit.Ignore;
import org.junit.Test;
import java.io.File;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletionStage;
import java.util.function.BiFunction;
public class FileUploadDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testUploadedFile() {
//#uploadedFile
// function (FileInfo, File) => Route to process the file metadata and file itself
BiFunction<FileInfo, File, Route> infoFileRoute =
(info, file) -> {
// do something with the file and file metadata ...
file.delete();
return complete(StatusCodes.OK);
};
final Route route = uploadedFile("csv", infoFileRoute);
Map<String, String> filenameMapping = new HashMap<>();
filenameMapping.put("filename", "data.csv");
akka.http.javadsl.model.Multipart.FormData multipartForm =
Multiparts.createStrictFormDataFromParts(Multiparts.createFormDataBodyPartStrict("csv",
HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8,
"1,5,7\n11,13,17"), filenameMapping));
// test:
testRoute(route).run(HttpRequest.POST("/")
.withEntity(
multipartForm.toEntity(HttpCharsets.UTF_8,
BodyPartRenderer
.randomBoundaryWithDefaults())))
.assertStatusCode(StatusCodes.OK);
//#
}
@Test
public void testFileUpload() {
//#fileUpload
final Route route = extractRequestContext(ctx -> {
// function (FileInfo, Source<ByteString,Object>) => Route to process the file contents
BiFunction<FileInfo, Source<ByteString, Object>, Route> processUploadedFile =
(metadata, byteSource) -> {
CompletionStage<Integer> sumF = byteSource.via(Framing.delimiter(
ByteString.fromString("\n"), 1024))
.mapConcat(bs -> Arrays.asList(bs.utf8String().split(",")))
.map(s -> Integer.parseInt(s))
.runFold(0, (acc, n) -> acc + n, ctx.getMaterializer());
return onSuccess(() -> sumF, sum -> complete("Sum: " + sum));
};
return fileUpload("csv", processUploadedFile);
});
Map<String, String> filenameMapping = new HashMap<>();
filenameMapping.put("filename", "primes.csv");
akka.http.javadsl.model.Multipart.FormData multipartForm =
Multiparts.createStrictFormDataFromParts(
Multiparts.createFormDataBodyPartStrict("csv",
HttpEntities.create(ContentTypes.TEXT_PLAIN_UTF8,
"2,3,5\n7,11,13,17,23\n29,31,37\n"), filenameMapping));
// test:
testRoute(route).run(HttpRequest.POST("/").withEntity(
multipartForm.toEntity(HttpCharsets.UTF_8, BodyPartRenderer.randomBoundaryWithDefaults())))
.assertStatusCode(StatusCodes.OK).assertEntityAs(Unmarshaller.entityToString(), "Sum: 178");
//#
}
@Ignore("compileOnly")
@Test
public void testFileProcessing() {
//#fileProcessing
final Route route = extractRequestContext(ctx -> {
// function (FileInfo, Source<ByteString,Object>) => Route to process the file contents
BiFunction<FileInfo, Source<ByteString, Object>, Route> processUploadedFile =
(metadata, byteSource) -> {
CompletionStage<Integer> sumF = byteSource.via(Framing.delimiter(
ByteString.fromString("\n"), 1024))
.mapConcat(bs -> Arrays.asList(bs.utf8String().split(",")))
.map(s -> Integer.parseInt(s))
.runFold(0, (acc, n) -> acc + n, ctx.getMaterializer());
return onSuccess(() -> sumF, sum -> complete("Sum: " + sum));
};
return fileUpload("csv", processUploadedFile);
});
Map<String, String> filenameMapping = new HashMap<>();
filenameMapping.put("filename", "primes.csv");
String prefix = "primes";
String suffix = ".csv";
File tempFile = null;
try {
tempFile = File.createTempFile(prefix, suffix);
tempFile.deleteOnExit();
Files.write(tempFile.toPath(), Arrays.asList("2,3,5", "7,11,13,17,23", "29,31,37"), Charset.forName("UTF-8"));
} catch (Exception e) {
// ignore
}
akka.http.javadsl.model.Multipart.FormData multipartForm =
Multiparts.createFormDataFromPath("csv", ContentTypes.TEXT_PLAIN_UTF8, tempFile.toPath());
// test:
testRoute(route).run(HttpRequest.POST("/").withEntity(
multipartForm.toEntity(HttpCharsets.UTF_8, BodyPartRenderer.randomBoundaryWithDefaults())))
.assertStatusCode(StatusCodes.OK).assertEntityAs(Unmarshaller.entityToString(), "Sum: 178");
//#
}
}

View file

@ -1,137 +0,0 @@
/*
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import akka.http.javadsl.model.FormData;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.StatusCodes;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.unmarshalling.StringUnmarshallers;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.japi.Pair;
import org.junit.Test;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.stream.Collectors;
public class FormFieldDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testFormField() {
//#formField
final Route route = route(
formField("color", color ->
complete("The color is '" + color + "'")
),
formField(StringUnmarshallers.INTEGER, "id", id ->
complete("The id is '" + id + "'")
)
);
// tests:
final FormData formData = FormData.create(Pair.create("color", "blue"));
testRoute(route).run(HttpRequest.POST("/").withEntity(formData.toEntity()))
.assertEntity("The color is 'blue'");
testRoute(route).run(HttpRequest.GET("/"))
.assertStatusCode(StatusCodes.BAD_REQUEST)
.assertEntity("Request is missing required form field 'color'");
//#formField
}
@Test
public void testFormFieldMap() {
//#formFieldMap
final Function<Map<String, String>, String> mapToString = map ->
map.entrySet()
.stream()
.map(e -> e.getKey() + " = '" + e.getValue() +"'")
.collect(Collectors.joining(", "));
final Route route = formFieldMap(fields ->
complete("The form fields are " + mapToString.apply(fields))
);
// tests:
final FormData formDataDiffKey =
FormData.create(
Pair.create("color", "blue"),
Pair.create("count", "42"));
testRoute(route).run(HttpRequest.POST("/").withEntity(formDataDiffKey.toEntity()))
.assertEntity("The form fields are color = 'blue', count = '42'");
final FormData formDataSameKey =
FormData.create(
Pair.create("x", "1"),
Pair.create("x", "5"));
testRoute(route).run(HttpRequest.POST("/").withEntity(formDataSameKey.toEntity()))
.assertEntity( "The form fields are x = '5'");
//#formFieldMap
}
@Test
public void testFormFieldMultiMap() {
//#formFieldMultiMap
final Function<Map<String, List<String>>, String> mapToString = map ->
map.entrySet()
.stream()
.map(e -> e.getKey() + " -> " + e.getValue().size())
.collect(Collectors.joining(", "));
final Route route = formFieldMultiMap(fields ->
complete("There are form fields " + mapToString.apply(fields))
);
// test:
final FormData formDataDiffKey =
FormData.create(
Pair.create("color", "blue"),
Pair.create("count", "42"));
testRoute(route).run(HttpRequest.POST("/").withEntity(formDataDiffKey.toEntity()))
.assertEntity("There are form fields color -> 1, count -> 1");
final FormData formDataSameKey =
FormData.create(
Pair.create("x", "23"),
Pair.create("x", "4"),
Pair.create("x", "89"));
testRoute(route).run(HttpRequest.POST("/").withEntity(formDataSameKey.toEntity()))
.assertEntity("There are form fields x -> 3");
//#formFieldMultiMap
}
@Test
public void testFormFieldList() {
//#formFieldList
final Function<List<Entry<String, String>>, String> listToString = list ->
list.stream()
.map(e -> e.getKey() + " = '" + e.getValue() +"'")
.collect(Collectors.joining(", "));
final Route route = formFieldList(fields ->
complete("The form fields are " + listToString.apply(fields))
);
// tests:
final FormData formDataDiffKey =
FormData.create(
Pair.create("color", "blue"),
Pair.create("count", "42"));
testRoute(route).run(HttpRequest.POST("/").withEntity(formDataDiffKey.toEntity()))
.assertEntity("The form fields are color = 'blue', count = '42'");
final FormData formDataSameKey =
FormData.create(
Pair.create("x", "23"),
Pair.create("x", "4"),
Pair.create("x", "89"));
testRoute(route).run(HttpRequest.POST("/").withEntity(formDataSameKey.toEntity()))
.assertEntity("The form fields are x = '23', x = '4', x = '89'");
//#formFieldList
}
}

View file

@ -1,150 +0,0 @@
/*
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.marshalling.Marshaller;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.scaladsl.model.StatusCodes;
import akka.japi.pf.PFBuilder;
import akka.pattern.CircuitBreaker;
import org.junit.Test;
import scala.concurrent.duration.FiniteDuration;
import static akka.http.javadsl.server.PathMatchers.*;
import static scala.compat.java8.JFunction.func;
public class FutureDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testOnComplete() {
//#onComplete
// import static scala.compat.java8.JFunction.func;
// import static akka.http.javadsl.server.PathMatchers.*;
final Route route = path(segment("divide").slash(integerSegment()).slash(integerSegment()),
(a, b) -> onComplete(
() -> CompletableFuture.supplyAsync(() -> a / b),
maybeResult -> maybeResult
.map(func(result -> complete("The result was " + result)))
.recover(new PFBuilder<Throwable, Route>()
.matchAny(ex -> complete(StatusCodes.InternalServerError(),
"An error occurred: " + ex.getMessage())
)
.build())
.get()
)
);
testRoute(route).run(HttpRequest.GET("/divide/10/2"))
.assertEntity("The result was 5");
testRoute(route).run(HttpRequest.GET("/divide/10/0"))
.assertStatusCode(StatusCodes.InternalServerError())
.assertEntity("An error occurred: / by zero");
//#onComplete
}
@Test
public void testOnSuccess() {
//#onSuccess
final Route route = path("success", () ->
onSuccess(() -> CompletableFuture.supplyAsync(() -> "Ok"),
extraction -> complete(extraction)
)
).orElse(path("failure", () ->
onSuccess(() -> CompletableFuture.supplyAsync(() -> {
throw new RuntimeException();
}),
extraction -> complete("never reaches here"))
));
testRoute(route).run(HttpRequest.GET("/success"))
.assertEntity("Ok");
testRoute(route).run(HttpRequest.GET("/failure"))
.assertStatusCode(StatusCodes.InternalServerError())
.assertEntity("There was an internal server error.");
//#onSuccess
}
@Test
public void testCompleteOrRecoverWith() {
//#completeOrRecoverWith
final Route route = path("success", () ->
completeOrRecoverWith(
() -> CompletableFuture.supplyAsync(() -> "Ok"),
Marshaller.stringToEntity(),
extraction -> failWith(extraction) // not executed
)
).orElse(path("failure", () ->
completeOrRecoverWith(
() -> CompletableFuture.supplyAsync(() -> {
throw new RuntimeException();
}),
Marshaller.stringToEntity(),
extraction -> failWith(extraction))
));
testRoute(route).run(HttpRequest.GET("/success"))
.assertEntity("Ok");
testRoute(route).run(HttpRequest.GET("/failure"))
.assertStatusCode(StatusCodes.InternalServerError())
.assertEntity("There was an internal server error.");
//#completeOrRecoverWith
}
@Test
public void testOnCompleteWithBreaker() throws InterruptedException {
//#onCompleteWithBreaker
// import static scala.compat.java8.JFunction.func;
// import static akka.http.javadsl.server.PathMatchers.*;
final int maxFailures = 1;
final FiniteDuration callTimeout = FiniteDuration.create(5, TimeUnit.SECONDS);
final FiniteDuration resetTimeout = FiniteDuration.create(1, TimeUnit.SECONDS);
final CircuitBreaker breaker = CircuitBreaker.create(system().scheduler(), maxFailures, callTimeout, resetTimeout);
final Route route = path(segment("divide").slash(integerSegment()).slash(integerSegment()),
(a, b) -> onCompleteWithBreaker(breaker,
() -> CompletableFuture.supplyAsync(() -> a / b),
maybeResult -> maybeResult
.map(func(result -> complete("The result was " + result)))
.recover(new PFBuilder<Throwable, Route>()
.matchAny(ex -> complete(StatusCodes.InternalServerError(),
"An error occurred: " + ex.getMessage())
)
.build())
.get()
)
);
testRoute(route).run(HttpRequest.GET("/divide/10/2"))
.assertEntity("The result was 5");
testRoute(route).run(HttpRequest.GET("/divide/10/0"))
.assertStatusCode(StatusCodes.InternalServerError())
.assertEntity("An error occurred: / by zero");
// opened the circuit-breaker
testRoute(route).run(HttpRequest.GET("/divide/10/0"))
.assertStatusCode(StatusCodes.ServiceUnavailable())
.assertEntity("The server is currently unavailable (because it is overloaded or down for maintenance).");
Thread.sleep(resetTimeout.toMillis() + 300);
// circuit breaker resets after this time
testRoute(route).run(HttpRequest.GET("/divide/8/2"))
.assertEntity("The result was 4");
//#onCompleteWithBreaker
}
}

View file

@ -1,261 +0,0 @@
/*
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import java.util.Optional;
import java.util.function.Function;
import org.junit.Test;
import akka.http.javadsl.model.HttpHeader;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.StatusCodes;
import akka.http.javadsl.model.headers.Host;
import akka.http.javadsl.model.headers.HttpOrigin;
import akka.http.javadsl.model.headers.HttpOriginRange;
import akka.http.javadsl.model.headers.Origin;
import akka.http.javadsl.model.headers.RawHeader;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.japi.JavaPartialFunction;
import akka.http.javadsl.testkit.TestRoute;
import scala.PartialFunction;
public class HeaderDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testHeaderValue() {
//#headerValue
final Function<HttpHeader, Optional<Host>> extractHostPort = header -> {
if (header instanceof Host) {
return Optional.of((Host) header);
} else {
return Optional.empty();
}
};
final Route route = headerValue(extractHostPort, host ->
complete("The port was " + host.port())
);
// tests:
testRoute(route).run(HttpRequest.GET("/").addHeader(Host.create("example.com", 5043)))
.assertEntity("The port was 5043");
testRoute(route).run(HttpRequest.GET("/"))
.assertStatusCode(StatusCodes.NOT_FOUND)
.assertEntity("The requested resource could not be found.");
//#headerValue
}
@Test
public void testHeaderValueByName() {
//#headerValueByName
final Route route = headerValueByName("X-User-Id", userId ->
complete("The user is " + userId)
);
// tests:
final RawHeader header = RawHeader.create("X-User-Id", "Joe42");
testRoute(route).run(HttpRequest.GET("/").addHeader(header))
.assertEntity("The user is Joe42");
testRoute(route).run(HttpRequest.GET("/"))
.assertStatusCode(StatusCodes.BAD_REQUEST)
.assertEntity("Request is missing required HTTP header 'X-User-Id'");
//#headerValueByName
}
@Test
public void testHeaderValueByType() {
//#headerValueByType
final Route route = headerValueByType(Origin.class, origin ->
complete("The first origin was " + origin.getOrigins().iterator().next())
);
// tests:
final Host host = Host.create("localhost", 8080);
final Origin originHeader = Origin.create(HttpOrigin.create("http", host));
testRoute(route).run(HttpRequest.GET("abc").addHeader(originHeader))
.assertEntity("The first origin was http://localhost:8080");
testRoute(route).run(HttpRequest.GET("abc"))
.assertStatusCode(StatusCodes.BAD_REQUEST)
.assertEntity("Request is missing required HTTP header 'Origin'");
//#headerValueByType
}
@Test
public void testHeaderValuePF() {
//#headerValuePF
final PartialFunction<HttpHeader, Integer> extractHostPort =
new JavaPartialFunction<HttpHeader, Integer>() {
@Override
public Integer apply(HttpHeader x, boolean isCheck) throws Exception {
if (x instanceof Host) {
if (isCheck) {
return null;
} else {
return ((Host) x).port();
}
} else {
throw noMatch();
}
}
};
final Route route = headerValuePF(extractHostPort, port ->
complete("The port was " + port)
);
// tests:
testRoute(route).run(HttpRequest.GET("/").addHeader(Host.create("example.com", 5043)))
.assertEntity("The port was 5043");
testRoute(route).run(HttpRequest.GET("/"))
.assertStatusCode(StatusCodes.NOT_FOUND)
.assertEntity("The requested resource could not be found.");
//#headerValuePF
}
@Test
public void testOptionalHeaderValue() {
//#optionalHeaderValue
final Function<HttpHeader, Optional<Integer>> extractHostPort = header -> {
if (header instanceof Host) {
return Optional.of(((Host) header).port());
} else {
return Optional.empty();
}
};
final Route route = optionalHeaderValue(extractHostPort, port -> {
if (port.isPresent()) {
return complete("The port was " + port.get());
} else {
return complete("The port was not provided explicitly");
}
});
// tests:
testRoute(route).run(HttpRequest.GET("/").addHeader(Host.create("example.com", 5043)))
.assertEntity("The port was 5043");
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("The port was not provided explicitly");
//#optionalHeaderValue
}
@Test
public void testOptionalHeaderValueByName() {
//#optionalHeaderValueByName
final Route route = optionalHeaderValueByName("X-User-Id", userId -> {
if (userId.isPresent()) {
return complete("The user is " + userId.get());
} else {
return complete("No user was provided");
}
});
// tests:
final RawHeader header = RawHeader.create("X-User-Id", "Joe42");
testRoute(route).run(HttpRequest.GET("/").addHeader(header))
.assertEntity("The user is Joe42");
testRoute(route).run(HttpRequest.GET("/")).assertEntity("No user was provided");
//#optionalHeaderValueByName
}
@Test
public void testOptionalHeaderValueByType() {
//#optionalHeaderValueByType
final Route route = optionalHeaderValueByType(Origin.class, origin -> {
if (origin.isPresent()) {
return complete("The first origin was " + origin.get().getOrigins().iterator().next());
} else {
return complete("No Origin header found.");
}
});
// tests:
// extract Some(header) if the type is matching
Host host = Host.create("localhost", 8080);
Origin originHeader = Origin.create(HttpOrigin.create("http", host));
testRoute(route).run(HttpRequest.GET("abc").addHeader(originHeader))
.assertEntity("The first origin was http://localhost:8080");
// extract None if no header of the given type is present
testRoute(route).run(HttpRequest.GET("abc")).assertEntity("No Origin header found.");
//#optionalHeaderValueByType
}
@Test
public void testOptionalHeaderValuePF() {
//#optionalHeaderValuePF
final PartialFunction<HttpHeader, Integer> extractHostPort =
new JavaPartialFunction<HttpHeader, Integer>() {
@Override
public Integer apply(HttpHeader x, boolean isCheck) throws Exception {
if (x instanceof Host) {
if (isCheck) {
return null;
} else {
return ((Host) x).port();
}
} else {
throw noMatch();
}
}
};
final Route route = optionalHeaderValuePF(extractHostPort, port -> {
if (port.isPresent()) {
return complete("The port was " + port.get());
} else {
return complete("The port was not provided explicitly");
}
});
// tests:
testRoute(route).run(HttpRequest.GET("/").addHeader(Host.create("example.com", 5043)))
.assertEntity("The port was 5043");
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("The port was not provided explicitly");
//#optionalHeaderValuePF
}
@Test
public void testCheckSameOrigin() {
//#checkSameOrigin
final HttpOrigin validOriginHeader =
HttpOrigin.create("http://localhost", Host.create("8080"));
final HttpOriginRange validOriginRange = HttpOriginRange.create(validOriginHeader);
final TestRoute route = testRoute(
checkSameOrigin(validOriginRange,
() -> complete("Result")));
route
.run(HttpRequest.create().addHeader(Origin.create(validOriginHeader)))
.assertStatusCode(StatusCodes.OK)
.assertEntity("Result");
route
.run(HttpRequest.create())
.assertStatusCode(StatusCodes.BAD_REQUEST);
final HttpOrigin invalidOriginHeader =
HttpOrigin.create("http://invalid.com", Host.create("8080"));
route
.run(HttpRequest.create().addHeader(Origin.create(invalidOriginHeader)))
.assertStatusCode(StatusCodes.FORBIDDEN);
//#checkSameOrigin
}
}

View file

@ -1,92 +0,0 @@
/*
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import java.util.Arrays;
import java.util.regex.Pattern;
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.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
public class HostDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testListOfHost() {
//#host1
final Route matchListOfHosts = host(
Arrays.asList("api.company.com", "rest.company.com"),
() -> complete(StatusCodes.OK));
testRoute(matchListOfHosts).run(HttpRequest.GET("/").addHeader(Host.create("api.company.com")))
.assertStatusCode(StatusCodes.OK);
//#host1
}
@Test
public void testHostPredicate() {
//#host2
final Route shortOnly = host(hostname -> hostname.length() < 10,
() -> complete(StatusCodes.OK));
testRoute(shortOnly).run(HttpRequest.GET("/").addHeader(Host.create("short.com")))
.assertStatusCode(StatusCodes.OK);
testRoute(shortOnly).run(HttpRequest.GET("/").addHeader(Host.create("verylonghostname.com")))
.assertStatusCode(StatusCodes.NOT_FOUND);
//#host2
}
@Test
public void testExtractHost() {
//#extractHostname
final Route route = extractHost(hn ->
complete("Hostname: " + hn));
testRoute(route).run(HttpRequest.GET("/").addHeader(Host.create("company.com", 9090)))
.assertEntity("Hostname: company.com");
//#extractHostname
}
@Test
public void testMatchAndExtractHost() {
//#matchAndExtractHost
final Route hostPrefixRoute = host(Pattern.compile("api|rest"), prefix ->
complete("Extracted prefix: " + prefix));
final Route hostPartRoute = host(Pattern.compile("public.(my|your)company.com"), captured ->
complete("You came through " + captured
+ " company"));
final Route route = route(hostPrefixRoute, hostPartRoute);
testRoute(route).run(HttpRequest.GET("/").addHeader(Host.create("api.company.com")))
.assertStatusCode(StatusCodes.OK).assertEntity("Extracted prefix: api");
testRoute(route).run(HttpRequest.GET("/").addHeader(Host.create("public.mycompany.com")))
.assertStatusCode(StatusCodes.OK)
.assertEntity("You came through my company");
//#matchAndExtractHost
}
@SuppressWarnings("unused")
@Test(expected = IllegalArgumentException.class)
public void testFailingMatchAndExtractHost() {
//#failing-matchAndExtractHost
// this will throw IllegalArgumentException
final Route hostRegex = host(Pattern.compile("server-([0-9]).company.(com|net|org)"), s ->
// will not reach here
complete(s)
);
//#failing-matchAndExtractHost
}
}

View file

@ -1,150 +0,0 @@
/*
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
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.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
public class MethodDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testDelete() {
//#delete
final Route route = delete(() -> complete("This is a DELETE request."));
testRoute(route).run(HttpRequest.DELETE("/")).assertEntity(
"This is a DELETE request.");
//#delete
}
@Test
public void testGet() {
//#get
final Route route = get(() -> complete("This is a GET request."));
testRoute(route).run(HttpRequest.GET("/")).assertEntity(
"This is a GET request.");
//#get
}
@Test
public void testHead() {
//#head
final Route route = head(() -> complete("This is a HEAD request."));
testRoute(route).run(HttpRequest.HEAD("/")).assertEntity(
"This is a HEAD request.");
//#head
}
@Test
public void testOptions() {
//#options
final Route route = options(() -> complete("This is a OPTIONS request."));
testRoute(route).run(HttpRequest.OPTIONS("/")).assertEntity(
"This is a OPTIONS request.");
//#options
}
@Test
public void testPatch() {
//#patch
final Route route = patch(() -> complete("This is a PATCH request."));
testRoute(route).run(HttpRequest.PATCH("/").withEntity("patch content"))
.assertEntity("This is a PATCH request.");
//#patch
}
@Test
public void testPost() {
//#post
final Route route = post(() -> complete("This is a POST request."));
testRoute(route).run(HttpRequest.POST("/").withEntity("post content"))
.assertEntity("This is a POST request.");
//#post
}
@Test
public void testPut() {
//#put
final Route route = put(() -> complete("This is a PUT request."));
testRoute(route).run(HttpRequest.PUT("/").withEntity("put content"))
.assertEntity("This is a PUT request.");
//#put
}
@Test
public void testMethodExample() {
//#method-example
final Route route = method(HttpMethods.PUT,
() -> complete("This is a PUT request."));
testRoute(route).run(HttpRequest.PUT("/").withEntity("put content"))
.assertEntity("This is a PUT request.");
testRoute(route).run(HttpRequest.GET("/")).assertStatusCode(
StatusCodes.METHOD_NOT_ALLOWED);
//#method-example
}
@Test
public void testExtractMethodExample() {
//#extractMethod
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.");
testRoute(route).run(HttpRequest.PUT("/").withEntity("put content"))
.assertEntity("This PUT request, clearly is not a GET!");
testRoute(route).run(HttpRequest.HEAD("/")).assertEntity(
"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
}
}

View file

@ -1,64 +0,0 @@
/*
* Copyright (C) 2016-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.server.Route;
import akka.http.javadsl.unmarshalling.Unmarshaller;
import akka.http.javadsl.testkit.JUnitRouteTest;
import org.junit.Test;
import java.util.Arrays;
import java.util.function.Function;
public class MiscDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testWithSizeLimit() {
//#withSizeLimitExample
final Route route = withSizeLimit(500, () ->
entity(Unmarshaller.entityToString(), (entity) ->
complete("ok")
)
);
Function<Integer, HttpRequest> withEntityOfSize = (sizeLimit) -> {
char[] charArray = new char[sizeLimit];
Arrays.fill(charArray, '0');
return HttpRequest.POST("/").withEntity(new String(charArray));
};
// tests:
testRoute(route).run(withEntityOfSize.apply(500))
.assertStatusCode(StatusCodes.OK);
testRoute(route).run(withEntityOfSize.apply(501))
.assertStatusCode(StatusCodes.BAD_REQUEST);
//#withSizeLimitExample
}
@Test
public void testWithoutSizeLimit() {
//#withoutSizeLimitExample
final Route route = withoutSizeLimit(() ->
entity(Unmarshaller.entityToString(), (entity) ->
complete("ok")
)
);
Function<Integer, HttpRequest> withEntityOfSize = (sizeLimit) -> {
char[] charArray = new char[sizeLimit];
Arrays.fill(charArray, '0');
return HttpRequest.POST("/").withEntity(new String(charArray));
};
// tests:
// will work even if you have configured akka.http.parsing.max-content-length = 500
testRoute(route).run(withEntityOfSize.apply(501))
.assertStatusCode(StatusCodes.OK);
//#withoutSizeLimitExample
}
}

View file

@ -1,121 +0,0 @@
/*
* Copyright (C) 2016-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.server.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
import org.junit.Test;
import java.util.Map.Entry;
import java.util.function.Function;
import java.util.stream.Collectors;
public class ParameterDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testParameter() {
//#parameter
final Route route = parameter("color", color ->
complete("The color is '" + color + "'")
);
// tests:
testRoute(route).run(HttpRequest.GET("/?color=blue"))
.assertEntity("The color is 'blue'");
testRoute(route).run(HttpRequest.GET("/"))
.assertStatusCode(StatusCodes.NOT_FOUND)
.assertEntity("Request is missing required query parameter 'color'");
//#parameter
}
@Test
public void testParameters() {
//#parameters
final Route route = parameter("color", color ->
parameter("backgroundColor", backgroundColor ->
complete("The color is '" + color
+ "' and the background is '" + backgroundColor + "'")
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/?color=blue&backgroundColor=red"))
.assertEntity("The color is 'blue' and the background is 'red'");
testRoute(route).run(HttpRequest.GET("/?color=blue"))
.assertStatusCode(StatusCodes.NOT_FOUND)
.assertEntity("Request is missing required query parameter 'backgroundColor'");
//#parameters
}
@Test
public void testParameterMap() {
//#parameterMap
final Function<Entry, String> paramString =
entry -> entry.getKey() + " = '" + entry.getValue() + "'";
final Route route = parameterMap(params -> {
final String pString = params.entrySet()
.stream()
.map(paramString::apply)
.collect(Collectors.joining(", "));
return complete("The parameters are " + pString);
});
// tests:
testRoute(route).run(HttpRequest.GET("/?color=blue&count=42"))
.assertEntity("The parameters are color = 'blue', count = '42'");
testRoute(route).run(HttpRequest.GET("/?x=1&x=2"))
.assertEntity("The parameters are x = '2'");
//#parameterMap
}
@Test
public void testParameterMultiMap() {
//#parameterMultiMap
final Route route = parameterMultiMap(params -> {
final String pString = params.entrySet()
.stream()
.map(e -> e.getKey() + " -> " + e.getValue().size())
.collect(Collectors.joining(", "));
return complete("There are parameters " + pString);
});
// tests:
testRoute(route).run(HttpRequest.GET("/?color=blue&count=42"))
.assertEntity("There are parameters color -> 1, count -> 1");
testRoute(route).run(HttpRequest.GET("/?x=23&x=42"))
.assertEntity("There are parameters x -> 2");
//#parameterMultiMap
}
@Test
public void testParameterSeq() {
//#parameterSeq
final Function<Entry, String> paramString =
entry -> entry.getKey() + " = '" + entry.getValue() + "'";
final Route route = parameterList(params -> {
final String pString = params.stream()
.map(paramString::apply)
.collect(Collectors.joining(", "));
return complete("The parameters are " + pString);
});
// tests:
testRoute(route).run(HttpRequest.GET("/?color=blue&count=42"))
.assertEntity("The parameters are color = 'blue', count = '42'");
testRoute(route).run(HttpRequest.GET("/?x=1&x=2"))
.assertEntity("The parameters are x = '1', x = '2'");
//#parameterSeq
}
}

View file

@ -1,322 +0,0 @@
/*
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import java.util.Arrays;
import java.util.regex.Pattern;
import org.junit.Test;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.StatusCodes;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
import static akka.http.javadsl.server.PathMatchers.segment;
import static akka.http.javadsl.server.PathMatchers.segments;
import static akka.http.javadsl.server.PathMatchers.integerSegment;
import static akka.http.javadsl.server.PathMatchers.neutral;
import static akka.http.javadsl.server.PathMatchers.slash;
import java.util.function.Supplier;
import akka.http.javadsl.server.directives.RouteAdapter;
import static java.util.regex.Pattern.compile;
public class PathDirectivesExamplesTest extends JUnitRouteTest {
//# path-prefix-test, path-suffix, raw-path-prefix, raw-path-prefix-test
Supplier<RouteAdapter> completeWithUnmatchedPath = ()->
extractUnmatchedPath((path) -> complete(path.toString()));
//#
@Test
public void testPathExamples() {
//# path-dsl
// matches /foo/
path(segment("foo").slash(), () -> complete(StatusCodes.OK));
// matches e.g. /foo/123 and extracts "123" as a String
path(segment("foo").slash(segment(compile("\\d+"))), (value) ->
complete(StatusCodes.OK));
// matches e.g. /foo/bar123 and extracts "123" as a String
path(segment("foo").slash(segment(compile("bar(\\d+)"))), (value) ->
complete(StatusCodes.OK));
// similar to `path(Segments)`
path(neutral().repeat(0, 10), () -> complete(StatusCodes.OK));
// identical to path("foo" ~ (PathEnd | Slash))
path(segment("foo").orElse(slash()), () -> complete(StatusCodes.OK));
//# path-dsl
}
@Test
public void testBasicExamples() {
path("test", () -> complete(StatusCodes.OK));
// matches "/test", as well
path(segment("test"), () -> complete(StatusCodes.OK));
}
@Test
public void testPathExample() {
//# pathPrefix
final Route route =
route(
path("foo", () -> complete("/foo")),
path(segment("foo").slash("bar"), () -> complete("/foo/bar")),
pathPrefix("ball", () ->
route(
pathEnd(() -> complete("/ball")),
path(integerSegment(), (i) ->
complete((i % 2 == 0) ? "even ball" : "odd ball"))
)
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/")).assertStatusCode(StatusCodes.NOT_FOUND);
testRoute(route).run(HttpRequest.GET("/foo")).assertEntity("/foo");
testRoute(route).run(HttpRequest.GET("/foo/bar")).assertEntity("/foo/bar");
testRoute(route).run(HttpRequest.GET("/ball/1337")).assertEntity("odd ball");
//# pathPrefix
}
@Test
public void testPathEnd() {
//# path-end
final Route route =
route(
pathPrefix("foo", () ->
route(
pathEnd(() -> complete("/foo")),
path("bar", () -> complete("/foo/bar"))
)
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/foo")).assertEntity("/foo");
testRoute(route).run(HttpRequest.GET("/foo/")).assertStatusCode(StatusCodes.NOT_FOUND);
testRoute(route).run(HttpRequest.GET("/foo/bar")).assertEntity("/foo/bar");
//# path-end
}
@Test
public void testPathEndOrSingleSlash() {
//# path-end-or-single-slash
final Route route =
route(
pathPrefix("foo", () ->
route(
pathEndOrSingleSlash(() -> complete("/foo")),
path("bar", () -> complete("/foo/bar"))
)
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/foo")).assertEntity("/foo");
testRoute(route).run(HttpRequest.GET("/foo/")).assertEntity("/foo");
testRoute(route).run(HttpRequest.GET("/foo/bar")).assertEntity("/foo/bar");
//# path-end-or-single-slash
}
@Test
public void testPathPrefix() {
//# path-prefix
final Route route =
route(
pathPrefix("ball", () ->
route(
pathEnd(() -> complete("/ball")),
path(integerSegment(), (i) ->
complete((i % 2 == 0) ? "even ball" : "odd ball"))
)
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/")).assertStatusCode(StatusCodes.NOT_FOUND);
testRoute(route).run(HttpRequest.GET("/ball")).assertEntity("/ball");
testRoute(route).run(HttpRequest.GET("/ball/1337")).assertEntity("odd ball");
//# path-prefix
}
@Test
public void testPathPrefixTest() {
//# path-prefix-test
final Route route =
route(
pathPrefixTest(segment("foo").orElse("bar"), () ->
route(
pathPrefix("foo", () -> completeWithUnmatchedPath.get()),
pathPrefix("bar", () -> completeWithUnmatchedPath.get())
)
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/foo/doo")).assertEntity("/doo");
testRoute(route).run(HttpRequest.GET("/bar/yes")).assertEntity("/yes");
//# path-prefix-test
}
@Test
public void testPathSingleSlash() {
//# path-single-slash
final Route route =
route(
pathSingleSlash(() -> complete("root")),
pathPrefix("ball", () ->
route(
pathSingleSlash(() -> complete("/ball/")),
path(integerSegment(), (i) -> complete((i % 2 == 0) ? "even ball" : "odd ball"))
)
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/")).assertEntity("root");
testRoute(route).run(HttpRequest.GET("/ball")).assertStatusCode(StatusCodes.NOT_FOUND);
testRoute(route).run(HttpRequest.GET("/ball/")).assertEntity("/ball/");
testRoute(route).run(HttpRequest.GET("/ball/1337")).assertEntity("odd ball");
//# path-single-slash
}
@Test
public void testPathSuffix() {
//# path-suffix
final Route route =
route(
pathPrefix("start", () ->
route(
pathSuffix("end", () -> completeWithUnmatchedPath.get()),
pathSuffix(segment("foo").slash("bar").concat("baz"), () ->
completeWithUnmatchedPath.get())
)
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/start/middle/end")).assertEntity("/middle/");
testRoute(route).run(HttpRequest.GET("/start/something/barbaz/foo")).assertEntity("/something/");
//# path-suffix
}
@Test
public void testPathSuffixTest() {
//# path-suffix-test
final Route route =
route(
pathSuffixTest(slash(), () -> complete("slashed")),
complete("unslashed")
);
// tests:
testRoute(route).run(HttpRequest.GET("/foo/")).assertEntity("slashed");
testRoute(route).run(HttpRequest.GET("/foo")).assertEntity("unslashed");
//# path-suffix-test
}
@Test
public void testRawPathPrefix() {
//# raw-path-prefix
final Route route =
route(
pathPrefix("foo", () ->
route(
rawPathPrefix("bar", () -> completeWithUnmatchedPath.get()),
rawPathPrefix("doo", () -> completeWithUnmatchedPath.get())
)
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/foobar/baz")).assertEntity("/baz");
testRoute(route).run(HttpRequest.GET("/foodoo/baz")).assertEntity("/baz");
//# raw-path-prefix
}
@Test
public void testRawPathPrefixTest() {
//# raw-path-prefix-test
final Route route =
route(
pathPrefix("foo", () ->
rawPathPrefixTest("bar", () -> completeWithUnmatchedPath.get())
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/foobar")).assertEntity("bar");
testRoute(route).run(HttpRequest.GET("/foobaz")).assertStatusCode(StatusCodes.NOT_FOUND);
//# raw-path-prefix-test
}
@Test
public void testRedirectToNoTrailingSlashIfMissing() {
//# redirect-notrailing-slash-missing
final Route route =
redirectToTrailingSlashIfMissing(
StatusCodes.MOVED_PERMANENTLY, () ->
route(
path(segment("foo").slash(), () -> complete("OK")),
path(segment("bad-1"), () ->
// MISTAKE!
// Missing `/` in path, causes this path to never match,
// because it is inside a `redirectToTrailingSlashIfMissing`
complete(StatusCodes.NOT_IMPLEMENTED)
),
path(segment("bad-2").slash(), () ->
// MISTAKE!
// / should be explicit as path element separator and not *in* the path element
// So it should be: "bad-1" /
complete(StatusCodes.NOT_IMPLEMENTED)
)
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/foo"))
.assertStatusCode(StatusCodes.MOVED_PERMANENTLY)
.assertEntity("This and all future requests should be directed to " +
"<a href=\"http://example.com/foo/\">this URI</a>.");
testRoute(route).run(HttpRequest.GET("/foo/"))
.assertStatusCode(StatusCodes.OK)
.assertEntity("OK");
testRoute(route).run(HttpRequest.GET("/bad-1/"))
.assertStatusCode(StatusCodes.NOT_FOUND);
//# redirect-notrailing-slash-missing
}
@Test
public void testRedirectToNoTrailingSlashIfPresent() {
//# redirect-notrailing-slash-present
final Route route =
redirectToNoTrailingSlashIfPresent(
StatusCodes.MOVED_PERMANENTLY, () ->
route(
path("foo", () -> complete("OK")),
path(segment("bad").slash(), () ->
// MISTAKE!
// Since inside a `redirectToNoTrailingSlashIfPresent` directive
// the matched path here will never contain a trailing slash,
// thus this path will never match.
//
// It should be `path("bad")` instead.
complete(StatusCodes.NOT_IMPLEMENTED)
)
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/foo/"))
.assertStatusCode(StatusCodes.MOVED_PERMANENTLY)
.assertEntity("This and all future requests should be directed to " +
"<a href=\"http://example.com/foo\">this URI</a>.");
testRoute(route).run(HttpRequest.GET("/foo"))
.assertStatusCode(StatusCodes.OK)
.assertEntity("OK");
testRoute(route).run(HttpRequest.GET("/bad"))
.assertStatusCode(StatusCodes.NOT_FOUND);
//# redirect-notrailing-slash-present
}
}

View file

@ -1,89 +0,0 @@
/**
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.Multipart;
import akka.http.javadsl.model.StatusCodes;
import akka.http.javadsl.model.headers.ByteRange;
import akka.http.javadsl.model.headers.ContentRange;
import akka.http.javadsl.model.headers.Range;
import akka.http.javadsl.model.headers.RangeUnits;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.unmarshalling.Unmarshaller;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.testkit.TestRouteResult;
import akka.stream.ActorMaterializer;
import akka.util.ByteString;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;
public class RangeDirectivesExamplesTest extends JUnitRouteTest {
@Override
public Config additionalConfig() {
return ConfigFactory.parseString("akka.http.routing.range-coalescing-threshold=2");
}
@Test
public void testWithRangeSupport() {
//#withRangeSupport
final Route route = withRangeSupport(() -> complete("ABCDEFGH"));
// test:
final String bytes348Range = ContentRange.create(RangeUnits.BYTES,
akka.http.javadsl.model.ContentRange.create(3, 4, 8)).value();
final akka.http.javadsl.model.ContentRange bytes028Range =
akka.http.javadsl.model.ContentRange.create(0, 2, 8);
final akka.http.javadsl.model.ContentRange bytes678Range =
akka.http.javadsl.model.ContentRange.create(6, 7, 8);
final ActorMaterializer materializer = systemResource().materializer();
testRoute(route).run(HttpRequest.GET("/")
.addHeader(Range.create(RangeUnits.BYTES, ByteRange.createSlice(3, 4))))
.assertHeaderKindExists("Content-Range")
.assertHeaderExists("Content-Range", bytes348Range)
.assertStatusCode(StatusCodes.PARTIAL_CONTENT)
.assertEntity("DE");
// we set "akka.http.routing.range-coalescing-threshold = 2"
// above to make sure we get two BodyParts
final TestRouteResult response = testRoute(route).run(HttpRequest.GET("/")
.addHeader(Range.create(RangeUnits.BYTES,
ByteRange.createSlice(0, 1), ByteRange.createSlice(1, 2), ByteRange.createSlice(6, 7))));
response.assertHeaderKindNotExists("Content-Range");
final CompletionStage<List<Multipart.ByteRanges.BodyPart>> completionStage =
response.entity(Unmarshaller.entityToMultipartByteRanges()).getParts()
.runFold(new ArrayList<>(), (acc, n) -> {
acc.add(n);
return acc;
}, materializer);
try {
final List<Multipart.ByteRanges.BodyPart> bodyParts =
completionStage.toCompletableFuture().get(3, TimeUnit.SECONDS);
assertEquals(2, bodyParts.toArray().length);
final Multipart.ByteRanges.BodyPart part1 = bodyParts.get(0);
assertEquals(bytes028Range, part1.getContentRange());
assertEquals(ByteString.fromString("ABC"),
part1.toStrict(1000, materializer).toCompletableFuture().get().getEntity().getData());
final Multipart.ByteRanges.BodyPart part2 = bodyParts.get(1);
assertEquals(bytes678Range, part2.getContentRange());
assertEquals(ByteString.fromString("GH"),
part2.toStrict(1000, materializer).toCompletableFuture().get().getEntity().getData());
} catch (Exception e) {
// please handle this in production code
}
//#
}
}

View file

@ -1,135 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import java.util.List;
import akka.http.javadsl.model.HttpHeader;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.headers.HttpOrigin;
import akka.http.javadsl.model.headers.Origin;
import akka.http.javadsl.model.headers.RawHeader;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
import com.google.common.collect.Lists;
import org.junit.Test;
public class RespondWithDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testRespondWithHeader() {
//#respondWithHeader
final Route route = path("foo", () ->
respondWithHeader(RawHeader.create("Funky-Muppet", "gonzo"), () ->
complete("beep")));
testRoute(route).run(HttpRequest.GET("/foo"))
.assertHeaderExists("Funky-Muppet", "gonzo")
.assertEntity("beep");
//#respondWithHeader
}
@Test
public void testRespondWithDefaultHeader() {
//#respondWithDefaultHeader
//custom headers
final RawHeader blippy = RawHeader.create("X-Fish-Name", "Blippy");
final RawHeader elTonno = RawHeader.create("X-Fish-Name", "El Tonno");
// format: OFF
// by default always include the Blippy header,
// unless a more specific X-Fish-Name is given by the inner route
final Route route =
respondWithDefaultHeader(blippy, () -> // blippy
respondWithHeader(elTonno, () -> // / el tonno
path("el-tonno", () -> // | /
complete("¡Ay blippy!") // | |- el tonno
).orElse( // | |
path("los-tonnos", () -> // | |
complete("¡Ay ay blippy!") // | |- el tonno
) // | |
) // | |
).orElse( // | x
complete("Blip!") // |- blippy
) // x
);
//format: ON
testRoute(route).run(HttpRequest.GET("/"))
.assertHeaderExists("X-Fish-Name", "Blippy")
.assertEntity("Blip!");
testRoute(route).run(HttpRequest.GET("/el-tonno"))
.assertHeaderExists("X-Fish-Name", "El Tonno")
.assertEntity("¡Ay blippy!");
testRoute(route).run(HttpRequest.GET("/los-tonnos"))
.assertHeaderExists("X-Fish-Name", "El Tonno")
.assertEntity("¡Ay ay blippy!");
//#respondWithDefaultHeader
}
@Test
public void respondWithHeaders() {
//#respondWithHeaders
final HttpHeader gonzo = RawHeader.create("Funky-Muppet", "gonzo");
final HttpHeader akka = Origin.create(HttpOrigin.parse("http://akka.io"));
final Route route = path("foo", () ->
respondWithHeaders(Lists.newArrayList(gonzo, akka), () ->
complete("beep")
)
);
testRoute(route).run(HttpRequest.GET("/foo"))
.assertHeaderExists("Funky-Muppet", "gonzo")
.assertHeaderExists("Origin", "http://akka.io")
.assertEntity("beep");
//#respondWithHeaders
}
@Test
public void testRespondWithDefaultHeaders() {
//#respondWithDefaultHeaders
//custom headers
final RawHeader blippy = RawHeader.create("X-Fish-Name", "Blippy");
final HttpHeader akka = Origin.create(HttpOrigin.parse("http://akka.io"));
final List<HttpHeader> defaultHeaders = Lists.newArrayList(blippy, akka);
final RawHeader elTonno = RawHeader.create("X-Fish-Name", "El Tonno");
// format: OFF
// by default always include the Blippy and Akka headers,
// unless a more specific X-Fish-Name is given by the inner route
final Route route =
respondWithDefaultHeaders(defaultHeaders, () -> // blippy and akka
respondWithHeader(elTonno, () -> // / el tonno
path("el-tonno", () -> // | /
complete("¡Ay blippy!") // | |- el tonno
).orElse( // | |
path("los-tonnos", () -> // | |
complete("¡Ay ay blippy!") // | |- el tonno
) // | |
) // | |
).orElse( // | x
complete("Blip!") // |- blippy and akka
) // x
);
//format: ON
testRoute(route).run(HttpRequest.GET("/"))
.assertHeaderExists("X-Fish-Name", "Blippy")
.assertHeaderExists("Origin", "http://akka.io")
.assertEntity("Blip!");
testRoute(route).run(HttpRequest.GET("/el-tonno"))
.assertHeaderExists("X-Fish-Name", "El Tonno")
.assertEntity("¡Ay blippy!");
testRoute(route).run(HttpRequest.GET("/los-tonnos"))
.assertHeaderExists("X-Fish-Name", "El Tonno")
.assertEntity("¡Ay ay blippy!");
//#respondWithDefaultHeaders
}
}

View file

@ -1,125 +0,0 @@
/*
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import akka.http.javadsl.model.HttpEntities;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.Uri;
import akka.http.javadsl.model.headers.ContentType;
import akka.http.javadsl.model.ContentTypes;
import akka.http.javadsl.model.HttpResponse;
import akka.http.javadsl.model.StatusCodes;
import akka.http.javadsl.server.Rejections;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
import org.junit.Test;
import java.util.Collections;
public class RouteDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testComplete() {
//#complete
final Route route = route(
path("a", () -> complete(HttpResponse.create().withEntity("foo"))),
path("b", () -> complete(StatusCodes.OK)),
path("c", () -> complete(StatusCodes.CREATED, "bar")),
path("d", () -> complete(StatusCodes.get(201), "bar")),
path("e", () ->
complete(StatusCodes.CREATED,
Collections.singletonList(ContentType.create(ContentTypes.TEXT_PLAIN_UTF8)),
HttpEntities.create("bar"))),
path("f", () ->
complete(StatusCodes.get(201),
Collections.singletonList(ContentType.create(ContentTypes.TEXT_PLAIN_UTF8)),
HttpEntities.create("bar"))),
path("g", () -> complete("baz"))
);
// tests:
testRoute(route).run(HttpRequest.GET("/a"))
.assertStatusCode(StatusCodes.OK)
.assertEntity("foo");
testRoute(route).run(HttpRequest.GET("/b"))
.assertStatusCode(StatusCodes.OK)
.assertEntity("OK");
testRoute(route).run(HttpRequest.GET("/c"))
.assertStatusCode(StatusCodes.CREATED)
.assertEntity("bar");
testRoute(route).run(HttpRequest.GET("/d"))
.assertStatusCode(StatusCodes.CREATED)
.assertEntity("bar");
testRoute(route).run(HttpRequest.GET("/e"))
.assertStatusCode(StatusCodes.CREATED)
.assertHeaderExists(ContentType.create(ContentTypes.TEXT_PLAIN_UTF8))
.assertEntity("bar");
testRoute(route).run(HttpRequest.GET("/f"))
.assertStatusCode(StatusCodes.CREATED)
.assertHeaderExists(ContentType.create(ContentTypes.TEXT_PLAIN_UTF8))
.assertEntity("bar");
testRoute(route).run(HttpRequest.GET("/g"))
.assertStatusCode(StatusCodes.OK)
.assertEntity("baz");
//#complete
}
@Test
public void testReject() {
//#reject
final Route route = route(
path("a", this::reject), // don't handle here, continue on
path("a", () -> complete("foo")),
path("b", () -> reject(Rejections.validationRejection("Restricted!")))
);
// tests:
testRoute(route).run(HttpRequest.GET("/a"))
.assertEntity("foo");
runRouteUnSealed(route, HttpRequest.GET("/b"))
.assertRejections(Rejections.validationRejection("Restricted!"));
//#reject
}
@Test
public void testRedirect() {
//#redirect
final Route route = pathPrefix("foo", () ->
route(
pathSingleSlash(() -> complete("yes")),
pathEnd(() -> redirect(Uri.create("/foo/"), StatusCodes.PERMANENT_REDIRECT))
)
);
// tests:
testRoute(route).run(HttpRequest.GET("/foo/"))
.assertEntity("yes");
testRoute(route).run(HttpRequest.GET("/foo"))
.assertStatusCode(StatusCodes.PERMANENT_REDIRECT)
.assertEntity("The request, and all future requests should be repeated using <a href=\"/foo/\">this URI</a>.");
//#redirect
}
@Test
public void testFailWith() {
//#failWith
final Route route = path("foo", () ->
failWith(new RuntimeException("Oops."))
);
// tests:
testRoute(route).run(HttpRequest.GET("/foo"))
.assertStatusCode(StatusCodes.INTERNAL_SERVER_ERROR)
.assertEntity("There was an internal server error.");
//#failWith
}
}

View file

@ -1,58 +0,0 @@
/*
* Copyright (C) 2015-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import java.util.Arrays;
import java.util.regex.Pattern;
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.Route;
import akka.http.javadsl.server.RequestContext;
import akka.http.javadsl.testkit.JUnitRouteTest;
import java.util.function.Function;
import akka.http.javadsl.model.Uri;
import akka.http.javadsl.model.headers.Location;
public class SchemeDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testScheme() {
//#extractScheme
final Route route = extractScheme((scheme) ->
complete(String.format("The scheme is '%s'", scheme)));
testRoute(route).run(HttpRequest.GET("https://www.example.com/"))
.assertEntity("The scheme is 'https'");
//#extractScheme
}
@Test
public void testRedirection() {
//#scheme
final Route route = route(
scheme("http", ()->
extract((ctx) -> ctx.getRequest().getUri(), (uri)->
redirect(uri.scheme("https"), StatusCodes.MOVED_PERMANENTLY)
)
),
scheme("https", ()->
complete("Safe and secure!")
)
);
testRoute(route).run(HttpRequest.GET("http://www.example.com/hello"))
.assertStatusCode(StatusCodes.MOVED_PERMANENTLY)
.assertHeaderExists(Location.create("https://www.example.com/hello"))
;
testRoute(route).run(HttpRequest.GET("https://www.example.com/hello"))
.assertEntity("Safe and secure!");
//#scheme
}
}

View file

@ -1,364 +0,0 @@
/*
* Copyright (C) 2016-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.BasicHttpCredentials;
import akka.http.javadsl.model.headers.HttpChallenge;
import akka.http.javadsl.model.headers.HttpCredentials;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.japi.JavaPartialFunction;
import org.junit.Test;
import scala.PartialFunction;
import scala.util.Either;
import scala.util.Left;
import scala.util.Right;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.function.Function;
import java.util.Optional;
public class SecurityDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testAuthenticateBasic() {
//#authenticateBasic
final Function<Optional<ProvidedCredentials>, Optional<String>> myUserPassAuthenticator =
credentials ->
credentials.filter(c -> c.verify("p4ssw0rd")).map(ProvidedCredentials::identifier);
final Route route = path("secured", () ->
authenticateBasic("secure site", myUserPassAuthenticator, userName ->
complete("The user is '" + userName + "'")
)
).seal(system(), materializer());
// tests:
testRoute(route).run(HttpRequest.GET("/secured"))
.assertStatusCode(StatusCodes.UNAUTHORIZED)
.assertEntity("The resource requires authentication, which was not supplied with the request")
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
final HttpCredentials validCredentials =
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(validCredentials))
.assertEntity("The user is 'John'");
final HttpCredentials invalidCredentials =
BasicHttpCredentials.createBasicHttpCredentials("Peter", "pan");
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(invalidCredentials))
.assertStatusCode(StatusCodes.UNAUTHORIZED)
.assertEntity("The supplied authentication is invalid")
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
//#authenticateBasic
}
@Test
public void testAuthenticateBasicPF() {
//#authenticateBasicPF
final PartialFunction<Optional<ProvidedCredentials>, String> myUserPassAuthenticator =
new JavaPartialFunction<Optional<ProvidedCredentials>, String>() {
@Override
public String apply(Optional<ProvidedCredentials> opt, boolean isCheck) throws Exception {
if (opt.filter(c -> (c != null) && c.verify("p4ssw0rd")).isPresent()) {
if (isCheck) return null;
else return opt.get().identifier();
} else if (opt.filter(c -> (c != null) && c.verify("p4ssw0rd-special")).isPresent()) {
if (isCheck) return null;
else return opt.get().identifier() + "-admin";
} else {
throw noMatch();
}
}
};
final Route route = path("secured", () ->
authenticateBasicPF("secure site", myUserPassAuthenticator, userName ->
complete("The user is '" + userName + "'")
)
).seal(system(), materializer());
// tests:
testRoute(route).run(HttpRequest.GET("/secured"))
.assertStatusCode(StatusCodes.UNAUTHORIZED)
.assertEntity("The resource requires authentication, which was not supplied with the request")
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
final HttpCredentials validCredentials =
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(validCredentials))
.assertEntity("The user is 'John'");
final HttpCredentials validAdminCredentials =
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd-special");
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(validAdminCredentials))
.assertEntity("The user is 'John-admin'");
final HttpCredentials invalidCredentials =
BasicHttpCredentials.createBasicHttpCredentials("Peter", "pan");
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(invalidCredentials))
.assertStatusCode(StatusCodes.UNAUTHORIZED)
.assertEntity("The supplied authentication is invalid")
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
//#authenticateBasicPF
}
@Test
public void testAuthenticateBasicPFAsync() {
//#authenticateBasicPFAsync
class User {
private final String id;
public User(String id) {
this.id = id;
}
public String getId() {
return id;
}
}
final PartialFunction<Optional<ProvidedCredentials>, CompletionStage<User>> myUserPassAuthenticator =
new JavaPartialFunction<Optional<ProvidedCredentials>,CompletionStage<User>>() {
@Override
public CompletionStage<User> apply(Optional<ProvidedCredentials> opt, boolean isCheck) throws Exception {
if (opt.filter(c -> (c != null) && c.verify("p4ssw0rd")).isPresent()) {
if (isCheck) return CompletableFuture.completedFuture(null);
else return CompletableFuture.completedFuture(new User(opt.get().identifier()));
} else {
throw noMatch();
}
}
};
final Route route = path("secured", () ->
authenticateBasicPFAsync("secure site", myUserPassAuthenticator, user ->
complete("The user is '" + user.getId() + "'"))
).seal(system(), materializer());
// tests:
testRoute(route).run(HttpRequest.GET("/secured"))
.assertStatusCode(StatusCodes.UNAUTHORIZED)
.assertEntity("The resource requires authentication, which was not supplied with the request")
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
final HttpCredentials validCredentials =
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(validCredentials))
.assertEntity("The user is 'John'");
final HttpCredentials invalidCredentials =
BasicHttpCredentials.createBasicHttpCredentials("Peter", "pan");
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(invalidCredentials))
.assertStatusCode(StatusCodes.UNAUTHORIZED)
.assertEntity("The supplied authentication is invalid")
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
//#authenticateBasicPFAsync
}
@Test
public void testAuthenticateBasicAsync() {
//#authenticateBasicAsync
final Function<Optional<ProvidedCredentials>, CompletionStage<Optional<String>>> myUserPassAuthenticator = opt -> {
if (opt.filter(c -> (c != null) && c.verify("p4ssw0rd")).isPresent()) {
return CompletableFuture.completedFuture(Optional.of(opt.get().identifier()));
} else {
return CompletableFuture.completedFuture(Optional.empty());
}
};
final Route route = path("secured", () ->
authenticateBasicAsync("secure site", myUserPassAuthenticator, userName ->
complete("The user is '" + userName + "'")
)
).seal(system(), materializer());
// tests:
testRoute(route).run(HttpRequest.GET("/secured"))
.assertStatusCode(StatusCodes.UNAUTHORIZED)
.assertEntity("The resource requires authentication, which was not supplied with the request")
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
final HttpCredentials validCredentials =
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(validCredentials))
.assertEntity("The user is 'John'");
final HttpCredentials invalidCredentials =
BasicHttpCredentials.createBasicHttpCredentials("Peter", "pan");
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(invalidCredentials))
.assertStatusCode(StatusCodes.UNAUTHORIZED)
.assertEntity("The supplied authentication is invalid")
.assertHeaderExists("WWW-Authenticate", "Basic realm=\"secure site\"");
//#authenticateBasicAsync
}
@Test
public void testAuthenticateOrRejectWithChallenge() {
//#authenticateOrRejectWithChallenge
final HttpChallenge challenge = HttpChallenge.create("MyAuth", "MyRealm");
// your custom authentication logic:
final Function<HttpCredentials, Boolean> auth = credentials -> true;
final Function<Optional<HttpCredentials>, CompletionStage<Either<HttpChallenge, String>>> myUserPassAuthenticator =
opt -> {
if (opt.isPresent() && auth.apply(opt.get())) {
return CompletableFuture.completedFuture(Right.apply("some-user-name-from-creds"));
} else {
return CompletableFuture.completedFuture(Left.apply(challenge));
}
};
final Route route = path("secured", () ->
authenticateOrRejectWithChallenge(myUserPassAuthenticator, userName ->
complete("Authenticated!")
)
).seal(system(), materializer());
// tests:
testRoute(route).run(HttpRequest.GET("/secured"))
.assertStatusCode(StatusCodes.UNAUTHORIZED)
.assertEntity("The resource requires authentication, which was not supplied with the request")
.assertHeaderExists("WWW-Authenticate", "MyAuth realm=\"MyRealm\"");
final HttpCredentials validCredentials =
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
testRoute(route).run(HttpRequest.GET("/secured").addCredentials(validCredentials))
.assertStatusCode(StatusCodes.OK)
.assertEntity("Authenticated!");
//#authenticateOrRejectWithChallenge
}
@Test
public void testAuthorize() {
//#authorize
class User {
private final String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// authenticate the user:
final Function<Optional<ProvidedCredentials>, Optional<User>> myUserPassAuthenticator =
opt -> {
if (opt.isPresent()) {
return Optional.of(new User(opt.get().identifier()));
} else {
return Optional.empty();
}
};
// check if user is authorized to perform admin actions:
final Set<String> admins = new HashSet<>();
admins.add("Peter");
final Function<User, Boolean> hasAdminPermissions = user -> admins.contains(user.getName());
final Route route = authenticateBasic("secure site", myUserPassAuthenticator, user ->
path("peters-lair", () ->
authorize(() -> hasAdminPermissions.apply(user), () ->
complete("'" + user.getName() +"' visited Peter's lair")
)
)
).seal(system(), materializer());
// tests:
final HttpCredentials johnsCred =
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
testRoute(route).run(HttpRequest.GET("/peters-lair").addCredentials(johnsCred))
.assertStatusCode(StatusCodes.FORBIDDEN)
.assertEntity("The supplied authentication is not authorized to access this resource");
final HttpCredentials petersCred =
BasicHttpCredentials.createBasicHttpCredentials("Peter", "pan");
testRoute(route).run(HttpRequest.GET("/peters-lair").addCredentials(petersCred))
.assertEntity("'Peter' visited Peter's lair");
//#authorize
}
@Test
public void testAuthorizeAsync() {
//#authorizeAsync
class User {
private final String name;
public User(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// authenticate the user:
final Function<Optional<ProvidedCredentials>, Optional<User>> myUserPassAuthenticator =
opt -> {
if (opt.isPresent()) {
return Optional.of(new User(opt.get().identifier()));
} else {
return Optional.empty();
}
};
// check if user is authorized to perform admin actions,
// this could potentially be a long operation so it would return a Future
final Set<String> admins = new HashSet<>();
admins.add("Peter");
final Set<String> synchronizedAdmins = Collections.synchronizedSet(admins);
final Function<User, CompletionStage<Object>> hasAdminPermissions =
user -> CompletableFuture.completedFuture(synchronizedAdmins.contains(user.getName()));
final Route route = authenticateBasic("secure site", myUserPassAuthenticator, user ->
path("peters-lair", () ->
authorizeAsync(() -> hasAdminPermissions.apply(user), () ->
complete("'" + user.getName() +"' visited Peter's lair")
)
)
).seal(system(), materializer());
// tests:
final HttpCredentials johnsCred =
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
testRoute(route).run(HttpRequest.GET("/peters-lair").addCredentials(johnsCred))
.assertStatusCode(StatusCodes.FORBIDDEN)
.assertEntity("The supplied authentication is not authorized to access this resource");
final HttpCredentials petersCred =
BasicHttpCredentials.createBasicHttpCredentials("Peter", "pan");
testRoute(route).run(HttpRequest.GET("/peters-lair").addCredentials(petersCred))
.assertEntity("'Peter' visited Peter's lair");
//#authorizeAsync
}
@Test
public void testExtractCredentials() {
//#extractCredentials
final Route route = extractCredentials(optCreds -> {
if (optCreds.isPresent()) {
return complete("Credentials: " + optCreds.get());
} else {
return complete("No credentials");
}
});
// tests:
final HttpCredentials johnsCred =
BasicHttpCredentials.createBasicHttpCredentials("John", "p4ssw0rd");
testRoute(route).run(HttpRequest.GET("/").addCredentials(johnsCred))
.assertEntity("Credentials: Basic Sm9objpwNHNzdzByZA==");
testRoute(route).run(HttpRequest.GET("/"))
.assertEntity("No credentials");
//#extractCredentials
}
}

View file

@ -1,180 +0,0 @@
/*
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
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.HttpRequest;
import akka.http.javadsl.model.HttpResponse;
import akka.http.javadsl.model.StatusCode;
import akka.http.javadsl.model.StatusCodes;
import akka.http.javadsl.server.AllDirectives;
import akka.http.javadsl.server.Route;
import akka.http.scaladsl.TestUtils;
import akka.stream.ActorMaterializer;
import akka.stream.javadsl.Flow;
import akka.testkit.TestKit;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import org.junit.After;
import org.junit.Ignore;
import org.junit.Test;
import scala.Tuple2;
import scala.Tuple3;
import scala.concurrent.duration.Duration;
import scala.runtime.BoxedUnit;
import java.net.InetSocketAddress;
import java.util.Optional;
import java.util.concurrent.*;
public class TimeoutDirectivesExamplesTest extends AllDirectives {
//#testSetup
private final Config testConf = ConfigFactory.parseString("akka.loggers = [\"akka.testkit.TestEventListener\"]\n"
+ "akka.loglevel = ERROR\n"
+ "akka.stdout-loglevel = ERROR\n"
+ "windows-connection-abort-workaround-enabled = auto\n"
+ "akka.log-dead-letters = OFF\n"
+ "akka.http.server.request-timeout = 1000s");
// large timeout - 1000s (please note - setting to infinite will disable Timeout-Access header
// and withRequestTimeout will not work)
private final ActorSystem system = ActorSystem.create("TimeoutDirectivesExamplesTest", testConf);
private final ActorMaterializer materializer = ActorMaterializer.create(system);
private final Http http = Http.get(system);
private CompletionStage<Void> shutdown(CompletionStage<ServerBinding> binding) {
return binding.thenAccept(b -> {
System.out.println(String.format("Unbinding from %s", b.localAddress()));
final CompletionStage<BoxedUnit> unbound = b.unbind();
try {
unbound.toCompletableFuture().get(3, TimeUnit.SECONDS); // block...
} catch (TimeoutException | InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
}
});
}
private Optional<HttpResponse> runRoute(ActorSystem system, ActorMaterializer materializer, Route route, String routePath) {
final Tuple3<InetSocketAddress, String, Object> inetaddrHostAndPort = TestUtils.temporaryServerHostnameAndPort("127.0.0.1");
Tuple2<String, Integer> hostAndPort = new Tuple2<>(
inetaddrHostAndPort._2(),
(Integer) inetaddrHostAndPort._3()
);
final Flow<HttpRequest, HttpResponse, NotUsed> routeFlow = route.flow(system, materializer);
final CompletionStage<ServerBinding> binding = http.bindAndHandle(routeFlow, ConnectHttp.toHost(hostAndPort._1(), hostAndPort._2()), materializer);
final CompletionStage<HttpResponse> responseCompletionStage = http.singleRequest(HttpRequest.create("http://" + hostAndPort._1() + ":" + hostAndPort._2() + "/" + routePath), materializer);
CompletableFuture<HttpResponse> responseFuture = responseCompletionStage.toCompletableFuture();
Optional<HttpResponse> responseOptional;
try {
responseOptional = Optional.of(responseFuture.get(3, TimeUnit.SECONDS)); // patienceConfig
} catch (Exception e) {
responseOptional = Optional.empty();
}
shutdown(binding);
return responseOptional;
}
//#
@After
public void shutDown() {
TestKit.shutdownActorSystem(system, Duration.create(1, TimeUnit.SECONDS), false);
}
@Test
public void testRequestTimeoutIsConfigurable() {
//#withRequestTimeout-plain
final Duration timeout = Duration.create(1, TimeUnit.SECONDS);
CompletionStage<String> slowFuture = new CompletableFuture<>();
final Route route = path("timeout", () ->
withRequestTimeout(timeout, () -> {
return completeOKWithFutureString(slowFuture); // very slow
})
);
// test:
StatusCode statusCode = runRoute(system, materializer, route, "timeout").get().status();
assert (StatusCodes.SERVICE_UNAVAILABLE.equals(statusCode));
//#
}
@Test
public void testRequestWithoutTimeoutCancelsTimeout() {
//#withoutRequestTimeout-1
CompletionStage<String> slowFuture = new CompletableFuture<>();
final Route route = path("timeout", () ->
withoutRequestTimeout(() -> {
return completeOKWithFutureString(slowFuture); // very slow
})
);
// test:
Boolean receivedReply = runRoute(system, materializer, route, "timeout").isPresent();
assert (!receivedReply); // timed-out
//#
}
@Test
public void testRequestTimeoutAllowsCustomResponse() {
//#withRequestTimeout-with-handler
final Duration timeout = Duration.create(1, TimeUnit.MILLISECONDS);
CompletionStage<String> slowFuture = new CompletableFuture<>();
HttpResponse enhanceYourCalmResponse = HttpResponse.create()
.withStatus(StatusCodes.ENHANCE_YOUR_CALM)
.withEntity("Unable to serve response within time limit, please enhance your calm.");
final Route route = path("timeout", () ->
withRequestTimeout(timeout, (request) -> enhanceYourCalmResponse, () -> {
return completeOKWithFutureString(slowFuture); // very slow
})
);
// test:
StatusCode statusCode = runRoute(system, materializer, route, "timeout").get().status();
assert (StatusCodes.ENHANCE_YOUR_CALM.equals(statusCode));
//#
}
// make it compile only to avoid flaking in slow builds
@Ignore("Compile only test")
@Test
public void testRequestTimeoutCustomResponseCanBeAddedSeparately() {
//#withRequestTimeoutResponse
final Duration timeout = Duration.create(100, TimeUnit.MILLISECONDS);
CompletionStage<String> slowFuture = new CompletableFuture<>();
HttpResponse enhanceYourCalmResponse = HttpResponse.create()
.withStatus(StatusCodes.ENHANCE_YOUR_CALM)
.withEntity("Unable to serve response within time limit, please enhance your calm.");
final Route route = path("timeout", () ->
withRequestTimeout(timeout, () ->
// racy! for a very short timeout like 1.milli you can still get 503
withRequestTimeoutResponse((request) -> enhanceYourCalmResponse, () -> {
return completeOKWithFutureString(slowFuture); // very slow
}))
);
// test:
StatusCode statusCode = runRoute(system, materializer, route, "timeout").get().status();
assert (StatusCodes.ENHANCE_YOUR_CALM.equals(statusCode));
//#
}
}

View file

@ -1,121 +0,0 @@
/*
* Copyright (C) 2016-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.directives;
import akka.NotUsed;
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.StatusCodes;
import akka.http.javadsl.model.Uri;
import akka.http.javadsl.model.headers.SecWebSocketProtocol;
import akka.http.javadsl.model.ws.BinaryMessage;
import akka.http.javadsl.model.ws.Message;
import akka.http.javadsl.model.ws.TextMessage;
import akka.http.javadsl.server.Route;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.testkit.WSProbe;
import akka.stream.OverflowStrategy;
import akka.stream.javadsl.Flow;
import akka.stream.javadsl.Sink;
import akka.stream.javadsl.Source;
import akka.util.ByteString;
import org.junit.Test;
import scala.concurrent.duration.FiniteDuration;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.TimeUnit;
public class WebSocketDirectivesExamplesTest extends JUnitRouteTest {
@Test
public void testHandleWebSocketMessages() {
//#handleWebSocketMessages
final Flow<Message, Message, NotUsed> greeter = Flow.of(Message.class).mapConcat(msg -> {
if (msg instanceof TextMessage) {
final TextMessage tm = (TextMessage) msg;
final TextMessage ret = TextMessage.create(Source.single("Hello ").concat(tm.getStreamedText()).concat(Source.single("!")));
return Collections.singletonList(ret);
} else if (msg instanceof BinaryMessage) {
final BinaryMessage bm = (BinaryMessage) msg;
bm.getStreamedData().runWith(Sink.ignore(), materializer());
return Collections.emptyList();
} else {
throw new IllegalArgumentException("Unsupported message type!");
}
});
final Route websocketRoute = path("greeter", () ->
handleWebSocketMessages(greeter)
);
// create a testing probe representing the client-side
final WSProbe wsClient = WSProbe.create(system(), materializer());
// WS creates a WebSocket request for testing
testRoute(websocketRoute).run(WS(Uri.create("/greeter"), wsClient.flow(), materializer()))
.assertStatusCode(StatusCodes.SWITCHING_PROTOCOLS);
// manually run a WS conversation
wsClient.sendMessage("Peter");
wsClient.expectMessage("Hello Peter!");
wsClient.sendMessage(BinaryMessage.create(ByteString.fromString("abcdef")));
wsClient.expectNoMessage(FiniteDuration.create(100, TimeUnit.MILLISECONDS));
wsClient.sendMessage("John");
wsClient.expectMessage("Hello John!");
wsClient.sendCompletion();
wsClient.expectCompletion();
//#handleWebSocketMessages
}
@Test
public void testHandleWebSocketMessagesForProtocol() {
//#handleWebSocketMessagesForProtocol
final Flow<Message, Message, NotUsed> greeterService = Flow.of(Message.class).mapConcat(msg -> {
if (msg instanceof TextMessage) {
final TextMessage tm = (TextMessage) msg;
final TextMessage ret = TextMessage.create(Source.single("Hello ").concat(tm.getStreamedText()).concat(Source.single("!")));
return Collections.singletonList(ret);
} else if (msg instanceof BinaryMessage) {
final BinaryMessage bm = (BinaryMessage) msg;
bm.getStreamedData().runWith(Sink.ignore(), materializer());
return Collections.emptyList();
} else {
throw new IllegalArgumentException("Unsupported message type!");
}
});
final Flow<Message, Message, NotUsed> echoService = Flow.of(Message.class).buffer(1, OverflowStrategy.backpressure());
final Route websocketMultipleProtocolRoute = path("services", () ->
route(
handleWebSocketMessagesForProtocol(greeterService, "greeter"),
handleWebSocketMessagesForProtocol(echoService, "echo")
)
);
// create a testing probe representing the client-side
final WSProbe wsClient = WSProbe.create(system(), materializer());
// WS creates a WebSocket request for testing
testRoute(websocketMultipleProtocolRoute)
.run(WS(Uri.create("/services"), wsClient.flow(), materializer(), Arrays.asList("other", "echo")))
.assertHeaderExists(SecWebSocketProtocol.create("echo"));
wsClient.sendMessage("Peter");
wsClient.expectMessage("Peter");
wsClient.sendMessage(BinaryMessage.create(ByteString.fromString("abcdef")));
wsClient.expectMessage(ByteString.fromString("abcdef"));
wsClient.sendMessage("John");
wsClient.expectMessage("John");
wsClient.sendCompletion();
wsClient.expectCompletion();
//#handleWebSocketMessagesForProtocol
}
}

View file

@ -1,55 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.testkit;
//#simple-app
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.unmarshalling.StringUnmarshallers;
import akka.http.javadsl.server.examples.simple.SimpleServerApp;
import akka.stream.ActorMaterializer;
import java.io.IOException;
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

View file

@ -1,36 +0,0 @@
/*
* Copyright (C) 2009-2016 Lightbend Inc. <http://www.lightbend.com>
*/
package docs.http.javadsl.server.testkit;
//#simple-app-testing
import akka.http.javadsl.model.HttpRequest;
import akka.http.javadsl.model.StatusCodes;
import akka.http.javadsl.testkit.JUnitRouteTest;
import akka.http.javadsl.testkit.TestRoute;
import org.junit.Test;
public class TestkitExampleTest extends JUnitRouteTest {
TestRoute appRoute = testRoute(new MyAppService().createRoute());
@Test
public void testCalculatorAdd() {
// test happy path
appRoute.run(HttpRequest.GET("/calculator/add?x=4.2&y=2.3"))
.assertStatusCode(200)
.assertEntity("x + y = 6.5");
// test responses to potential errors
appRoute.run(HttpRequest.GET("/calculator/add?x=3.2"))
.assertStatusCode(StatusCodes.NOT_FOUND) // 404
.assertEntity("Request is missing required query parameter 'y'");
// test responses to potential errors
appRoute.run(HttpRequest.GET("/calculator/add?x=3.2&y=three"))
.assertStatusCode(StatusCodes.BAD_REQUEST)
.assertEntity("The query parameter 'y' was malformed:\n" +
"'three' is not a valid 64-bit floating point value");
}
}
//#simple-app-testing

View file

@ -1,137 +0,0 @@
.. _clientSideHTTPS-java:
Client-Side HTTPS Support
=========================
Akka HTTP supports TLS encryption on the client-side as well as on the :ref:`server-side <serverSideHTTPS-java>`.
.. warning:
Akka HTTP 1.0 does not completely validate certificates when using HTTPS. Please do not treat HTTPS connections
made with this version as secure. Requests are vulnerable to a Man-In-The-Middle attack via certificate substitution.
The central vehicle for configuring encryption is the ``HttpsConnectionContext``, which can be created using
the static method ``ConnectionContext.https`` which is defined like this:
.. includecode:: /../../akka-http-core/src/main/scala/akka/http/javadsl/ConnectionContext.scala
:include: https-context-creation
In addition to the ``outgoingConnection``, ``newHostConnectionPool`` and ``cachedHostConnectionPool`` methods the
`akka.http.javadsl.Http`_ extension also defines ``outgoingConnectionTls``, ``newHostConnectionPoolTls`` and
``cachedHostConnectionPoolTls``. These methods work identically to their counterparts without the ``-Tls`` suffix,
with the exception that all connections will always be encrypted.
The ``singleRequest`` and ``superPool`` methods determine the encryption state via the scheme of the incoming request,
i.e. requests to an "https" URI will be encrypted, while requests to an "http" URI won't.
The encryption configuration for all HTTPS connections, i.e. the ``HttpsContext`` is determined according to the
following logic:
1. If the optional ``httpsContext`` method parameter is defined it contains the configuration to be used (and thus
takes precedence over any potentially set default client-side ``HttpsContext``).
2. If the optional ``httpsContext`` method parameter is undefined (which is the default) the default client-side
``HttpsContext`` is used, which can be set via the ``setDefaultClientHttpsContext`` on the ``Http`` extension.
3. If no default client-side ``HttpsContext`` has been set via the ``setDefaultClientHttpsContext`` on the ``Http``
extension the default system configuration is used.
Usually the process is, if the default system TLS configuration is not good enough for your application's needs,
that you configure a custom ``HttpsContext`` instance and set it via ``Http.get(system).setDefaultClientHttpsContext``.
Afterwards you simply use ``outgoingConnectionTls``, ``newHostConnectionPoolTls``, ``cachedHostConnectionPoolTls``,
``superPool`` or ``singleRequest`` without a specific ``httpsContext`` argument, which causes encrypted connections
to rely on the configured default client-side ``HttpsContext``.
If no custom ``HttpsContext`` is defined the default context uses Java's default TLS settings. Customizing the
``HttpsContext`` can make the Https client less secure. Understand what you are doing!
SSL-Config
----------
Akka HTTP heavily relies on, and delegates most configuration of any SSL/TLS related options to
`Lightbend SSL-Config`_, which is a library specialized in providing an secure-by-default SSLContext
and related options.
Please refer to the `Lightbend SSL-Config`_ documentation for detailed documentation of all available settings.
SSL Config settings used by Akka HTTP (as well as Streaming TCP) are located under the `akka.ssl-config` namespace.
.. _Lightbend SSL-Config: http://typesafehub.github.io/ssl-config/
Detailed configuration and workarounds
--------------------------------------
Akka HTTP relies on `Typesafe SSL-Config`_ which is a library maintained by Lightbend that makes configuring
things related to SSL/TLS much simpler than using the raw SSL APIs provided by the JDK. Please refer to its
documentation to learn more about it.
All configuration options available to this library may be set under the ``akka.ssl-context`` configuration for Akka HTTP applications.
.. note::
When encountering problems connecting to HTTPS hosts we highly encourage to reading up on the excellent ssl-config
configuration. Especially the quick start sections about `adding certificates to the trust store`_ should prove
very useful, for example to easily trust a self-signed certificate that applications might use in development mode.
.. warning::
While it is possible to disable certain checks using the so called "loose" settings in SSL Config, we **strongly recommend**
to instead attempt to solve these issues by properly configuring TLSfor example by adding trusted keys to the keystore.
If however certain checks really need to be disabled because of misconfigured (or legacy) servers that your
application has to speak to, instead of disabling the checks globally (i.e. in ``application.conf``) we suggest
configuring the loose settings for *specific connections* that are known to need them disabled (and trusted for some other reason).
The pattern of doing so is documented in the folowing sub-sections.
.. _adding certificates to the trust store: http://typesafehub.github.io/ssl-config/WSQuickStart.html#connecting-to-a-remote-server-over-https
Hostname verification
^^^^^^^^^^^^^^^^^^^^^
Hostname verification proves that the Akka HTTP client is actually communicating with the server it intended to
communicate with. Without this check a man-in-the-middle attack is possible. In the attack scenario, an alternative
certificate would be presented which was issued for another host name. Checking the host name in the certificate
against the host name the connection was opened against is therefore vital.
The default ``HttpsContext`` enables hostname verification. Akka HTTP relies on the `Typesafe SSL-Config`_ library
to implement this and security options for SSL/TLS. Hostname verification is provided by the JDK
and used by Akka HTTP since Java 7, and on Java 6 the verification is implemented by ssl-config manually.
For further recommended reading we would like to highlight the `fixing hostname verification blog post`_ by blog post by Will Sargent.
.. _Typesafe SSL-Config: http://typesafehub.github.io/ssl-config
.. _fixing hostname verification blog post: https://tersesystems.com/2014/03/23/fixing-hostname-verification/
.. _akka.http.javadsl.Http: @github@/akka-http-core/src/main/scala/akka/http/javadsl/Http.scala
Server Name Indication (SNI)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
SNI is an TLS extension which aims to guard against man-in-the-middle attacks. It does so by having the client send the
name of the virtual domain it is expecting to talk to as part of the TLS handshake.
It is specified as part of `RFC 6066`_.
Disabling TLS security features, at your own risk
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. warning::
It is highly discouraged to disable any of the security features of TLS, however do acknowlage that workarounds may sometimes be needed.
Before disabling any of the features one should consider if they may be solvable *within* the TLS world,
for example by `trusting a certificate`_, or `configuring the trusted cipher suites`_.
There's also a very important section in the ssl-config docs titled `LooseSSL - Please read this before turning anything off!`_.
If disabling features is indeed desired, we recommend doing so for *specific connections*,
instead of globally configuring it via ``application.conf``.
The following shows an example of disabling SNI for a given connection:
.. includecode:: ../../code/docs/http/javadsl/HttpsExamplesDocTest.java
:include: disable-sni-connection
The ``badSslConfig`` is a copy of the default ``AkkaSSLConfig`` with with the slightly changed configuration to disable SNI.
This value can be cached and used for connections which should indeed not use this feature.
.. _RFC 6066: https://tools.ietf.org/html/rfc6066#page-6
.. _LooseSSL - Please read this before turning anything off!: http://typesafehub.github.io/ssl-config/LooseSSL.html#please-read-this-before-turning-anything-off
.. _trusting a certificate: http://typesafehub.github.io/ssl-config/WSQuickStart.html
.. _configuring the trusted cipher suites: http://typesafehub.github.io/ssl-config/CipherSuites.html

View file

@ -1,81 +0,0 @@
.. _connection-level-api-java:
Connection-Level Client-Side API
================================
The connection-level API is the lowest-level client-side API Akka HTTP provides. It gives you full control over when
HTTP connections are opened and closed and how requests are to be send across which connection. As such it offers the
highest flexibility at the cost of providing the least convenience.
Opening HTTP Connections
------------------------
With the connection-level API you open a new HTTP connection to a target endpoint by materializing a ``Flow``
returned by the ``Http.get(system).outgoingConnection(...)`` method. Here is an example:
.. includecode:: ../../code/docs/http/javadsl/HttpClientExampleDocTest.java#outgoing-connection-example
Apart from the host name and port the ``Http.get(system).outgoingConnection(...)`` method also allows you to specify socket options
and a number of configuration settings for the connection.
Note that no connection is attempted until the returned flow is actually materialized! If the flow is materialized
several times then several independent connections will be opened (one per materialization).
If the connection attempt fails, for whatever reason, the materialized flow will be immediately terminated with a
respective exception.
Request-Response Cycle
----------------------
Once the connection flow has been materialized it is ready to consume ``HttpRequest`` instances from the source it is
attached to. Each request is sent across the connection and incoming responses dispatched to the downstream pipeline.
Of course and as always, back-pressure is adequately maintained across all parts of the
connection. This means that, if the downstream pipeline consuming the HTTP responses is slow, the request source will
eventually be slowed down in sending requests.
Any errors occurring on the underlying connection are surfaced as exceptions terminating the response stream (and
canceling the request source).
Note that, if the source produces subsequent requests before the prior responses have arrived, these requests will be
pipelined__ across the connection, which is something that is not supported by all HTTP servers.
Also, if the server closes the connection before responses to all requests have been received this will result in the
response stream being terminated with a truncation error.
__ http://en.wikipedia.org/wiki/HTTP_pipelining
Closing Connections
-------------------
Akka HTTP actively closes an established connection upon reception of a response containing ``Connection: close`` header.
The connection can also be closed by the server.
An application can actively trigger the closing of the connection by completing the request stream. In this case the
underlying TCP connection will be closed when the last pending response has been received.
The connection will also be closed if the response entity is cancelled (e.g. by attaching it to ``Sink.cancelled()``)
or consumed only partially (e.g. by using ``take`` combinator). In order to prevent this behaviour the entity should be
explicitly drained by attaching it to ``Sink.ignore()``.
Timeouts
--------
Timeouts are configured in the same way for Scala and Akka. See :ref:`http-timeouts-java` .
.. _http-client-layer-java:
Stand-Alone HTTP Layer Usage
----------------------------
Due to its Reactive-Streams-based nature the Akka HTTP layer is fully detachable from the underlying TCP
interface. While in most applications this "feature" will not be crucial it can be useful in certain cases to be able
to "run" the HTTP layer (and, potentially, higher-layers) against data that do not come from the network but rather
some other source. Potential scenarios where this might be useful include tests, debugging or low-level event-sourcing
(e.g by replaying network traffic).
On the client-side the stand-alone HTTP layer forms a ``BidiFlow<HttpRequest, SslTlsOutbound, SslTlsInbound, HttpResponse, NotUsed>``,
that is a stage that "upgrades" a potentially encrypted raw connection to the HTTP level.
You create an instance of the layer by calling one of the two overloads of the ``Http.get(system).clientLayer`` method,
which also allows for varying degrees of configuration.

View file

@ -1,156 +0,0 @@
.. _host-level-api-java:
Host-Level Client-Side API
==========================
As opposed to the :ref:`connection-level-api` the host-level API relieves you from manually managing individual HTTP
connections. It autonomously manages a configurable pool of connections to *one particular target endpoint* (i.e.
host/port combination).
Requesting a Host Connection Pool
---------------------------------
The best way to get a hold of a connection pool to a given target endpoint is the ``Http.get(system).cachedHostConnectionPool(...)``
method, which returns a ``Flow`` that can be "baked" into an application-level stream setup. This flow is also called
a "pool client flow".
The connection pool underlying a pool client flow is cached. For every ``ActorSystem``, target endpoint and pool
configuration there will never be more than a single pool live at any time.
Also, the HTTP layer transparently manages idle shutdown and restarting of connection pools as configured.
The client flow instances therefore remain valid throughout the lifetime of the application, i.e. they can be
materialized as often as required and the time between individual materialization is of no importance.
When you request a pool client flow with ``Http.get(system).cachedHostConnectionPool(...)`` Akka HTTP will immediately start
the pool, even before the first client flow materialization. However, this running pool will not actually open the
first connection to the target endpoint until the first request has arrived.
Configuring a Host Connection Pool
----------------------------------
Apart from the connection-level config settings and socket options there are a number of settings that allow you to
influence the behavior of the connection pool logic itself.
Check out the ``akka.http.host-connection-pool`` section of the Akka HTTP :ref:`akka-http-configuration-java` for
more information about which settings are available and what they mean.
Note that, if you request pools with different configurations for the same target host you will get *independent* pools.
This means that, in total, your application might open more concurrent HTTP connections to the target endpoint than any
of the individual pool's ``max-connections`` settings allow!
There is one setting that likely deserves a bit deeper explanation: ``max-open-requests``.
This setting limits the maximum number of requests that can be in-flight at any time for a single connection pool.
If an application calls ``Http.get(system).cachedHostConnectionPool(...)`` 3 times (with the same endpoint and settings) it will get
back ``3`` different client flow instances for the same pool. If each of these client flows is then materialized ``4`` times
(concurrently) the application will have 12 concurrently running client flow materializations.
All of these share the resources of the single pool.
This means that, if the pool's ``pipelining-limit`` is left at ``1`` (effecitvely disabeling pipelining), no more than 12 requests can be open at any time.
With a ``pipelining-limit`` of ``8`` and 12 concurrent client flow materializations the theoretical open requests
maximum is ``96``.
The ``max-open-requests`` config setting allows for applying a hard limit which serves mainly as a protection against
erroneous connection pool use, e.g. because the application is materializing too many client flows that all compete for
the same pooled connections.
.. _using-a-host-connection-pool-java:
Using a Host Connection Pool
----------------------------
The "pool client flow" returned by ``Http.get(system).cachedHostConnectionPool(...)`` has the following type::
// TODO Tuple2 will be changed to be `akka.japi.Pair`
Flow[Tuple2[HttpRequest, T], Tuple2[Try[HttpResponse], T], HostConnectionPool]
This means it consumes tuples of type ``(HttpRequest, T)`` and produces tuples of type ``(Try[HttpResponse], T)``
which might appear more complicated than necessary on first sight.
The reason why the pool API includes objects of custom type ``T`` on both ends lies in the fact that the underlying
transport usually comprises more than a single connection and as such the pool client flow often generates responses in
an order that doesn't directly match the consumed requests.
We could have built the pool logic in a way that reorders responses according to their requests before dispatching them
to the application, but this would have meant that a single slow response could block the delivery of potentially many
responses that would otherwise be ready for consumption by the application.
In order to prevent unnecessary head-of-line blocking the pool client-flow is allowed to dispatch responses as soon as
they arrive, independently of the request order. Of course this means that there needs to be another way to associate a
response with its respective request. The way that this is done is by allowing the application to pass along a custom
"context" object with the request, which is then passed back to the application with the respective response.
This context object of type ``T`` is completely opaque to Akka HTTP, i.e. you can pick whatever works best for your
particular application scenario.
.. note::
A consequence of using a pool is that long-running requests block a connection while running and may starve other
requests. Make sure not to use a connection pool for long-running requests like long-polling GET requests.
Use the :ref:`connection-level-api-java` instead.
Connection Allocation Logic
---------------------------
This is how Akka HTTP allocates incoming requests to the available connection "slots":
1. If there is a connection alive and currently idle then schedule the request across this connection.
2. If no connection is idle and there is still an unconnected slot then establish a new connection.
3. If all connections are already established and "loaded" with other requests then pick the connection with the least
open requests (< the configured ``pipelining-limit``) that only has requests with idempotent methods scheduled to it,
if there is one.
4. Otherwise apply back-pressure to the request source, i.e. stop accepting new requests.
For more information about scheduling more than one request at a time across a single connection see
`this wikipedia entry on HTTP pipelining`__.
__ http://en.wikipedia.org/wiki/HTTP_pipelining
Retrying a Request
------------------
If the ``max-retries`` pool config setting is greater than zero the pool retries idempotent requests for which
a response could not be successfully retrieved. Idempotent requests are those whose HTTP method is defined to be
idempotent by the HTTP spec, which are all the ones currently modelled by Akka HTTP except for the ``POST``, ``PATCH``
and ``CONNECT`` methods.
When a response could not be received for a certain request there are essentially three possible error scenarios:
1. The request got lost on the way to the server.
2. The server experiences a problem while processing the request.
3. The response from the server got lost on the way back.
Since the host connector cannot know which one of these possible reasons caused the problem and therefore ``PATCH`` and
``POST`` requests could have already triggered a non-idempotent action on the server these requests cannot be retried.
In these cases, as well as when all retries have not yielded a proper response, the pool produces a failed ``Try``
(i.e. a ``scala.util.Failure``) together with the custom request context.
Pool Shutdown
-------------
Completing a pool client flow will simply detach the flow from the pool. The connection pool itself will continue to run
as it may be serving other client flows concurrently or in the future. Only after the configured ``idle-timeout`` for
the pool has expired will Akka HTTP automatically terminate the pool and free all its resources.
If a new client flow is requested with ``Http.get(system).cachedHostConnectionPool(...)`` or if an already existing client flow is
re-materialized the respective pool is automatically and transparently restarted.
In addition to the automatic shutdown via the configured idle timeouts it's also possible to trigger the immediate
shutdown of a specific pool by calling ``shutdown()`` on the :class:`HostConnectionPool` instance that the pool client
flow materializes into. This ``shutdown()`` call produces a ``CompletionStage<Done>`` which is fulfilled when the pool
termination has been completed.
It's also possible to trigger the immediate termination of *all* connection pools in the ``ActorSystem`` at the same
time by calling ``Http.get(system).shutdownAllConnectionPools()``. This call too produces a ``CompletionStage<Done>`` which is fulfilled when
all pools have terminated.
.. note::
When encoutering unexpected ``akka.stream.AbruptTerminationException`` exceptions during ``ActorSystem`` **shutdown**
please make sure that active connections are shut down before shutting down the entire system, this can be done by
calling the ``Http.get(system).shutdownAllConnectionPools()`` method, and only once its CompletionStage completes,
shutting down the actor system.
Example
-------
.. includecode:: ../../code/docs/http/javadsl/HttpClientExampleDocTest.java#host-level-example

View file

@ -1,30 +0,0 @@
.. _http-client-side-java:
Consuming HTTP-based Services (Client-Side)
===========================================
All client-side functionality of Akka HTTP, for consuming HTTP-based services offered by other endpoints, is currently
provided by the ``akka-http-core`` module.
Depending on your application's specific needs you can choose from three different API levels:
:ref:`connection-level-api-java`
for full-control over when HTTP connections are opened/closed and how requests are scheduled across them
:ref:`host-level-api-java`
for letting Akka HTTP manage a connection-pool to *one specific* host/port endpoint
:ref:`request-level-api-java`
for letting Akka HTTP perform all connection management
You can interact with different API levels at the same time and, independently of which API level you choose,
Akka HTTP will happily handle many thousand concurrent connections to a single or many different hosts.
.. toctree::
:maxdepth: 2
connection-level
host-level
request-level
client-https-support
websocket-support

View file

@ -1,73 +0,0 @@
.. _request-level-api-java:
Request-Level Client-Side API
=============================
The request-level API is the most convenient way of using Akka HTTP's client-side functionality. It internally builds upon the
:ref:`host-level-api-java` to provide you with a simple and easy-to-use way of retrieving HTTP responses from remote servers.
Depending on your preference you can pick the flow-based or the future-based variant.
.. note::
The request-level API is implemented on top of a connection pool that is shared inside the ActorSystem. A consequence of
using a pool is that long-running requests block a connection while running and starve other requests. Make sure not to use
the request-level API for long-running requests like long-polling GET requests. Use the :ref:`connection-level-api-java` instead.
Flow-Based Variant
------------------
The flow-based variant of the request-level client-side API is presented by the ``Http().superPool(...)`` method.
It creates a new "super connection pool flow", which routes incoming requests to a (cached) host connection pool
depending on their respective effective URIs.
The ``Flow`` returned by ``Http().superPool(...)`` is very similar to the one from the :ref:`host-level-api-java`, so the
:ref:`using-a-host-connection-pool-java` section also applies here.
However, there is one notable difference between a "host connection pool client flow" for the host-level API and a
"super-pool flow":
Since in the former case the flow has an implicit target host context the requests it takes don't need to have absolute
URIs or a valid ``Host`` header. The host connection pool will automatically add a ``Host`` header if required.
For a super-pool flow this is not the case. All requests to a super-pool must either have an absolute URI or a valid
``Host`` header, because otherwise it'd be impossible to find out which target endpoint to direct the request to.
Future-Based Variant
--------------------
Sometimes your HTTP client needs are very basic. You simply need the HTTP response for a certain request and don't
want to bother with setting up a full-blown streaming infrastructure.
For these cases Akka HTTP offers the ``Http().singleRequest(...)`` method, which simply turns an ``HttpRequest`` instance
into ``CompletionStage<HttpResponse>``. Internally the request is dispatched across the (cached) host connection pool for the
request's effective URI.
Just like in the case of the super-pool flow described above the request must have either an absolute URI or a valid
``Host`` header, otherwise the returned future will be completed with an error.
.. includecode:: ../../code/docs/http/javadsl/HttpClientExampleDocTest.java#single-request-example
Using the Future-Based API in Actors
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When using the ``CompletionStage`` based API from inside an ``Actor``, all the usual caveats apply to how one should deal
with the futures completion. For example you should not access the Actors state from within the CompletionStage's callbacks
(such as ``map``, ``onComplete``, ...) and instead you should use the ``pipe`` pattern to pipe the result back
to the Actor as a message:
.. includecode:: ../../code/docs/http/javadsl/HttpClientExampleDocTest.java#single-request-in-actor-example
.. warning::
Be sure to consume the response entities ``dataBytes:Source[ByteString,Unit]`` by for example connecting it
to a ``Sink`` (for example ``response.discardEntityBytes(Materializer)`` if you don't care about the
response entity), since otherwise Akka HTTP (and the underlying Streams infrastructure) will understand the
lack of entity consumption as a back-pressure signal and stop reading from the underlying TCP connection!
This is a feature of Akka HTTP that allows consuming entities (and pulling them through the network) in
a streaming fashion, and only *on demand* when the client is ready to consume the bytes -
it may be a bit surprising at first though.
There are tickets open about automatically dropping entities if not consumed (`#18716`_ and `#18540`_),
so these may be implemented in the near future.
.. _#18540: https://github.com/akka/akka/issues/18540
.. _#18716: https://github.com/akka/akka/issues/18716

View file

@ -1,128 +0,0 @@
.. _client-side-websocket-support-java:
Client-Side WebSocket Support
=============================
Client side WebSocket support is available through ``Http.singleWebSocketRequest`` ,
``Http.webSocketClientFlow`` and ``Http.webSocketClientLayer``.
A WebSocket consists of two streams of messages, incoming messages (a :class:`Sink`) and outgoing messages
(a :class:`Source`) where either may be signalled first; or even be the only direction in which messages flow
during the lifetime of the connection. Therefore a WebSocket connection is modelled as either something you connect a
``Flow<Message, Message, Mat>`` to or a ``Flow<Message, Message, Mat>`` that you connect a ``Source<Message, Mat>``
and a ``Sink<Message, Mat>`` to.
A WebSocket request starts with a regular HTTP request which contains an ``Upgrade`` header (and possibly
other regular HTTP request properties), so in addition to the flow of messages there also is an initial response
from the server, this is modelled with :class:`WebSocketUpgradeResponse`.
The methods of the WebSocket client API handle the upgrade to WebSocket on connection success and materializes
the connected WebSocket stream. If the connection fails, for example with a ``404 NotFound`` error, this regular
HTTP result can be found in ``WebSocketUpgradeResponse.response``
.. note::
Make sure to read and understand the section about :ref:`half-closed-client-websockets-java` as the behavior
when using WebSockets for one-way communication may not be what you would expect.
Message
-------
Messages sent and received over a WebSocket can be either :class:`TextMessage` s or :class:`BinaryMessage` s and each
of those can be either strict (all data in one chunk) or streamed. In typical applications messages will be strict as
WebSockets are usually deployed to communicate using small messages not stream data, the protocol does however
allow this (by not marking the first fragment as final, as described in `rfc 6455 section 5.2`__).
__ https://tools.ietf.org/html/rfc6455#section-5.2
The strict text is available from ``TextMessage.getStrictText`` and strict binary data from
``BinaryMessage.getStrictData``.
For streamed messages ``BinaryMessage.getStreamedData`` and ``TextMessage.getStreamedText`` is used to access the data.
In these cases the data is provided as a ``Source<ByteString, NotUsed>`` for binary and ``Source<String, NotUsed>``
for text messages.
singleWebSocketRequest
----------------------
``singleWebSocketRequest`` takes a :class:`WebSocketRequest` and a flow it will connect to the source and
sink of the WebSocket connection. It will trigger the request right away and returns a tuple containing a
``CompletionStage<WebSocketUpgradeResponse>`` and the materialized value from the flow passed to the method.
The future will succeed when the WebSocket connection has been established or the server returned a regular
HTTP response, or fail if the connection fails with an exception.
Simple example sending a message and printing any incoming message:
.. includecode:: ../../code/docs/http/javadsl/WebSocketClientExampleTest.java
:include: single-WebSocket-request
The websocket request may also include additional headers, like in this example, HTTP Basic Auth:
.. includecode:: ../../code/docs/http/javadsl/WebSocketClientExampleTest.java
:include: authorized-single-WebSocket-request
webSocketClientFlow
-------------------
``webSocketClientFlow`` takes a request, and returns a ``Flow<Message, Message, CompletionStage<WebSocketUpgradeResponse>>``.
The future that is materialized from the flow will succeed when the WebSocket connection has been established or
the server returned a regular HTTP response, or fail if the connection fails with an exception.
.. note::
The :class:`Flow` that is returned by this method can only be materialized once. For each request a new
flow must be acquired by calling the method again.
Simple example sending a message and printing any incoming message:
.. includecode:: ../../code/docs/http/javadsl/WebSocketClientExampleTest.java
:include: WebSocket-client-flow
webSocketClientLayer
--------------------
Just like the :ref:`http-client-layer-java` for regular HTTP requests, the WebSocket layer can be used fully detached from the
underlying TCP interface. The same scenarios as described for regular HTTP requests apply here.
The returned layer forms a ``BidiFlow<Message, SslTlsOutbound, SslTlsInbound, Message, CompletionStage<WebSocketUpgradeResponse>>``.
.. _half-closed-client-websockets-java:
Half-Closed WebSockets
----------------------
The Akka HTTP WebSocket API does not support half-closed connections which means that if the either stream completes the
entire connection is closed (after a "Closing Handshake" has been exchanged or a timeout of 3 seconds has passed).
This may lead to unexpected behavior, for example if we are trying to only consume messages coming from the server,
like this:
.. includecode:: ../../code/docs/http/javadsl/WebSocketClientExampleTest.java
:include: half-closed-WebSocket-closing
This will in fact quickly close the connection because of the ``Source.empty`` being completed immediately when the
stream is materialized. To solve this you can make sure to not complete the outgoing source by using for example
``Source.maybe`` like this:
.. includecode:: ../../code/docs/http/javadsl/WebSocketClientExampleTest.java
:include: half-closed-WebSocket-working
This will keep the outgoing source from completing, but without emitting any elements until the ``CompletableFuture`` is manually
completed which makes the ``Source`` complete and the connection to close.
The same problem holds true if emitting a finite number of elements, as soon as the last element is reached the ``Source``
will close and cause the connection to close. To avoid that you can concatenate ``Source.maybe`` to the finite stream:
.. includecode:: ../../code/docs/http/javadsl/WebSocketClientExampleTest.java
:include: half-closed-WebSocket-finite
Scenarios that exist with the two streams in a WebSocket and possible ways to deal with it:
=========================================== ================================================================================
Scenario Possible solution
=========================================== ================================================================================
Two-way communication ``Flow.fromSinkAndSource``, or ``Flow.map`` for a request-response protocol
Infinite incoming stream, no outgoing ``Flow.fromSinkAndSource(someSink, Source.maybe())``
Infinite outgoing stream, no incoming ``Flow.fromSinkAndSource(Sink.ignore(), yourSource)``
=========================================== ================================================================================

View file

@ -1,16 +0,0 @@
Encoding / Decoding
===================
The `HTTP spec`_ defines a ``Content-Encoding`` header, which signifies whether the entity body of an HTTP message is
"encoded" and, if so, by which algorithm. The only commonly used content encodings are compression algorithms.
Currently Akka HTTP supports the compression and decompression of HTTP requests and responses with the ``gzip`` or
``deflate`` encodings.
The core logic for this lives in the `akka.http.scaladsl.coding`_ package.
The support is not enabled automatically, but must be explicitly requested.
For enabling message encoding/decoding with :ref:`Routing DSL <http-high-level-server-side-api>` see the :ref:`CodingDirectives`.
.. _HTTP spec: http://tools.ietf.org/html/rfc7231#section-3.1.2.1
.. _akka.http.scaladsl.coding: @github@/akka-http/src/main/scala/akka/http/scaladsl/coding

View file

@ -1,21 +0,0 @@
.. _http-java-common:
Common Abstractions (Client- and Server-Side)
=============================================
HTTP and related specifications define a great number of concepts and functionality that is not specific to either
HTTP's client- or server-side since they are meaningful on both end of an HTTP connection.
The documentation for their counterparts in Akka HTTP lives in this section rather than in the ones for the
:ref:`Client-Side API <http-client-side>`, :ref:`http-low-level-server-side-api` or :ref:`http-high-level-server-side-api`,
which are specific to one side only.
.. toctree::
:maxdepth: 2
../http-model
marshalling
unmarshalling
de-coding
json-support
timeouts

View file

@ -1,31 +0,0 @@
.. _json-support-java:
Json Support
============
akka-http provides support to convert application-domain objects from and to JSON using jackson_ in an
extra artifact.
Integration with other JSON libraries may be supported by the community.
See `the list of current community extensions for Akka HTTP`_.
.. _`the list of current community extensions for Akka HTTP`: http://akka.io/community/#extensions-to-akka-http
.. _json-jackson-support-java:
Json Support via Jackson
------------------------
To make use of the support module, you need to add a dependency on `akka-http-jackson-experimental`.
Use ``akka.http.javadsl.marshallers.jackson.Jackson.unmarshaller(T.class)`` to create an ``Unmarshaller<HttpEntity,T>`` which expects the request
body (HttpEntity) to be of type ``application/json`` and converts it to ``T`` using Jackson.
See `this example`__ in the sources for an example.
Use ``akka.http.javadsl.marshallers.jackson.Jackson.marshaller(T.class)`` to create a ``Marshaller<T,RequestEntity>`` which can be used with
``RequestContext.complete`` or ``RouteDirectives.complete`` to convert a POJO to an HttpResponse.
.. _jackson: https://github.com/FasterXML/jackson
__ @github@/akka-http-tests/src/main/java/akka/http/javadsl/server/examples/petstore/PetStoreExample.java

View file

@ -1,166 +0,0 @@
.. _http-marshalling-java:
Marshalling
===========
TODO overhaul for Java
"Marshalling" is the process of converting a higher-level (object) structure into some kind of lower-level
representation, often a "wire format". Other popular names for it are "Serialization" or "Pickling".
In Akka HTTP "Marshalling" means the conversion of an object of type ``T`` into a lower-level target type,
e.g. a ``MessageEntity`` (which forms the "entity body" of an HTTP request or response) or a full ``HttpRequest`` or
``HttpResponse``.
Basic Design
------------
Marshalling of instances of type ``A`` into instances of type ``B`` is performed by a ``Marshaller[A, B]``.
Akka HTTP also predefines a number of helpful aliases for the types of marshallers that you'll likely work with most:
.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/marshalling/package.scala
:snippet: marshaller-aliases
Contrary to what you might initially expect ``Marshaller[A, B]`` is not a plain function ``A => B`` but rather
essentially a function ``A => Future[List[Marshalling[B]]]``.
Let's dissect this rather complicated looking signature piece by piece to understand what marshallers are designed this
way.
Given an instance of type ``A`` a ``Marshaller[A, B]`` produces:
1. A ``Future``: This is probably quite clear. Marshallers are not required to synchronously produce a result, so instead
they return a future, which allows for asynchronicity in the marshalling process.
2. of ``List``: Rather than only a single target representation for ``A`` marshallers can offer several ones. Which
one will be rendered onto the wire in the end is decided by content negotiation.
For example, the ``ToEntityMarshaller[OrderConfirmation]`` might offer a JSON as well as an XML representation.
The client can decide through the addition of an ``Accept`` request header which one is preferred. If the client doesn't
express a preference the first representation is picked.
3. of ``Marshalling[B]``: Rather than returning an instance of ``B`` directly marshallers first produce a
``Marshalling[B]``. This allows for querying the ``MediaType`` and potentially the ``HttpCharset`` that the marshaller
will produce before the actual marshalling is triggered. Apart from enabling content negotiation this design allows for
delaying the actual construction of the marshalling target instance to the very last moment when it is really needed.
This is how ``Marshalling`` is defined:
.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/marshalling/Marshaller.scala
:snippet: marshalling
Predefined Marshallers
----------------------
Akka HTTP already predefines a number of marshallers for the most common types.
Specifically these are:
- PredefinedToEntityMarshallers_
- ``Array[Byte]``
- ``ByteString``
- ``Array[Char]``
- ``String``
- ``akka.http.scaladsl.model.FormData``
- ``akka.http.scaladsl.model.MessageEntity``
- ``T <: akka.http.scaladsl.model.Multipart``
- PredefinedToResponseMarshallers_
- ``T``, if a ``ToEntityMarshaller[T]`` is available
- ``HttpResponse``
- ``StatusCode``
- ``(StatusCode, T)``, if a ``ToEntityMarshaller[T]`` is available
- ``(Int, T)``, if a ``ToEntityMarshaller[T]`` is available
- ``(StatusCode, immutable.Seq[HttpHeader], T)``, if a ``ToEntityMarshaller[T]`` is available
- ``(Int, immutable.Seq[HttpHeader], T)``, if a ``ToEntityMarshaller[T]`` is available
- PredefinedToRequestMarshallers_
- ``HttpRequest``
- ``Uri``
- ``(HttpMethod, Uri, T)``, if a ``ToEntityMarshaller[T]`` is available
- ``(HttpMethod, Uri, immutable.Seq[HttpHeader], T)``, if a ``ToEntityMarshaller[T]`` is available
- GenericMarshallers_
- ``Marshaller[Throwable, T]``
- ``Marshaller[Option[A], B]``, if a ``Marshaller[A, B]`` and an ``EmptyValue[B]`` is available
- ``Marshaller[Either[A1, A2], B]``, if a ``Marshaller[A1, B]`` and a ``Marshaller[A2, B]`` is available
- ``Marshaller[Future[A], B]``, if a ``Marshaller[A, B]`` is available
- ``Marshaller[Try[A], B]``, if a ``Marshaller[A, B]`` is available
.. _PredefinedToEntityMarshallers: @github@/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToEntityMarshallers.scala
.. _PredefinedToResponseMarshallers: @github@/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToResponseMarshallers.scala
.. _PredefinedToRequestMarshallers: @github@/akka-http/src/main/scala/akka/http/scaladsl/marshalling/PredefinedToRequestMarshallers.scala
.. _GenericMarshallers: @github@/akka-http/src/main/scala/akka/http/scaladsl/marshalling/GenericMarshallers.scala
Implicit Resolution
-------------------
The marshalling infrastructure of Akka HTTP relies on a type-class based approach, which means that ``Marshaller``
instances from a certain type ``A`` to a certain type ``B`` have to be available implicitly.
The implicits for most of the predefined marshallers in Akka HTTP are provided through the companion object of the
``Marshaller`` trait. This means that they are always available and never need to be explicitly imported.
Additionally, you can simply "override" them by bringing your own custom version into local scope.
Custom Marshallers
------------------
Akka HTTP gives you a few convenience tools for constructing marshallers for your own types.
Before you do that you need to think about what kind of marshaller you want to create.
If all your marshaller needs to produce is a ``MessageEntity`` then you should probably provide a
``ToEntityMarshaller[T]``. The advantage here is that it will work on both the client- as well as the server-side since
a ``ToResponseMarshaller[T]`` as well as a ``ToRequestMarshaller[T]`` can automatically be created if a
``ToEntityMarshaller[T]`` is available.
If, however, your marshaller also needs to set things like the response status code, the request method, the request URI
or any headers then a ``ToEntityMarshaller[T]`` won't work. You'll need to fall down to providing a
``ToResponseMarshaller[T]`` or a ``ToRequestMarshaller[T]`` directly.
For writing your own marshallers you won't have to "manually" implement the ``Marshaller`` trait directly.
Rather, it should be possible to use one of the convenience construction helpers defined on the ``Marshaller``
companion:
.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/marshalling/Marshaller.scala
:snippet: marshaller-creation
Deriving Marshallers
--------------------
Sometimes you can save yourself some work by reusing existing marshallers for your custom ones.
The idea is to "wrap" an existing marshaller with some logic to "re-target" it to your type.
In this regard wrapping a marshaller can mean one or both of the following two things:
- Transform the input before it reaches the wrapped marshaller
- Transform the output of the wrapped marshaller
For the latter (transforming the output) you can use ``baseMarshaller.map``, which works exactly as it does for functions.
For the former (transforming the input) you have four alternatives:
- ``baseMarshaller.compose``
- ``baseMarshaller.composeWithEC``
- ``baseMarshaller.wrap``
- ``baseMarshaller.wrapWithEC``
``compose`` works just like it does for functions.
``wrap`` is a compose that allows you to also change the ``ContentType`` that the marshaller marshals to.
The ``...WithEC`` variants allow you to receive an ``ExecutionContext`` internally if you need one, without having to
depend on one being available implicitly at the usage site.
Using Marshallers
-----------------
In many places throughput Akka HTTP marshallers are used implicitly, e.g. when you define how to :ref:`-complete-` a
request using the :ref:`Routing DSL <http-high-level-server-side-api>`.
However, you can also use the marshalling infrastructure directly if you wish, which can be useful for example in tests.
The best entry point for this is the ``akka.http.scaladsl.marshalling.Marshal`` object, which you can use like this:
.. TODO rewrite for Java
.. .. includecode2:: ../../code/docs/http/scaladsl/MarshalSpec.scala
:snippet: use marshal

View file

@ -1,78 +0,0 @@
.. _http-timeouts-java:
Akka HTTP Timeouts
==================
Akka HTTP comes with a variety of built-in timeout mechanisms to protect your servers from malicious attacks or
programming mistakes. Some of these are simply configuration options (which may be overriden in code) while others
are left to the streaming APIs and are easily implementable as patterns in user-code directly.
Common timeouts
---------------
.. _idle-timeouts-java:
Idle timeouts
^^^^^^^^^^^^^
The ``idle-timeout`` is a global setting which sets the maximum inactivity time of a given connection.
In other words, if a connection is open but no request/response is being written to it for over ``idle-timeout`` time,
the connection will be automatically closed.
The setting works the same way for all connections, be it server-side or client-side, and it's configurable
independently for each of those using the following keys::
akka.http.server.idle-timeout
akka.http.client.idle-timeout
akka.http.host-connection-pool.idle-timeout
akka.http.host-connection-pool.client.idle-timeout
.. note::
For the connection pooled client side the idle period is counted only when the pool has no pending requests waiting.
Server timeouts
---------------
.. _request-timeout-java:
Request timeout
^^^^^^^^^^^^^^^
Request timeouts are a mechanism that limits the maximum time it may take to produce an ``HttpResponse`` from a route.
If that deadline is not met the server will automatically inject a Service Unavailable HTTP response and close the connection
to prevent it from leaking and staying around indefinitely (for example if by programming error a Future would never complete,
never sending the real response otherwise).
The default ``HttpResponse`` that is written when a request timeout is exceeded looks like this:
.. includecode2:: /../../akka-http-core/src/main/scala/akka/http/impl/engine/server/HttpServerBluePrint.scala
:snippet: default-request-timeout-httpresponse
A default request timeout is applied globally to all routes and can be configured using the
``akka.http.server.request-timeout`` setting (which defaults to 20 seconds).
.. note::
Please note that if multiple requests (``R1,R2,R3,...``) were sent by a client (see "HTTP pipelining")
using the same connection and the ``n-th`` request triggers a request timeout the server will reply with an Http Response
and close the connection, leaving the ``(n+1)-th`` (and subsequent requests on the same connection) unhandled.
The request timeout can be configured at run-time for a given route using the any of the :ref:`TimeoutDirectives`.
Bind timeout
^^^^^^^^^^^^
The bind timeout is the time period within which the TCP binding process must be completed (using any of the ``Http().bind*`` methods).
It can be configured using the ``akka.http.server.bind-timeout`` setting.
Client timeouts
---------------
Connecting timeout
^^^^^^^^^^^^^^^^^^
The connecting timeout is the time period within which the TCP connecting process must be completed.
Tweaking it should rarely be required, but it allows erroring out the connection in case a connection
is unable to be established for a given amount of time.
it can be configured using the ``akka.http.client.connecting-timeout`` setting.

View file

@ -1,124 +0,0 @@
.. _http-unmarshalling-java:
Unmarshalling
=============
TODO overhaul for Java
"Unmarshalling" is the process of converting some kind of a lower-level representation, often a "wire format", into a
higher-level (object) structure. Other popular names for it are "Deserialization" or "Unpickling".
In Akka HTTP "Unmarshalling" means the conversion of a lower-level source object, e.g. a ``MessageEntity``
(which forms the "entity body" of an HTTP request or response) or a full ``HttpRequest`` or ``HttpResponse``,
into an instance of type ``T``.
Basic Design
------------
Unmarshalling of instances of type ``A`` into instances of type ``B`` is performed by an ``Unmarshaller[A, B]``.
Akka HTTP also predefines a number of helpful aliases for the types of unmarshallers that you'll likely work with most:
.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/package.scala
:snippet: unmarshaller-aliases
At its core an ``Unmarshaller[A, B]`` is very similar to a ``Function<A, CompletionStage<B>>`` and as such quite a bit simpler
than its :ref:`marshalling <http-marshalling-java>` counterpart. The process of unmarshalling does not have to support
content negotiation which saves two additional layers of indirection that are required on the marshalling side.
Predefined Unmarshallers
------------------------
Akka HTTP already predefines a number of marshallers for the most common types.
Specifically these are:
- PredefinedFromStringUnmarshallers_
- ``Byte``
- ``Short``
- ``Int``
- ``Long``
- ``Float``
- ``Double``
- ``Boolean``
- PredefinedFromEntityUnmarshallers_
- ``Array[Byte]``
- ``ByteString``
- ``Array[Char]``
- ``String``
- ``akka.http.scaladsl.model.FormData``
- GenericUnmarshallers_
- ``Unmarshaller[T, T]`` (identity unmarshaller)
- ``Unmarshaller[Option[A], B]``, if an ``Unmarshaller[A, B]`` is available
- ``Unmarshaller[A, Option[B]]``, if an ``Unmarshaller[A, B]`` is available
.. _PredefinedFromStringUnmarshallers: @github@/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromStringUnmarshallers.scala
.. _PredefinedFromEntityUnmarshallers: @github@/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/PredefinedFromEntityUnmarshallers.scala
.. _GenericUnmarshallers: @github@/akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/GenericUnmarshallers.scala
Implicit Resolution
-------------------
The unmarshalling infrastructure of Akka HTTP relies on a type-class based approach, which means that ``Unmarshaller``
instances from a certain type ``A`` to a certain type ``B`` have to be available implicitly.
The implicits for most of the predefined unmarshallers in Akka HTTP are provided through the companion object of the
``Unmarshaller`` trait. This means that they are always available and never need to be explicitly imported.
Additionally, you can simply "override" them by bringing your own custom version into local scope.
Custom Unmarshallers
--------------------
Akka HTTP gives you a few convenience tools for constructing unmarshallers for your own types.
Usually you won't have to "manually" implement the ``Unmarshaller`` trait directly.
Rather, it should be possible to use one of the convenience construction helpers defined on the ``Unmarshaller``
companion:
TODO rewrite sample for Java
..
.. includecode2:: /../../akka-http/src/main/scala/akka/http/scaladsl/unmarshalling/Unmarshaller.scala
:snippet: unmarshaller-creation
Deriving Unmarshallers
----------------------
Sometimes you can save yourself some work by reusing existing unmarshallers for your custom ones.
The idea is to "wrap" an existing unmarshaller with some logic to "re-target" it to your type.
Usually what you want to do is to transform the output of some existing unmarshaller and convert it to your type.
For this type of unmarshaller transformation Akka HTTP defines these methods:
- ``baseUnmarshaller.transform``
- ``baseUnmarshaller.map``
- ``baseUnmarshaller.mapWithInput``
- ``baseUnmarshaller.flatMap``
- ``baseUnmarshaller.flatMapWithInput``
- ``baseUnmarshaller.recover``
- ``baseUnmarshaller.withDefaultValue``
- ``baseUnmarshaller.mapWithCharset`` (only available for FromEntityUnmarshallers)
- ``baseUnmarshaller.forContentTypes`` (only available for FromEntityUnmarshallers)
The method signatures should make their semantics relatively clear.
Using Unmarshallers
-------------------
In many places throughput Akka HTTP unmarshallers are used implicitly, e.g. when you want to access the :ref:`-entity-`
of a request using the :ref:`Routing DSL <http-high-level-server-side-api>`.
However, you can also use the unmarshalling infrastructure directly if you wish, which can be useful for example in tests.
The best entry point for this is the ``akka.http.scaladsl.unmarshalling.Unmarshal`` object, which you can use like this:
.. TODO rewrite for java
.. .. includecode2:: ../../code/docs/http/scaladsl/UnmarshalSpec.scala
:snippet: use unmarshal

View file

@ -1,28 +0,0 @@
.. _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

View file

@ -1,286 +0,0 @@
.. _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/ModelDocTest.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/ModelDocTest.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/ModelDocTest.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 ``CompletionStage`` 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, the general method
``HttpEntity.getDataBytes()`` is provided which returns a ``Source<ByteString, ?>`` that 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!
Special processing for HEAD requests
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
`RFC 7230`_ defines very clear rules for the entity length of HTTP messages.
Especially this rule requires special treatment in Akka HTTP:
Any response to a HEAD request and any response with a 1xx
(Informational), 204 (No Content), or 304 (Not Modified) status
code is always terminated by the first empty line after the
header fields, regardless of the header fields present in the
message, and thus cannot contain a message body.
Responses to HEAD requests introduce the complexity that `Content-Length` or `Transfer-Encoding` headers
can be present but the entity is empty. This is modeled by allowing `HttpEntityDefault` and `HttpEntityChunked`
to be used for HEAD responses with an empty data stream.
Also, when a HEAD response has an `HttpEntityCloseDelimited` entity the Akka HTTP implementation will *not* close the
connection after the response has been sent. This allows the sending of HEAD responses without `Content-Length`
header across persistent HTTP connections.
.. _RFC 7230: http://tools.ietf.org/html/rfc7230#section-3.3.3
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/ModelDocTest.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.
.. note::
Various parsing and rendering settings are available to tweak in the configuration under ``akka.http.client[.parsing]``,
``akka.http.server[.parsing]`` and ``akka.http.host-connection-pool[.client.parsing]``, with defaults for all of these
being defined in the ``akka.http.parsing`` configuration section.
For example, if you want to change a parsing setting for all components, you can set the ``akka.http.parsing.illegal-header-warnings = off``
value. However this setting can be stil overriden by the more specific sections, like for example ``akka.http.server.parsing.illegal-header-warnings = on``.
In this case both ``client`` and ``host-connection-pool`` APIs will see the setting ``off``, however the server will see ``on``.
In the case of ``akka.http.host-connection-pool.client`` settings, they default to settings set in ``akka.http.client``,
and can override them if needed. This is useful, since both ``client`` and ``host-connection-pool`` APIs,
such as the Client API ``Http.get(sys).outgoingConnection`` or the Host Connection Pool APIs ``Http.get(sys).singleRequest``
or ``Http.get(sys).superPool``, usually need the same settings, however the ``server`` most likely has a very different set of settings.
The URI model
-------------
Akka HTTP offers its own specialised URI model class which is tuned for both performance and idiomatic usage within
other types of the HTTP model. For example, an HTTPRequest's target URI is parsed into this type, where all character
escaping and other URI specific semantics are applied.
Obtaining the Raw Request URI
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Sometimes it may be needed to obtain the "raw" value of an incoming URI, without applying any escaping or parsing to it.
While this use-case is rare, it comes up every once in a while. It is possible to obtain the "raw" request URI in Akka
HTTP Server side by turning on the ``akka.http.server.raw-request-uri-header`` flag.
When enabled, a ``Raw-Request-URI`` header will be added to each request. This header will hold the original raw request's
URI that was used. For an example check the reference configuration.

View file

@ -1,122 +0,0 @@
.. _implications-of-streaming-http-entities-java:
Implications of the streaming nature of Request/Response Entities
-----------------------------------------------------------------
Akka HTTP is streaming *all the way through*, which means that the back-pressure mechanisms enabled by Akka Streams
are exposed through all layersfrom the TCP layer, through the HTTP server, all the way up to the user-facing ``HttpRequest``
and ``HttpResponse`` and their ``HttpEntity`` APIs.
This has surprising implications if you are used to non-streaming / not-reactive HTTP clients.
Specifically it means that: "*lack of consumption of the HTTP Entity, is signaled as back-pressure to the other
side of the connection*". This is a feature, as it allows one only to consume the entity, and back-pressure servers/clients
from overwhelming our application, possibly causing un-necessary buffering of the entity in memory.
.. warning::
Consuming (or discarding) the Entity of a request is mandatory!
If *accidentally* left neither consumed or discarded Akka HTTP will
assume the incoming data should remain back-pressured, and will stall the incoming data via TCP back-pressure mechanisms.
A client should consume the Entity regardless of the status of the ``HttpResponse``.
Client-Side handling of streaming HTTP Entities
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Consuming the HTTP Response Entity (Client)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The most common use-case of course is consuming the response entity, which can be done via
running the underlying ``dataBytes`` Source. This is as simple as running the dataBytes source,
(or on the server-side using directives such as ``BasicDirectives.extractDataBytes``).
It is encouraged to use various streaming techniques to utilise the underlying infrastructure to its fullest,
for example by framing the incoming chunks, parsing them line-by-line and then connecting the flow into another
destination Sink, such as a File or other Akka Streams connector:
.. includecode:: ../code/docs/http/javadsl/HttpClientExampleDocTest.java#manual-entity-consume-example-1
however sometimes the need may arise to consume the entire entity as ``Strict`` entity (which means that it is
completely loaded into memory). Akka HTTP provides a special ``toStrict(timeout, materializer)`` method which can be used to
eagerly consume the entity and make it available in memory:
.. includecode:: ../code/docs/http/javadsl/HttpClientExampleDocTest.java#manual-entity-consume-example-2
Discarding the HTTP Response Entity (Client)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sometimes when calling HTTP services we do not care about their response payload (e.g. all we care about is the response code),
yet as explained above entity still has to be consumed in some way, otherwise we'll be exherting back-pressure on the
underlying TCP connection.
The ``discardEntityBytes`` convenience method serves the purpose of easily discarding the entity if it has no purpose for us.
It does so by piping the incoming bytes directly into an ``Sink.ignore``.
The two snippets below are equivalent, and work the same way on the server-side for incoming HTTP Requests:
.. includecode:: ../code/docs/http/javadsl/HttpClientExampleDocTest.java#manual-entity-discard-example-1
Or the equivalent low-level code achieving the same result:
.. includecode:: ../code/docs/http/javadsl/HttpClientExampleDocTest.java#manual-entity-discard-example-2
Server-Side handling of streaming HTTP Entities
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Similarily as with the Client-side, HTTP Entities are directly linked to Streams which are fed by the underlying
TCP connection. Thus, if request entities remain not consumed, the server will back-pressure the connection, expecting
that the user-code will eventually decide what to do with the incoming data.
Note that some directives force an implicit ``toStrict`` operation, such as ``entity(exampleUnmarshaller, example -> {})`` and similar ones.
Consuming the HTTP Request Entity (Server)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The simplest way of consuming the incoming request entity is to simply transform it into an actual domain object,
for example by using the :ref:`-entity-java-` directive:
.. includecode:: ../code/docs/http/javadsl/server/HttpServerExampleDocTest.java#consume-entity-directive
Of course you can access the raw dataBytes as well and run the underlying stream, for example piping it into an
FileIO Sink, that signals completion via a ``CompletionStage<IoResult>`` once all the data has been written into the file:
.. includecode:: ../code/docs/http/javadsl/server/HttpServerExampleDocTest.java#consume-raw-dataBytes
Discarding the HTTP Request Entity (Server)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sometimes, depending on some validation (e.g. checking if given user is allowed to perform uploads or not)
you may want to decide to discard the uploaded entity.
Please note that discarding means that the entire upload will proceed, even though you are not interested in the data
being streamed to the server - this may be useful if you are simply not interested in the given entity, however
you don't want to abort the entire connection (which we'll demonstrate as well), since there may be more requests
pending on the same connection still.
In order to discard the databytes explicitly you can invoke the ``discardEntityBytes`` bytes of the incoming ``HTTPRequest``:
.. includecode:: ../code/docs/http/javadsl/server/HttpServerExampleDocTest.java#discard-discardEntityBytes
A related concept is *cancelling* the incoming ``entity.getDataBytes()`` stream, which results in Akka HTTP
*abruptly closing the connection from the Client*. This may be useful when you detect that the given user should not be allowed to make any
uploads at all, and you want to drop the connection (instead of reading and ignoring the incoming data).
This can be done by attaching the incoming ``entity.getDataBytes()`` to a ``Sink.cancelled`` which will cancel
the entity stream, which in turn will cause the underlying connection to be shut-down by the server
effectively hard-aborting the incoming request:
.. includecode:: ../code/docs/http/javadsl/server/HttpServerExampleDocTest.java#discard-close-connections
Closing connections is also explained in depth in the :ref:`http-closing-connection-low-level-java` section of the docs.
Pending: Automatic discarding of not used entities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Under certain conditions it is possible to detect an entity is very unlikely to be used by the user for a given request,
and issue warnings or discard the entity automatically. This advanced feature has not been implemented yet, see the below
note and issues for further discussion and ideas.
.. note::
An advanced feature code named "auto draining" has been discussed and proposed for Akka HTTP, and we're hoping
to implement or help the community implement it.
You can read more about it in `issue #18716 <https://github.com/akka/akka/issues/18716>`_
as well as `issue #18540 <https://github.com/akka/akka/issues/18540>`_ ; as always, contributions are very welcome!

View file

@ -1,45 +1,5 @@
.. _http-java:
Akka HTTP Documentation (Java) moved!
=====================================
Akka HTTP
=========
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).
Includes a model of all things HTTP.
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_
.. toctree::
:maxdepth: 2
http-model
server-side/low-level-server-side-api
server-side/websocket-support
routing-dsl/index
client-side/index
common/index
implications-of-streaming-http-entity
configuration
server-side-https-support
../../scala/http/migration-guide-2.4.x-experimental
.. _jackson: https://github.com/FasterXML/jackson
Akka HTTP has been released as independent stable module (from Akka HTTP 3.x onwards).
The documentation is available under `doc.akka.io/akka-http/current/ <http://doc.akka.io/docs/akka-http/current/java.html>`_.

View file

@ -1,152 +0,0 @@
.. _Predefined Directives-java:
Predefined Directives (alphabetically)
======================================
================================================ ============================================================================
Directive Description
================================================ ============================================================================
:ref:`-authenticateBasic-java-` Wraps the inner route with Http Basic authentication support using a given ``Authenticator<T>``
:ref:`-authenticateBasicAsync-java-` Wraps the inner route with Http Basic authentication support using a given ``AsyncAuthenticator<T>``
:ref:`-authenticateBasicPF-java-` Wraps the inner route with Http Basic authentication support using a given ``AuthenticatorPF<T>``
:ref:`-authenticateBasicPFAsync-java-` Wraps the inner route with Http Basic authentication support using a given ``AsyncAuthenticatorPF<T>``
:ref:`-authenticateOAuth2-java-` Wraps the inner route with OAuth Bearer Token authentication support using a given ``AuthenticatorPF<T>``
:ref:`-authenticateOAuth2Async-java-` Wraps the inner route with OAuth Bearer Token authentication support using a given ``AsyncAuthenticator<T>``
:ref:`-authenticateOAuth2PF-java-` Wraps the inner route with OAuth Bearer Token authentication support using a given ``AuthenticatorPF<T>``
:ref:`-authenticateOAuth2PFAsync-java-` Wraps the inner route with OAuth Bearer Token authentication support using a given ``AsyncAuthenticatorPF<T>``
:ref:`-authenticateOrRejectWithChallenge-java-` Lifts an authenticator function into a directive
:ref:`-authorize-java-` Applies the given authorization check to the request
:ref:`-authorizeAsync-java-` Applies the given asynchronous authorization check to the request
:ref:`-cancelRejection-java-` Adds a ``TransformationRejection`` cancelling all rejections equal to the given one to the rejections potentially coming back from the inner route.
:ref:`-cancelRejections-java-` Adds a ``TransformationRejection`` cancelling all matching rejections to the rejections potentially coming back from the inner route
:ref:`-checkSameOrigin-java-` Checks that the request comes from the same origin
:ref:`-complete-java-` Completes the request using the given arguments
:ref:`-completeOrRecoverWith-java-` "Unwraps" a ``CompletionStage<T>`` and runs the inner route when the future has failed with the error as an extraction of type ``Throwable``
:ref:`-completeWith-java-` Uses the marshaller for a given type to extract a completion function
:ref:`-conditional-java-` Wraps its inner route with support for conditional requests as defined by http://tools.ietf.org/html/rfc7232
:ref:`-cookie-java-` Extracts the ``HttpCookie`` with the given name
:ref:`-decodeRequest-java-` Decompresses the request if it is ``gzip`` or ``deflate`` compressed
:ref:`-decodeRequestWith-java-` Decodes the incoming request using one of the given decoders
:ref:`-delete-java-` Rejects all non-DELETE requests
:ref:`-deleteCookie-java-` Adds a ``Set-Cookie`` response header expiring the given cookies
:ref:`-encodeResponse-java-` Encodes the response with the encoding that is requested by the client via the ``Accept-Encoding`` header (``NoCoding``, ``Gzip`` and ``Deflate``)
:ref:`-encodeResponseWith-java-` Encodes the response with the encoding that is requested by the client via the ``Accept-Encoding`` header (from a user-defined set)
:ref:`-entity-java-` Extracts the request entity unmarshalled to a given type
:ref:`-extract-java-` Extracts a single value using a ``RequestContext ⇒ T`` function
:ref:`-extractClientIP-java-` Extracts the client's IP from either the ``X-Forwarded-``, ``Remote-Address`` or ``X-Real-IP`` header
:ref:`-extractCredentials-java-` Extracts the potentially present ``HttpCredentials`` provided with the request's ``Authorization`` header
:ref:`-extractExecutionContext-java-` Extracts the ``ExecutionContext`` from the ``RequestContext``
:ref:`-extractMaterializer-java-` Extracts the ``Materializer`` from the ``RequestContext``
:ref:`-extractHost-java-` Extracts the hostname part of the Host request header value
:ref:`-extractLog-java-` Extracts the ``LoggingAdapter`` from the ``RequestContext``
:ref:`-extractMethod-java-` Extracts the request method
:ref:`-extractRequest-java-` Extracts the current ``HttpRequest`` instance
:ref:`-extractRequestContext-java-` Extracts the ``RequestContext`` itself
:ref:`-extractScheme-java-` Extracts the URI scheme from the request
:ref:`-extractSettings-java-` Extracts the ``RoutingSettings`` from the ``RequestContext``
:ref:`-extractUnmatchedPath-java-` Extracts the yet unmatched path from the ``RequestContext``
:ref:`-extractUri-java-` Extracts the complete request URI
:ref:`-failWith-java-` Bubbles the given error up the response chain where it is dealt with by the closest :ref:`-handleExceptions-java-` directive and its ``ExceptionHandler``
:ref:`-fileUpload-java-` Provides a stream of an uploaded file from a multipart request
:ref:`-formField-java-` Extracts an HTTP form field from the request
:ref:`-formFieldMap-java-` Extracts a number of HTTP form field from the request as a ``Map<String, String>``
:ref:`-formFieldMultiMap-java-` Extracts a number of HTTP form field from the request as a ``Map<String, List<String>``
:ref:`-formFieldList-java-` Extracts a number of HTTP form field from the request as a ``List<Pair<String, String>>``
:ref:`-get-java-` Rejects all non-GET requests
:ref:`-getFromBrowseableDirectories-java-` Serves the content of the given directories as a file-system browser, i.e. files are sent and directories served as browseable listings
:ref:`-getFromBrowseableDirectory-java-` Serves the content of the given directory as a file-system browser, i.e. files are sent and directories served as browseable listings
:ref:`-getFromDirectory-java-` Completes GET requests with the content of a file underneath a given file-system directory
:ref:`-getFromFile-java-` Completes GET requests with the content of a given file
:ref:`-getFromResource-java-` Completes GET requests with the content of a given class-path resource
:ref:`-getFromResourceDirectory-java-` Completes GET requests with the content of a file underneath a given "class-path resource directory"
:ref:`-handleExceptions-java-` Transforms exceptions thrown during evaluation of the inner route using the given ``ExceptionHandler``
:ref:`-handleRejections-java-` Transforms rejections produced by the inner route using the given ``RejectionHandler``
:ref:`-handleWebSocketMessages-java-` Handles websocket requests with the given handler and rejects other requests with an ``ExpectedWebSocketRequestRejection``
:ref:`-handleWebSocketMessagesForProtocol-java-` Handles websocket requests with the given handler if the subprotocol matches and rejects other requests with an ``ExpectedWebSocketRequestRejection`` or an ``UnsupportedWebSocketSubprotocolRejection``.
:ref:`-handleWith-java-` Completes the request using a given function
:ref:`-head-java-` Rejects all non-HEAD requests
:ref:`-headerValue-java-` Extracts an HTTP header value using a given ``HttpHeader ⇒ Option<T>`` function
:ref:`-headerValueByName-java-` Extracts the value of the first HTTP request header with a given name
:ref:`-headerValueByType-java-` Extracts the first HTTP request header of the given type
:ref:`-headerValuePF-java-` Extracts an HTTP header value using a given ``PartialFunction<HttpHeader, T>``
:ref:`-host-java-` Rejects all requests with a non-matching host name
:ref:`-listDirectoryContents-java-` Completes GET requests with a unified listing of the contents of all given file-system directories
:ref:`-logRequest-java-` Produces a log entry for every incoming request
:ref:`-logRequestResult-java-` Produces a log entry for every incoming request and ``RouteResult``
:ref:`-logResult-java-` Produces a log entry for every ``RouteResult``
:ref:`-mapInnerRoute-java-` Transforms its inner ``Route`` with a ``Route => Route`` function
:ref:`-mapRejections-java-` Transforms rejections from a previous route with an ``List<Rejection] ⇒ List<Rejection>`` function
:ref:`-mapRequest-java-` Transforms the request with an ``HttpRequest => HttpRequest`` function
:ref:`-mapRequestContext-java-` Transforms the ``RequestContext`` with a ``RequestContext => RequestContext`` function
:ref:`-mapResponse-java-` Transforms the response with an ``HttpResponse => HttpResponse`` function
:ref:`-mapResponseEntity-java-` Transforms the response entity with an ``ResponseEntity ⇒ ResponseEntity`` function
:ref:`-mapResponseHeaders-java-` Transforms the response headers with an ``List<HttpHeader] ⇒ List<HttpHeader>`` function
:ref:`-mapRouteResult-java-` Transforms the ``RouteResult`` with a ``RouteResult ⇒ RouteResult`` function
:ref:`-mapRouteResultFuture-java-` Transforms the ``RouteResult`` future with a ``CompletionStage<RouteResult] ⇒ CompletionStage<RouteResult>`` function
:ref:`-mapRouteResultPF-java-` Transforms the ``RouteResult`` with a ``PartialFunction<RouteResult, RouteResult>``
:ref:`-mapRouteResultWith-java-` Transforms the ``RouteResult`` with a ``RouteResult ⇒ CompletionStage<RouteResult>`` function
:ref:`-mapRouteResultWithPF-java-` Transforms the ``RouteResult`` with a ``PartialFunction<RouteResult, CompletionStage<RouteResult]>``
:ref:`-mapSettings-java-` Transforms the ``RoutingSettings`` with a ``RoutingSettings ⇒ RoutingSettings`` function
:ref:`-mapUnmatchedPath-java-` Transforms the ``unmatchedPath`` of the ``RequestContext`` using a ``Uri.Path ⇒ Uri.Path`` function
:ref:`-method-java-` Rejects all requests whose HTTP method does not match the given one
:ref:`-onComplete-java-` "Unwraps" a ``CompletionStage<T>`` and runs the inner route after future completion with the future's value as an extraction of type ``Try<T>``
:ref:`-onCompleteWithBreaker-java-` "Unwraps" a ``CompletionStage<T>`` inside a ``CircuitBreaker`` and runs the inner route after future completion with the future's value as an extraction of type ``Try<T>``
:ref:`-onSuccess-java-` "Unwraps" a ``CompletionStage<T>`` and runs the inner route after future completion with the future's value as an extraction of type ``T``
:ref:`-optionalCookie-java-` Extracts the ``HttpCookiePair`` with the given name as an ``Option<HttpCookiePair>``
:ref:`-optionalHeaderValue-java-` Extracts an optional HTTP header value using a given ``HttpHeader ⇒ Option<T>`` function
:ref:`-optionalHeaderValueByName-java-` Extracts the value of the first optional HTTP request header with a given name
:ref:`-optionalHeaderValueByType-java-` Extracts the first optional HTTP request header of the given type
:ref:`-optionalHeaderValuePF-java-` Extracts an optional HTTP header value using a given ``PartialFunction<HttpHeader, T>``
:ref:`-options-java-` Rejects all non-OPTIONS requests
:ref:`-overrideMethodWithParameter-java-` Changes the request method to the value of the specified query parameter
:ref:`-parameter-java-` Extracts a query parameter value from the request
:ref:`-parameterMap-java-` Extracts the request's query parameters as a ``Map<String, String>``
:ref:`-parameterMultiMap-java-` Extracts the request's query parameters as a ``Map<String, List<String>>``
:ref:`-parameterList-java-` Extracts the request's query parameters as a ``Seq<Pair<String, String>>``
:ref:`-pass-java-` Always simply passes the request on to its inner route, i.e. doesn't do anything, neither with the request nor the response
:ref:`-patch-java-` Rejects all non-PATCH requests
:ref:`-path-java-` Applies the given ``PathMatcher`` to the remaining unmatched path after consuming a leading slash
:ref:`-pathEnd-java-` Only passes on the request to its inner route if the request path has been matched completely
:ref:`-pathEndOrSingleSlash-java-` Only passes on the request to its inner route if the request path has been matched completely or only consists of exactly one remaining slash
:ref:`-pathPrefix-java-` Applies the given ``PathMatcher`` to a prefix of the remaining unmatched path after consuming a leading slash
:ref:`-pathPrefixTest-java-` Checks whether the unmatchedPath has a prefix matched by the given ``PathMatcher`` after implicitly consuming a leading slash
:ref:`-pathSingleSlash-java-` Only passes on the request to its inner route if the request path consists of exactly one remaining slash
:ref:`-pathSuffix-java-` Applies the given ``PathMatcher`` to a suffix of the remaining unmatched path (Caution: check java!)
:ref:`-pathSuffixTest-java-` Checks whether the unmatched path has a suffix matched by the given ``PathMatcher`` (Caution: check java!)
:ref:`-post-java-` Rejects all non-POST requests
:ref:`-provide-java-` Injects a given value into a directive
:ref:`-put-java-` Rejects all non-PUT requests
:ref:`-rawPathPrefix-java-` Applies the given matcher directly to a prefix of the unmatched path of the ``RequestContext``, without implicitly consuming a leading slash
:ref:`-rawPathPrefixTest-java-` Checks whether the unmatchedPath has a prefix matched by the given ``PathMatcher``
:ref:`-recoverRejections-java-` Transforms rejections from the inner route with an ``List<Rejection] ⇒ RouteResult`` function
:ref:`-recoverRejectionsWith-java-` Transforms rejections from the inner route with an ``List<Rejection] ⇒ CompletionStage<RouteResult>`` function
:ref:`-redirect-java-` Completes the request with redirection response of the given type to the given URI
:ref:`-redirectToNoTrailingSlashIfPresent-java-` If the request path ends with a slash, redirects to the same uri without trailing slash in the path
:ref:`-redirectToTrailingSlashIfMissing-java-` If the request path doesn't end with a slash, redirects to the same uri with trailing slash in the path
:ref:`-reject-java-` Rejects the request with the given rejections
:ref:`-rejectEmptyResponse-java-` Converts responses with an empty entity into (empty) rejections
:ref:`-requestEncodedWith-java-` Rejects the request with an ``UnsupportedRequestEncodingRejection`` if its encoding doesn't match the given one
:ref:`-requestEntityEmpty-java-` Rejects if the request entity is non-empty
:ref:`-requestEntityPresent-java-` Rejects with a ``RequestEntityExpectedRejection`` if the request entity is empty
:ref:`-respondWithDefaultHeader-java-` Adds a given response header if the response doesn't already contain a header with the same name
:ref:`-respondWithDefaultHeaders-java-` Adds the subset of the given headers to the response which doesn't already have a header with the respective name present in the response
:ref:`-respondWithHeader-java-` Unconditionally adds a given header to the outgoing response
:ref:`-respondWithHeaders-java-` Unconditionally adds the given headers to the outgoing response
:ref:`-responseEncodingAccepted-java-` Rejects the request with an ``UnacceptedResponseEncodingRejection`` if the given response encoding is not accepted by the client
:ref:`-scheme-java-` Rejects all requests whose URI scheme doesn't match the given one
:ref:`-selectPreferredLanguage-java-` Inspects the request's ``Accept-Language`` header and determines, which of a given set of language alternatives is preferred by the client
:ref:`-setCookie-java-` Adds a ``Set-Cookie`` response header with the given cookies
:ref:`-uploadedFile-java-` Streams one uploaded file from a multipart request to a file on disk
:ref:`-validate-java-` Checks a given condition before running its inner route
:ref:`-withoutRequestTimeout-java-` Disables :ref:`request timeouts <request-timeout-java>` for a given route.
:ref:`-withoutSizeLimit-java-` Skips request entity size check
:ref:`-withExecutionContext-java-` Runs its inner route with the given alternative ``ExecutionContext``
:ref:`-withMaterializer-java-` Runs its inner route with the given alternative ``Materializer``
:ref:`-withLog-java-` Runs its inner route with the given alternative ``LoggingAdapter``
:ref:`-withRangeSupport-java-` Adds ``Accept-Ranges: bytes`` to responses to GET requests, produces partial responses if the initial request contained a valid ``Range`` header
:ref:`-withRequestTimeout-java-` Configures the :ref:`request timeouts <request-timeout-java>` for a given route.
:ref:`-withRequestTimeoutResponse-java-` Prepares the ``HttpResponse`` that is emitted if a request timeout is triggered. ``RequestContext => RequestContext`` function
:ref:`-withSettings-java-` Runs its inner route with the given alternative ``RoutingSettings``
:ref:`-withSizeLimit-java-` Applies request entity size check
================================================ ============================================================================

View file

@ -1,20 +0,0 @@
.. _-cancelRejection-java-:
cancelRejection
===============
Description
-----------
Adds a ``TransformationRejection`` cancelling all rejections equal to the
given one to the rejections potentially coming back from the inner route.
Read :ref:`rejections-java` to learn more about rejections.
For more advanced handling of rejections refer to the :ref:`-handleRejections-java-` directive
which provides a nicer DSL for building rejection handlers.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#cancelRejection

View file

@ -1,22 +0,0 @@
.. _-cancelRejections-java-:
cancelRejections
================
Description
-----------
Adds a ``TransformationRejection`` cancelling all rejections created by the inner route for which
the condition argument function returns ``true``.
See also :ref:`-cancelRejection-java-`, for canceling a specific rejection.
Read :ref:`rejections-java` to learn more about rejections.
For more advanced handling of rejections refer to the :ref:`-handleRejections-java-` directive
which provides a nicer DSL for building rejection handlers.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#cancelRejections

View file

@ -1,17 +0,0 @@
.. _-extract-java-:
extract
=======
Description
-----------
The ``extract`` directive is used as a building block for :ref:`Custom Directives-java` to extract data from the
``RequestContext`` and provide it to the inner route.
See :ref:`ProvideDirectives-java` for an overview of similar directives.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#extract

View file

@ -1,19 +0,0 @@
.. _-extractActorSystem-java-:
extractActorSystem
==================
Description
-----------
Extracts the ``ActorSystem`` from the ``RequestContext``, which can be useful when the external API
in your route needs one.
.. warning::
This is only supported when the available Materializer is an ActorMaterializer.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#extractActorSystem

View file

@ -1,16 +0,0 @@
.. _-extractDataBytes-java-:
extractDataBytes
================
Description
-----------
Extracts the entities data bytes as ``Source[ByteString, Any]`` from the :class:`RequestContext`.
The directive returns a stream containing the request data bytes.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#extractDataBytes

View file

@ -1,18 +0,0 @@
.. _-extractExecutionContext-java-:
extractExecutionContext
=======================
Description
-----------
Extracts the ``ExecutionContext`` from the ``RequestContext``.
See :ref:`-withExecutionContext-java-` to see how to customise the execution context provided for an inner route.
See :ref:`-extract-java-` to learn more about how extractions work.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#extractExecutionContext

View file

@ -1,19 +0,0 @@
.. _-extractLog-java-:
extractLog
==========
Description
-----------
Extracts a :class:`LoggingAdapter` from the request context which can be used for logging inside the route.
The ``extractLog`` directive is used for providing logging to routes, such that they don't have to depend on
closing over a logger provided in the class body.
See :ref:`-extract-java-` and :ref:`ProvideDirectives-java` for an overview of similar directives.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#extractLog

View file

@ -1,17 +0,0 @@
.. _-extractMaterializer-java-:
extractMaterializer
===================
Description
-----------
Extracts the ``Materializer`` from the ``RequestContext``, which can be useful when you want to run an
Akka Stream directly in your route.
See also :ref:`-withMaterializer-java-` to see how to customise the used materializer for specific inner routes.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#extractMaterializer

View file

@ -1,17 +0,0 @@
.. _-extractRequest-java-:
extractRequest
==============
Description
-----------
Extracts the complete ``HttpRequest`` instance.
Use ``extractRequest`` to extract just the complete URI of the request. Usually there's little use of
extracting the complete request because extracting of most of the aspects of HttpRequests is handled by specialized
directives. See :ref:`Request Directives-java`.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#extractRequest

View file

@ -1,20 +0,0 @@
.. _-extractRequestContext-java-:
extractRequestContext
=====================
Description
-----------
Extracts the request's underlying :class:`RequestContext`.
This directive is used as a building block for most of the other directives,
which extract the context and by inspecting some of it's values can decide
what to do with the request - for example provide a value, or reject the request.
See also :ref:`-extractRequest-java-` if only interested in the :class:`HttpRequest` instance itself.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#extractRequestContext

View file

@ -1,17 +0,0 @@
.. _-extractRequestEntity-java-:
extractRequestEntity
====================
Description
-----------
Extracts the ``RequestEntity`` from the :class:`RequestContext`.
The directive returns a ``RequestEntity`` without unmarshalling the request. To extract domain entity,
:ref:`-entity-java-` should be used.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#extractRequestEntity

View file

@ -1,17 +0,0 @@
.. _-extractSettings-java-:
extractSettings
===============
Description
-----------
Extracts the :class:`RoutingSettings` from the :class:`RequestContext`.
By default the settings of the ``Http()`` extension running the route will be returned.
It is possible to override the settings for specific sub-routes by using the :ref:`-withSettings-java-` directive.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#extractRequestContext

View file

@ -1,23 +0,0 @@
.. _-extractStrictEntity-java-:
extractStrictEntity
===================
Description
-----------
Extracts the strict http entity as ``HttpEntity.Strict`` from the :class:`RequestContext`.
A timeout parameter is given and if the stream isn't completed after the timeout, the directive will be failed.
.. warning::
The directive will read the request entity into memory within the size limit(8M by default) and effectively disable streaming.
The size limit can be configured globally with ``akka.http.parsing.max-content-length`` or
overridden by wrapping with :ref:`-withSizeLimit-java-` or :ref:`-withoutSizeLimit-java-` directive.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#extractStrictEntity

View file

@ -1,19 +0,0 @@
.. _-extractUnmatchedPath-java-:
extractUnmatchedPath
====================
Description
-----------
Extracts the unmatched path from the request context.
The ``extractUnmatchedPath`` directive extracts the remaining path that was not yet matched by any of the :ref:`PathDirectives-java`
(or any custom ones that change the unmatched path field of the request context). You can use it for building directives
that handle complete suffixes of paths (like the ``getFromDirectory`` directives and similar ones).
Use ``mapUnmatchedPath`` to change the value of the unmatched path.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#extractUnmatchedPath

View file

@ -1,16 +0,0 @@
.. _-extractUri-java-:
extractUri
==========
Description
-----------
Access the full URI of the request.
Use :ref:`SchemeDirectives-java`, :ref:`HostDirectives-java`, :ref:`PathDirectives-java`, and :ref:`ParameterDirectives-java` for more
targeted access to parts of the URI.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#extractUri

View file

@ -1,133 +0,0 @@
.. _BasicDirectives-java:
BasicDirectives
===============
Basic directives are building blocks for building :ref:`Custom Directives`. As such they
usually aren't used in a route directly but rather in the definition of new directives.
.. _ProvideDirectives-java:
Providing Values to Inner Routes
--------------------------------
These directives provide values to the inner routes with extractions. They can be distinguished
on two axes: a) provide a constant value or extract a value from the ``RequestContext`` b) provide
a single value or a tuple of values.
* :ref:`-extract-java-`
* :ref:`-extractActorSystem-java-`
* :ref:`-extractDataBytes-java-`
* :ref:`-extractExecutionContext-java-`
* :ref:`-extractMaterializer-java-`
* :ref:`-extractStrictEntity-java-`
* :ref:`-extractLog-java-`
* :ref:`-extractRequest-java-`
* :ref:`-extractRequestContext-java-`
* :ref:`-extractRequestEntity-java-`
* :ref:`-extractSettings-java-`
* :ref:`-extractUnmatchedPath-java-`
* :ref:`-extractUri-java-`
* :ref:`-provide-java-`
.. _Request Transforming Directives-java:
Transforming the Request(Context)
---------------------------------
* :ref:`-mapRequest-java-`
* :ref:`-mapRequestContext-java-`
* :ref:`-mapSettings-java-`
* :ref:`-mapUnmatchedPath-java-`
* :ref:`-withExecutionContext-java-`
* :ref:`-withMaterializer-java-`
* :ref:`-withLog-java-`
* :ref:`-withSettings-java-`
* :ref:`-toStrictEntity-java-`
.. _Response Transforming Directives-java:
Transforming the Response
-------------------------
These directives allow to hook into the response path and transform the complete response or
the parts of a response or the list of rejections:
* :ref:`-mapResponse-java-`
* :ref:`-mapResponseEntity-java-`
* :ref:`-mapResponseHeaders-java-`
.. _Result Transformation Directives-java:
Transforming the RouteResult
----------------------------
These directives allow to transform the RouteResult of the inner route.
* :ref:`-cancelRejection-java-`
* :ref:`-cancelRejections-java-`
* :ref:`-mapRejections-java-`
* :ref:`-mapRouteResult-java-`
* :ref:`-mapRouteResultFuture-java-`
* :ref:`-mapRouteResultPF-java-`
* :ref:`-mapRouteResultWith-java-`
* :ref:`-mapRouteResultWithPF-java-`
* :ref:`-recoverRejections-java-`
* :ref:`-recoverRejectionsWith-java-`
Other
-----
* :ref:`-mapInnerRoute-java-`
* :ref:`-pass-java-`
Alphabetically
--------------
.. toctree::
:maxdepth: 1
cancelRejection
cancelRejections
extract
extractActorSystem
extractDataBytes
extractExecutionContext
extractMaterializer
extractStrictEntity
extractLog
extractRequest
extractRequestContext
extractRequestEntity
extractSettings
extractUnmatchedPath
extractUri
mapInnerRoute
mapRejections
mapRequest
mapRequestContext
mapResponse
mapResponseEntity
mapResponseHeaders
mapRouteResult
mapRouteResultFuture
mapRouteResultPF
mapRouteResultWith
mapRouteResultWithPF
mapSettings
mapUnmatchedPath
pass
provide
recoverRejections
recoverRejectionsWith
toStrictEntity
withExecutionContext
withMaterializer
withLog
withSettings

View file

@ -1,16 +0,0 @@
.. _-mapInnerRoute-java-:
mapInnerRoute
=============
Description
-----------
Changes the execution model of the inner route by wrapping it with arbitrary logic.
The ``mapInnerRoute`` directive is used as a building block for :ref:`Custom Directives-java` to replace the inner route
with any other route. Usually, the returned route wraps the original one with custom execution logic.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapInnerRoute

View file

@ -1,20 +0,0 @@
.. _-mapRejections-java-:
mapRejections
=============
Description
-----------
**Low level directive** unless you're sure you need to be working on this low-level you might instead
want to try the :ref:`-handleRejections-java-` directive which provides a nicer DSL for building rejection handlers.
The ``mapRejections`` directive is used as a building block for :ref:`Custom Directives-java` to transform a list
of rejections from the inner route to a new list of rejections.
See :ref:`Response Transforming Directives-java` for similar directives.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapRejections

View file

@ -1,20 +0,0 @@
.. _-mapRequest-java-:
mapRequest
==========
Description
-----------
Transforms the request before it is handled by the inner route.
The ``mapRequest`` directive is used as a building block for :ref:`Custom Directives-java` to transform a request before it
is handled by the inner route. Changing the ``request.uri`` parameter has no effect on path matching in the inner route
because the unmatched path is a separate field of the ``RequestContext`` value which is passed into routes. To change
the unmatched path or other fields of the ``RequestContext`` use the :ref:`-mapRequestContext-java-` directive.
See :ref:`Request Transforming Directives-java` for an overview of similar directives.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapRequest

View file

@ -1,19 +0,0 @@
.. _-mapRequestContext-java-:
mapRequestContext
=================
Description
-----------
Transforms the ``RequestContext`` before it is passed to the inner route.
The ``mapRequestContext`` directive is used as a building block for :ref:`Custom Directives-java` to transform
the request context before it is passed to the inner route. To change only the request value itself the
:ref:`-mapRequest-java-` directive can be used instead.
See :ref:`Request Transforming Directives-java` for an overview of similar directives.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapRequestContext

View file

@ -1,23 +0,0 @@
.. _-mapResponse-java-:
mapResponse
===========
Description
-----------
The ``mapResponse`` directive is used as a building block for :ref:`Custom Directives-java` to transform a response that
was generated by the inner route. This directive transforms complete responses.
See also :ref:`-mapResponseHeaders-java-` or :ref:`-mapResponseEntity-java-` for more specialized variants and
:ref:`Response Transforming Directives-java` for similar directives.
Example: Override status
------------------------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapResponse
Example: Default to empty JSON response on errors
-------------------------------------------------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapResponse-advanced

View file

@ -1,17 +0,0 @@
.. _-mapResponseEntity-java-:
mapResponseEntity
=================
Description
-----------
The ``mapResponseEntity`` directive is used as a building block for :ref:`Custom Directives-java` to transform a
response entity that was generated by the inner route.
See :ref:`Response Transforming Directives-java` for similar directives.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapResponseEntity

View file

@ -1,18 +0,0 @@
.. _-mapResponseHeaders-java-:
mapResponseHeaders
==================
Description
-----------
Changes the list of response headers that was generated by the inner route.
The ``mapResponseHeaders`` directive is used as a building block for :ref:`Custom Directives-java` to transform the list of
response headers that was generated by the inner route.
See :ref:`Response Transforming Directives-java` for similar directives.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapResponseHeaders

View file

@ -1,18 +0,0 @@
.. _-mapRouteResult-java-:
mapRouteResult
==============
Description
-----------
Changes the message the inner route sends to the responder.
The ``mapRouteResult`` directive is used as a building block for :ref:`Custom Directives-java` to transform the
:class:`RouteResult` coming back from the inner route.
See :ref:`Result Transformation Directives-java` for similar directives.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapRouteResult

View file

@ -1,21 +0,0 @@
.. _-mapRouteResultFuture-java-:
mapRouteResultFuture
====================
Description
-----------
Asynchronous version of :ref:`-mapRouteResult-java-`.
It's similar to :ref:`-mapRouteResultWith-java-`, however it's
``Function<CompletionStage<RouteResult>, CompletionStage<RouteResult>>``
instead of ``Function<RouteResult, CompletionStage<RouteResult>>`` which may be useful when
combining multiple transformations and / or wanting to ``recover`` from a failed route result.
See :ref:`Result Transformation Directives-java` for similar directives.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapRouteResultFuture

View file

@ -1,22 +0,0 @@
.. _-mapRouteResultPF-java-:
mapRouteResultPF
================
Description
-----------
*Partial Function* version of :ref:`-mapRouteResult-java-`.
Changes the message the inner route sends to the responder.
The ``mapRouteResult`` directive is used as a building block for :ref:`Custom Directives-java` to transform the
:class:`RouteResult` coming back from the inner route. It's similar to the :ref:`-mapRouteResult-java-` directive but allows to
specify a partial function that doesn't have to handle all potential ``RouteResult`` instances.
See :ref:`Result Transformation Directives-java` for similar directives.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapRouteResultPF

View file

@ -1,20 +0,0 @@
.. _-mapRouteResultWith-java-:
mapRouteResultWith
==================
Description
-----------
Changes the message the inner route sends to the responder.
The ``mapRouteResult`` directive is used as a building block for :ref:`Custom Directives-java` to transform the
:class:`RouteResult` coming back from the inner route. It's similar to the :ref:`-mapRouteResult-java-` directive but
returning a ``CompletionStage`` instead of a result immediately, which may be useful for longer running transformations.
See :ref:`Result Transformation Directives-java` for similar directives.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapRouteResultWith

View file

@ -1,21 +0,0 @@
.. _-mapRouteResultWithPF-java-:
mapRouteResultWithPF
====================
Description
-----------
Asynchronous variant of :ref:`-mapRouteResultPF-java-`.
Changes the message the inner route sends to the responder.
The ``mapRouteResult`` directive is used as a building block for :ref:`Custom Directives-java` to transform the
:class:`RouteResult` coming back from the inner route.
See :ref:`Result Transformation Directives-java` for similar directives.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapRouteResultWithPF

View file

@ -1,16 +0,0 @@
.. _-mapSettings-java-:
mapSettings
===========
Description
-----------
Transforms the ``RoutingSettings`` with a ``Function<RoutingSettings, RoutingSettings>``.
See also :ref:`-withSettings-java-` or :ref:`-extractSettings-java-`.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapSettings

View file

@ -1,18 +0,0 @@
.. _-mapUnmatchedPath-java-:
mapUnmatchedPath
================
Description
-----------
Transforms the unmatchedPath field of the request context for inner routes.
The ``mapUnmatchedPath`` directive is used as a building block for writing :ref:`Custom Directives-java`. You can use it
for implementing custom path matching directives.
Use ``extractUnmatchedPath`` for extracting the current value of the unmatched path.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#mapUnmatchedPath

View file

@ -1,15 +0,0 @@
.. _-pass-java-:
pass
====
Description
-----------
A directive that passes the request unchanged to its inner route.
It is usually used as a "neutral element" when combining directives generically.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#pass

View file

@ -1,17 +0,0 @@
.. _-provide-java-:
provide
=======
Description
-----------
Provides a constant value to the inner route.
The `provide` directive is used as a building block for :ref:`Custom Directives-java` to provide a single value to the
inner route.
See :ref:`ProvideDirectives-java` for an overview of similar directives.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#provide

View file

@ -1,21 +0,0 @@
.. _-recoverRejections-java-:
recoverRejections
=================
Description
-----------
**Low level directive** unless you're sure you need to be working on this low-level you might instead
want to try the :ref:`-handleRejections-java-` directive which provides a nicer DSL for building rejection handlers.
Transforms rejections from the inner route with a ``Function<Iterable<Rejection>, RouteResult>``.
A ``RouteResult`` is either a ``Complete`` containing the ``HttpResponse`` or a ``Rejected`` containing the
rejections.
.. note::
To learn more about how and why rejections work read the :ref:`rejections-java` section of the documentation.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#recoverRejections

View file

@ -1,24 +0,0 @@
.. _-recoverRejectionsWith-java-:
recoverRejectionsWith
=====================
Description
-----------
**Low level directive** unless you're sure you need to be working on this low-level you might instead
want to try the :ref:`-handleRejections-java-` directive which provides a nicer DSL for building rejection handlers.
Transforms rejections from the inner route with a ``Function<Iterable<Rejection>, CompletionStage<RouteResult>>``.
Asynchronous version of :ref:`-recoverRejections-java-`.
See :ref:`-recoverRejections-java-` (the synchronous equivalent of this directive) for a detailed description.
.. note::
To learn more about how and why rejections work read the :ref:`rejections-java` section of the documentation.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#recoverRejectionsWith

View file

@ -1,23 +0,0 @@
.. _-toStrictEntity-java-:
toStrictEntity
==============
Description
-----------
Transforms the request entity to strict entity before it is handled by the inner route.
A timeout parameter is given and if the stream isn't completed after the timeout, the directive will be failed.
.. warning::
The directive will read the request entity into memory within the size limit(8M by default) and effectively disable streaming.
The size limit can be configured globally with ``akka.http.parsing.max-content-length`` or
overridden by wrapping with :ref:`-withSizeLimit-java-` or :ref:`-withoutSizeLimit-java-` directive.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#toStrictEntity

View file

@ -1,18 +0,0 @@
.. _-withExecutionContext-java-:
withExecutionContext
====================
Description
-----------
Allows running an inner route using an alternative ``ExecutionContextExecutor`` in place of the default one.
The execution context can be extracted in an inner route using :ref:`-extractExecutionContext-java-` directly,
or used by directives which internally extract the materializer without sufracing this fact in the API.
Example
-------
.. includecode:: ../../../../code/docs/http/javadsl/server/directives/BasicDirectivesExamplesTest.java#withExecutionContext

Some files were not shown because too many files have changed in this diff Show more